diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ae81b2..7b1545b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 5.0.0 2023-12-22 + +* Align versioning scheme with that of other pg_query libraries + (which is to generally aim to match the libpg_query version) +* Upgrade to libpg_query 5.0.0 + - Updates to the Postgres 16 parser + - Multiple deparser improvements + + ## 0.8.2 2023-09-11 * Update bindgen to 0.66.1 to remove transitive dependency on atty and resolve build errors [#28](https://github.com/pganalyze/pg_query.rs/pull/28) diff --git a/Cargo.lock b/Cargo.lock index 8f2452d..fde4156 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,7 +390,7 @@ dependencies = [ [[package]] name = "pg_query" -version = "0.8.2" +version = "5.0.0" dependencies = [ "bindgen", "clippy", diff --git a/Cargo.toml b/Cargo.toml index 03ed4f5..c73e21a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pg_query" description = "PostgreSQL parser that uses the actual PostgreSQL server source to parse SQL queries and return the internal PostgreSQL parse tree." -version = "0.8.2" +version = "5.0.0" edition = "2021" documentation = "https://docs.rs/pg_query/" build = "build.rs" @@ -26,5 +26,5 @@ fs_extra = "1.2.0" [dev-dependencies] easy-parallel = "3.2.0" -pretty_assertions = "1.2.1" +pretty_assertions = "1.4.0" regex = "1.6.0" diff --git a/LICENSE b/LICENSE index e4e54ee..ba39f22 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,8 @@ MIT License Copyright (c) 2021 Paul Mason +Copyright (c) 2021-2023, Duboce Labs, Inc. (pganalyze) +All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +20,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index f02c1d8..f62a158 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,10 @@ pg_query.rs   [![Build Status]][actions] [![Latest Version]][crates.io] [![ This Rust library uses the actual PostgreSQL server source to parse SQL queries and return the internal PostgreSQL parse tree. -It also allows you to normalize queries (replacing constant values with ?) and parse these normalized queries into a parse tree again. +It also allows you to normalize queries (replacing constant values with $1, etc.) and parse these normalized queries into a parse tree again. When you build this library, it builds parts of the PostgreSQL server source (see [libpg_query](https://github.com/pganalyze/libpg_query)), and then statically links it into this library. -This is slightly crazy, but is the only reliable way of parsing all valid PostgreSQL queries. - You can find further examples and a longer rationale for the original Ruby implementation [here](https://pganalyze.com/blog/parse-postgresql-queries-in-ruby.html). The Rust version tries to have a very similar API. ## Getting started @@ -24,7 +22,7 @@ Add the following to your `Cargo.toml` ```toml [dependencies] -pg_query = "0.8" +pg_query = "5.0" ``` ## Examples @@ -67,4 +65,14 @@ assert_eq!(result.truncate(32).unwrap(), "INSERT INTO x (...) VALUES (...)"); Thanks to [Paul Mason](https://github.com/paupino) for his work on [pg_parse](https://github.com/paupino/pg_parse) that this crate is based on. -After version 0.6.0, Paul donated the pg_query crate to the pganalyze team. pg_parse is a lighter alternative that focuses on query parsing, while pg_query aims for feaure parity with the Ruby gem. +After version 0.6.0, Paul donated the pg_query crate to the pganalyze team. pg_parse is a lighter alternative that focuses on query parsing, while pg_query aims for feature parity with the Ruby gem. + +## License + +PostgreSQL server source code, used under the [PostgreSQL license](https://www.postgresql.org/about/licence/).
+Portions Copyright (c) 1996-2023, The PostgreSQL Global Development Group
+Portions Copyright (c) 1994, The Regents of the University of California + +All other parts are licensed under the MIT license, see LICENSE file for details.
+Copyright (c) 2021 Paul Mason +Copyright (c) 2021-2023, Duboce Labs, Inc. (pganalyze) diff --git a/libpg_query b/libpg_query index 9b21e32..2a00188 160000 --- a/libpg_query +++ b/libpg_query @@ -1 +1 @@ -Subproject commit 9b21e3295402a0d0ee9a50c468d426c2dbb73ee6 +Subproject commit 2a0018867c20011fc7166767083c05965241140b diff --git a/src/node_enum.rs b/src/node_enum.rs index 9d14880..ddcb71e 100644 --- a/src/node_enum.rs +++ b/src/node_enum.rs @@ -1119,6 +1119,20 @@ impl NodeEnum { NodeEnum::MergeWhenClause(n) => NodeRef::MergeWhenClause(n), NodeEnum::PublicationObjSpec(n) => NodeRef::PublicationObjSpec(n), NodeEnum::PublicationTable(n) => NodeRef::PublicationTable(n), + NodeEnum::JsonFormat(n) => NodeRef::JsonFormat(n), + NodeEnum::JsonReturning(n) => NodeRef::JsonReturning(n), + NodeEnum::JsonValueExpr(n) => NodeRef::JsonValueExpr(n), + NodeEnum::JsonConstructorExpr(n) => NodeRef::JsonConstructorExpr(n), + NodeEnum::JsonIsPredicate(n) => NodeRef::JsonIsPredicate(n), + NodeEnum::JsonOutput(n) => NodeRef::JsonOutput(n), + NodeEnum::JsonKeyValue(n) => NodeRef::JsonKeyValue(n), + NodeEnum::JsonObjectConstructor(n) => NodeRef::JsonObjectConstructor(n), + NodeEnum::JsonArrayConstructor(n) => NodeRef::JsonArrayConstructor(n), + NodeEnum::JsonArrayQueryConstructor(n) => NodeRef::JsonArrayQueryConstructor(n), + NodeEnum::JsonAggConstructor(n) => NodeRef::JsonAggConstructor(n), + NodeEnum::JsonObjectAgg(n) => NodeRef::JsonObjectAgg(n), + NodeEnum::JsonArrayAgg(n) => NodeRef::JsonArrayAgg(n), + NodeEnum::RtepermissionInfo(n) => NodeRef::RtepermissionInfo(n), } } @@ -1362,6 +1376,20 @@ impl NodeEnum { NodeEnum::MergeWhenClause(n) => NodeMut::MergeWhenClause(&mut **n as *mut _), NodeEnum::PublicationObjSpec(n) => NodeMut::PublicationObjSpec(&mut **n as *mut _), NodeEnum::PublicationTable(n) => NodeMut::PublicationTable(&mut **n as *mut _), + NodeEnum::JsonFormat(n) => NodeMut::JsonFormat(&mut *n as *mut _), + NodeEnum::JsonReturning(n) => NodeMut::JsonReturning(&mut *n as *mut _), + NodeEnum::JsonValueExpr(n) => NodeMut::JsonValueExpr(&mut **n as *mut _), + NodeEnum::JsonConstructorExpr(n) => NodeMut::JsonConstructorExpr(&mut **n as *mut _), + NodeEnum::JsonIsPredicate(n) => NodeMut::JsonIsPredicate(&mut **n as *mut _), + NodeEnum::JsonOutput(n) => NodeMut::JsonOutput(&mut *n as *mut _), + NodeEnum::JsonKeyValue(n) => NodeMut::JsonKeyValue(&mut **n as *mut _), + NodeEnum::JsonObjectConstructor(n) => NodeMut::JsonObjectConstructor(&mut *n as *mut _), + NodeEnum::JsonArrayConstructor(n) => NodeMut::JsonArrayConstructor(&mut *n as *mut _), + NodeEnum::JsonArrayQueryConstructor(n) => NodeMut::JsonArrayQueryConstructor(&mut **n as *mut _), + NodeEnum::JsonAggConstructor(n) => NodeMut::JsonAggConstructor(&mut **n as *mut _), + NodeEnum::JsonObjectAgg(n) => NodeMut::JsonObjectAgg(&mut **n as *mut _), + NodeEnum::JsonArrayAgg(n) => NodeMut::JsonArrayAgg(&mut **n as *mut _), + NodeEnum::RtepermissionInfo(n) => NodeMut::RtepermissionInfo(&mut *n as *mut _), } } } diff --git a/src/node_mut.rs b/src/node_mut.rs index bb6e3f0..8006bd5 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -240,6 +240,20 @@ pub enum NodeMut { MergeWhenClause(*mut protobuf::MergeWhenClause), PublicationObjSpec(*mut protobuf::PublicationObjSpec), PublicationTable(*mut protobuf::PublicationTable), + JsonFormat(*mut protobuf::JsonFormat), + JsonReturning(*mut protobuf::JsonReturning), + JsonValueExpr(*mut protobuf::JsonValueExpr), + JsonConstructorExpr(*mut protobuf::JsonConstructorExpr), + JsonIsPredicate(*mut protobuf::JsonIsPredicate), + JsonOutput(*mut protobuf::JsonOutput), + JsonKeyValue(*mut protobuf::JsonKeyValue), + JsonObjectConstructor(*mut protobuf::JsonObjectConstructor), + JsonArrayConstructor(*mut protobuf::JsonArrayConstructor), + JsonArrayQueryConstructor(*mut protobuf::JsonArrayQueryConstructor), + JsonAggConstructor(*mut protobuf::JsonAggConstructor), + JsonObjectAgg(*mut protobuf::JsonObjectAgg), + JsonArrayAgg(*mut protobuf::JsonArrayAgg), + RtepermissionInfo(*mut protobuf::RtePermissionInfo), } impl NodeMut { @@ -492,6 +506,20 @@ impl NodeMut { NodeMut::MergeWhenClause(n) => Ok(NodeEnum::MergeWhenClause(Box::new(n.as_ref().ok_or(err)?.clone()))), NodeMut::PublicationObjSpec(n) => Ok(NodeEnum::PublicationObjSpec(Box::new(n.as_ref().ok_or(err)?.clone()))), NodeMut::PublicationTable(n) => Ok(NodeEnum::PublicationTable(Box::new(n.as_ref().ok_or(err)?.clone()))), + NodeMut::JsonFormat(n) => Ok(NodeEnum::JsonFormat(n.as_ref().ok_or(err)?.clone())), + NodeMut::JsonReturning(n) => Ok(NodeEnum::JsonReturning(n.as_ref().ok_or(err)?.clone())), + NodeMut::JsonValueExpr(n) => Ok(NodeEnum::JsonValueExpr(Box::new(n.as_ref().ok_or(err)?.clone()))), + NodeMut::JsonConstructorExpr(n) => Ok(NodeEnum::JsonConstructorExpr(Box::new(n.as_ref().ok_or(err)?.clone()))), + NodeMut::JsonIsPredicate(n) => Ok(NodeEnum::JsonIsPredicate(Box::new(n.as_ref().ok_or(err)?.clone()))), + NodeMut::JsonOutput(n) => Ok(NodeEnum::JsonOutput(n.as_ref().ok_or(err)?.clone())), + NodeMut::JsonKeyValue(n) => Ok(NodeEnum::JsonKeyValue(Box::new(n.as_ref().ok_or(err)?.clone()))), + NodeMut::JsonObjectConstructor(n) => Ok(NodeEnum::JsonObjectConstructor(n.as_ref().ok_or(err)?.clone())), + NodeMut::JsonArrayConstructor(n) => Ok(NodeEnum::JsonArrayConstructor(n.as_ref().ok_or(err)?.clone())), + NodeMut::JsonArrayQueryConstructor(n) => Ok(NodeEnum::JsonArrayQueryConstructor(Box::new(n.as_ref().ok_or(err)?.clone()))), + NodeMut::JsonAggConstructor(n) => Ok(NodeEnum::JsonAggConstructor(Box::new(n.as_ref().ok_or(err)?.clone()))), + NodeMut::JsonObjectAgg(n) => Ok(NodeEnum::JsonObjectAgg(Box::new(n.as_ref().ok_or(err)?.clone()))), + NodeMut::JsonArrayAgg(n) => Ok(NodeEnum::JsonArrayAgg(Box::new(n.as_ref().ok_or(err)?.clone()))), + NodeMut::RtepermissionInfo(n) => Ok(NodeEnum::RtepermissionInfo(n.as_ref().ok_or(err)?.clone())), } } } diff --git a/src/node_ref.rs b/src/node_ref.rs index bfd423d..41a97d6 100644 --- a/src/node_ref.rs +++ b/src/node_ref.rs @@ -240,6 +240,20 @@ pub enum NodeRef<'a> { MergeWhenClause(&'a protobuf::MergeWhenClause), PublicationObjSpec(&'a protobuf::PublicationObjSpec), PublicationTable(&'a protobuf::PublicationTable), + JsonFormat(&'a protobuf::JsonFormat), + JsonReturning(&'a protobuf::JsonReturning), + JsonValueExpr(&'a protobuf::JsonValueExpr), + JsonConstructorExpr(&'a protobuf::JsonConstructorExpr), + JsonIsPredicate(&'a protobuf::JsonIsPredicate), + JsonOutput(&'a protobuf::JsonOutput), + JsonKeyValue(&'a protobuf::JsonKeyValue), + JsonObjectConstructor(&'a protobuf::JsonObjectConstructor), + JsonArrayConstructor(&'a protobuf::JsonArrayConstructor), + JsonArrayQueryConstructor(&'a protobuf::JsonArrayQueryConstructor), + JsonAggConstructor(&'a protobuf::JsonAggConstructor), + JsonObjectAgg(&'a protobuf::JsonObjectAgg), + JsonArrayAgg(&'a protobuf::JsonArrayAgg), + RtepermissionInfo(&'a protobuf::RtePermissionInfo), } impl<'a> NodeRef<'a> { @@ -491,6 +505,20 @@ impl<'a> NodeRef<'a> { NodeRef::MergeWhenClause(n) => NodeEnum::MergeWhenClause(Box::new((*n).clone())), NodeRef::PublicationObjSpec(n) => NodeEnum::PublicationObjSpec(Box::new((*n).clone())), NodeRef::PublicationTable(n) => NodeEnum::PublicationTable(Box::new((*n).clone())), + NodeRef::JsonFormat(n) => NodeEnum::JsonFormat((*n).clone()), + NodeRef::JsonReturning(n) => NodeEnum::JsonReturning((*n).clone()), + NodeRef::JsonValueExpr(n) => NodeEnum::JsonValueExpr(Box::new((*n).clone())), + NodeRef::JsonConstructorExpr(n) => NodeEnum::JsonConstructorExpr(Box::new((*n).clone())), + NodeRef::JsonIsPredicate(n) => NodeEnum::JsonIsPredicate(Box::new((*n).clone())), + NodeRef::JsonOutput(n) => NodeEnum::JsonOutput((*n).clone()), + NodeRef::JsonKeyValue(n) => NodeEnum::JsonKeyValue(Box::new((*n).clone())), + NodeRef::JsonObjectConstructor(n) => NodeEnum::JsonObjectConstructor((*n).clone()), + NodeRef::JsonArrayConstructor(n) => NodeEnum::JsonArrayConstructor((*n).clone()), + NodeRef::JsonArrayQueryConstructor(n) => NodeEnum::JsonArrayQueryConstructor(Box::new((*n).clone())), + NodeRef::JsonAggConstructor(n) => NodeEnum::JsonAggConstructor(Box::new((*n).clone())), + NodeRef::JsonObjectAgg(n) => NodeEnum::JsonObjectAgg(Box::new((*n).clone())), + NodeRef::JsonArrayAgg(n) => NodeEnum::JsonArrayAgg(Box::new((*n).clone())), + NodeRef::RtepermissionInfo(n) => NodeEnum::RtepermissionInfo((*n).clone()), } } } diff --git a/tests/data/plpgsql_query.json b/tests/data/plpgsql_query.json index f0f4c9f..cd9dcdd 100644 --- a/tests/data/plpgsql_query.json +++ b/tests/data/plpgsql_query.json @@ -10,6 +10,7 @@ "lineno": 5, "sqlstmt": { "PLpgSQL_expr": { + "parseMode": 0, "query": "SELECT details FROM t WHERE col = input" } }, @@ -31,6 +32,7 @@ "PLpgSQL_stmt_return": { "expr": { "PLpgSQL_expr": { + "parseMode": 2, "query": "result" } }, @@ -88,4 +90,4 @@ ] } } -] \ No newline at end of file +] diff --git a/tests/data/plpgsql_simple.json b/tests/data/plpgsql_simple.json index 6af8524..b70cc76 100644 --- a/tests/data/plpgsql_simple.json +++ b/tests/data/plpgsql_simple.json @@ -8,6 +8,7 @@ "PLpgSQL_stmt_if": { "cond": { "PLpgSQL_expr": { + "parseMode": 2, "query": "v_version IS NULL" } }, @@ -17,6 +18,7 @@ "PLpgSQL_stmt_return": { "expr": { "PLpgSQL_expr": { + "parseMode": 2, "query": "v_name" } }, @@ -30,6 +32,7 @@ "PLpgSQL_stmt_return": { "expr": { "PLpgSQL_expr": { + "parseMode": 2, "query": "v_name || '/' || v_version" } }, @@ -74,4 +77,4 @@ ] } } -] \ No newline at end of file +] diff --git a/tests/parse_plpgsql_tests.rs b/tests/parse_plpgsql_tests.rs index 8edafdd..208ffeb 100644 --- a/tests/parse_plpgsql_tests.rs +++ b/tests/parse_plpgsql_tests.rs @@ -21,7 +21,7 @@ fn it_can_parse_a_simple_function() { let result = result.unwrap(); let expected = include_str!("data/plpgsql_simple.json"); let actual = serde_json::to_string_pretty(&result).unwrap(); - assert_eq!(expected, &actual); + pretty_assertions::assert_eq!(expected.trim(), actual.trim()); } #[test] @@ -43,7 +43,7 @@ fn it_can_parse_a_query_function() { let result = result.unwrap(); let expected = include_str!("data/plpgsql_query.json"); let actual = serde_json::to_string_pretty(&result).unwrap(); - assert_eq!(expected, actual); + pretty_assertions::assert_eq!(expected.trim(), actual.trim()); } #[test] diff --git a/tests/parse_tests.rs b/tests/parse_tests.rs index 9444bb2..57e5284 100644 --- a/tests/parse_tests.rs +++ b/tests/parse_tests.rs @@ -383,6 +383,7 @@ fn it_parses_CREATE_TABLE() { is_not_null: false, is_from_type: false, storage: "", + storage_name: "", raw_default: None, cooked_default: None, identity: "", @@ -942,7 +943,7 @@ fn it_parses_REVOKE() { }, ], is_grant: false, - admin_opt: false, + opt: [], grantor: None, behavior: DropRestrict, }"#