pg_query 4.2.0 → 4.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f3ea528544d70b22864e56f006dffce6c849d51eedb1de5d8644eb9f4d1b0ad
4
- data.tar.gz: 7b2793ea012fcd4dd88a42703da627f6794c6f032ccd39fc9addb2f0bbc40208
3
+ metadata.gz: 32a51d0f608ebfe0e27991db81924289d86c7f0b9a90ab56a321b9ef5c21794e
4
+ data.tar.gz: 0d56fc7fd092c3406a40d102f420d637347d45f77203cc4de2f4613a111438dc
5
5
  SHA512:
6
- metadata.gz: 23d2fae9e57c9c4f8b35856b42748ba8ff061ba5aa92ff4c679ebf5bce562ad7ddafa452183104318f19f09fb089f31108f922c39b057bccbbf028e50412b2fa
7
- data.tar.gz: 3c64bf6d9eb662f348296ebc79ba8671a5be8e9053b873a4a7e0be8065229ab89ba0d28d5cea2ed3aea48a7dc2ee6053470adbe45b46bd93e5a2ca2ffb041128
6
+ metadata.gz: f1adefd8ebb6ab5595bf97f79ca027fb5e3c6cc351fbac310252a1a0f6d13c566c2a09b268ff5c173d0ab968ce7171f6b29dc2c071814b407883d77c315d50ae
7
+ data.tar.gz: 824faa4086e30b42199491049a16c4e7287a9c5b6146aba89ef353b77cfe6a3452ae4f2f09169b5851ff3b266df71bd02a948141f2e4bd05e02425e09db5b6f1
data/CHANGELOG.md CHANGED
@@ -4,17 +4,32 @@
4
4
 
5
5
  * ...
6
6
 
7
- ## 4.2.0 2022-02-08
7
+
8
+ ## 4.2.1 2023-05-19
9
+
10
+ * Parse: Fix `ALTER INDEX my_index_name` to return `tables=[]` ([#285](https://github.com/pganalyze/pg_query/pull/285))
11
+ * Parse: Detect tables in a SELECT INTO clause as DDL tables ([#281](https://github.com/pganalyze/pg_query/pull/281))
12
+ * Add support for Ruby 3.2 ([#283](https://github.com/pganalyze/pg_query/pull/283))
13
+ * Bump up `google-protobuf` dependency to `>= 3.22.3`
14
+ - 3.22.0 or newer is required for Ruby 3.2 support
15
+ * Update to libpg_query 15-4.2.1
16
+ - Deparser: Handle INTERVAL correctly when used in SET statements
17
+ - Deparser: Ensure index names are quoted as identifiers
18
+
19
+
20
+ ## 4.2.0 2023-02-08
8
21
 
9
22
  * Update to libpg_query 15-4.2.0
10
23
  - Update to PostgreSQL 15.1
11
24
 
12
- ## 2.2.1 2022-01-20
25
+
26
+ ## 2.2.1 2023-01-20
13
27
 
14
28
  * Detect tables used in the query of a PREPARE statement ([#273](https://github.com/pganalyze/pg_query/pull/273))
15
29
  * Expose recursive walk functionality via walk! ([#268](https://github.com/pganalyze/pg_query/pull/268))
16
30
  * Retain schema in name when parsing out functions ([#272](https://github.com/pganalyze/pg_query/pull/272))
17
31
 
32
+
18
33
  ## 2.2.0 2022-11-02
19
34
 
20
35
  * Update to libpg_query 13-2.2.0 ([#264](https://github.com/pganalyze/pg_query/pull/264))
data/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  This Ruby extension uses the actual PostgreSQL server source to parse SQL queries and return the internal PostgreSQL parsetree.
4
4
 
5
- In addition the extension allows you to normalize queries (replacing constant values with ?) and parse these normalized queries into a parsetree again.
5
+ In addition the extension allows you to normalize queries (replacing constant values with $n) and parse these normalized queries into a parsetree again.
6
6
 
7
7
  When you build this extension, it builds parts of the PostgreSQL server source (see [libpg_query](https://github.com/pganalyze/libpg_query)), and then statically links it into this extension.
8
8
 
9
- This is slightly crazy, but is the only reliable way of parsing all valid PostgreSQL queries.
9
+ This may seem like a lot of complexity, but is the only reliable way of parsing all valid PostgreSQL queries.
10
10
 
11
11
  You can find further examples and a longer rationale here: https://pganalyze.com/blog/parse-postgresql-queries-in-ruby.html
12
12
 
@@ -25,10 +25,10 @@ Due to compiling parts of PostgreSQL, installation might take a while on slower
25
25
  ```ruby
26
26
  PgQuery.parse("SELECT 1")
27
27
 
28
- => #<PgQuery::ParserResult:0x00007fb69a958820
28
+ => #<PgQuery::ParserResult:0x000000012000c438
29
29
  @query="SELECT 1",
30
30
  @tree=<PgQuery::ParseResult:
31
- version: 130002,
31
+ version: 150001,
32
32
  stmts: [
33
33
  <PgQuery::RawStmt:
34
34
  stmt: <PgQuery::Node:
@@ -41,10 +41,11 @@ PgQuery.parse("SELECT 1")
41
41
  indirection: [],
42
42
  val: <PgQuery::Node:
43
43
  a_const: <PgQuery::A_Const:
44
- val: <PgQuery::Node:
45
- integer: <PgQuery::Integer: ival: 1>
46
- >,
47
- location: 7
44
+ isnull: false,
45
+ location: 7,
46
+ ival: <PgQuery::Integer:
47
+ ival: 1
48
+ >
48
49
  >
49
50
  >,
50
51
  location: 7
@@ -53,6 +54,7 @@ PgQuery.parse("SELECT 1")
53
54
  ],
54
55
  from_clause: [],
55
56
  group_clause: [],
57
+ group_distinct: false,
56
58
  window_clause: [],
57
59
  values_lists: [],
58
60
  sort_clause: [],
@@ -67,7 +69,12 @@ PgQuery.parse("SELECT 1")
67
69
  >
68
70
  ]
69
71
  >,
70
- @warnings=[]>
72
+ @warnings=[],
73
+ @tables=nil,
74
+ @aliases=nil,
75
+ @cte_names=nil,
76
+ @functions=nil
77
+ >
71
78
  ```
72
79
 
73
80
  ### Modifying a parsed query and turning it into SQL again
@@ -92,20 +99,12 @@ parsed_query.deparse
92
99
  PgQuery.normalize("SELECT 1 FROM x WHERE y = 'foo'")
93
100
 
94
101
  => "SELECT $1 FROM x WHERE y = $2"
95
-
96
- # Parsing a normalized query (pre-Postgres 10 style)
97
- PgQuery.parse("SELECT ? FROM x WHERE y = ?")
98
-
99
- => #<PgQuery::ParserResult:0x00007fb69a97a5d8
100
- @query="SELECT ? FROM x WHERE y = ?",
101
- @tree=<PgQuery::ParseResult: ...>,
102
- @warnings=[]>
103
102
  ```
104
103
 
105
104
  ### Extracting tables from a query
106
105
 
107
106
  ```ruby
108
- PgQuery.parse("SELECT ? FROM x JOIN y USING (id) WHERE z = ?").tables
107
+ PgQuery.parse("SELECT $1 FROM x JOIN y USING (id) WHERE z = $2").tables
109
108
 
110
109
  => ["x", "y"]
111
110
  ```
@@ -113,7 +112,7 @@ PgQuery.parse("SELECT ? FROM x JOIN y USING (id) WHERE z = ?").tables
113
112
  ### Extracting columns from a query
114
113
 
115
114
  ```ruby
116
- PgQuery.parse("SELECT ? FROM x WHERE x.y = ? AND z = ?").filter_columns
115
+ PgQuery.parse("SELECT $1 FROM x WHERE x.y = $2 AND z = $3").filter_columns
117
116
 
118
117
  => [["x", "y"], [nil, "z"]]
119
118
  ```
@@ -130,7 +129,7 @@ PgQuery.parse("SELECT 2; --- comment").fingerprint
130
129
  => "50fde20626009aba"
131
130
 
132
131
  # Faster fingerprint method that is implemented inside the native C library
133
- PgQuery.fingerprint("SELECT ?")
132
+ PgQuery.fingerprint("SELECT $1")
134
133
 
135
134
  => "50fde20626009aba"
136
135
  ```
@@ -140,7 +139,7 @@ PgQuery.fingerprint("SELECT ?")
140
139
  ```ruby
141
140
  PgQuery.scan('SELECT 1 --comment')
142
141
 
143
- => [<PgQuery::ScanResult: version: 130002, tokens: [
142
+ => [<PgQuery::ScanResult: version: 150001, tokens: [
144
143
  <PgQuery::ScanToken: start: 0, end: 6, token: :SELECT, keyword_kind: :RESERVED_KEYWORD>,
145
144
  <PgQuery::ScanToken: start: 7, end: 8, token: :ICONST, keyword_kind: :NO_KEYWORD>,
146
145
  <PgQuery::ScanToken: start: 9, end: 18, token: :SQL_COMMENT, keyword_kind: :NO_KEYWORD>]>,
@@ -177,20 +176,15 @@ First, some of the tree nodes are frozen. You can replace them, but you cannot m
177
176
  Second, table rewriting is a bit more nuanced than this example. While this will rewrite the table names, it will
178
177
  not correctly handle all CTEs, or rewrite columns with explicit table names.
179
178
 
180
- ## Differences from Upstream PostgreSQL
181
-
182
- This gem is based on [libpg_query](https://github.com/pganalyze/libpg_query),
183
- which uses the latest stable PostgreSQL version, but with a patch applied
184
- to support parsing normalized queries containing `?` replacement characters.
185
-
186
179
  ## Supported Ruby Versions
187
180
 
188
181
  Currently tested and officially supported Ruby versions:
189
182
 
190
- * CRuby 2.5
191
183
  * CRuby 2.6
192
184
  * CRuby 2.7
193
185
  * CRuby 3.0
186
+ * CRuby 3.1
187
+ * CRuby 3.2
194
188
 
195
189
  Not supported:
196
190
 
@@ -214,7 +208,7 @@ Once that is done, follow the following steps:
214
208
 
215
209
  ## Resources
216
210
 
217
- See [libpg_query](https://github.com/pganalyze/libpg_query/blob/13-latest/README.md#resources) for pg_query in other languages, as well as products/tools built on pg_query.
211
+ See [libpg_query](https://github.com/pganalyze/libpg_query/blob/15-latest/README.md#resources) for pg_query in other languages, as well as products/tools built on pg_query.
218
212
 
219
213
  ## Original Author
220
214
 
@@ -228,8 +222,10 @@ See [libpg_query](https://github.com/pganalyze/libpg_query/blob/13-latest/README
228
222
 
229
223
  ## License
230
224
 
231
- Copyright (c) 2015, pganalyze Team <team@pganalyze.com><br>
232
- pg_query is licensed under the 3-clause BSD license, see LICENSE file for details.
225
+ PostgreSQL server source code, used under the [PostgreSQL license](https://www.postgresql.org/about/licence/).<br>
226
+ Portions Copyright (c) 1996-2023, The PostgreSQL Global Development Group<br>
227
+ Portions Copyright (c) 1994, The Regents of the University of California
233
228
 
234
- Query normalization code:<br>
235
- Copyright (c) 2008-2015, PostgreSQL Global Development Group
229
+ All other parts are licensed under the 3-clause BSD license, see LICENSE file for details.<br>
230
+ Copyright (c) 2015, Lukas Fittl <lukas@fittl.com><br>
231
+ Copyright (c) 2016-2023, Duboce Labs, Inc. (pganalyze) <team@pganalyze.com>
data/Rakefile CHANGED
@@ -5,8 +5,8 @@ require 'rspec/core/rake_task'
5
5
  require 'rubocop/rake_task'
6
6
  require 'open-uri'
7
7
 
8
- LIB_PG_QUERY_TAG = '15-4.2.0'.freeze
9
- LIB_PG_QUERY_SHA256SUM = '573b7b0d3b04702ec9e541e0a504632de17bd90b948c5658e72c1cf9dfc3a05d'.freeze
8
+ LIB_PG_QUERY_TAG = '15-4.2.1'.freeze
9
+ LIB_PG_QUERY_SHA256SUM = '5828124517d8fd3091336fad6897e16ab49ec0b0b188f5859b3b928fc91608c2'.freeze
10
10
 
11
11
  Rake::ExtensionTask.new 'pg_query' do |ext|
12
12
  ext.lib_dir = 'lib/pg_query'
@@ -11,7 +11,6 @@
11
11
  #include "common/keywords.h"
12
12
  #include "common/kwlookup.h"
13
13
  #include "lib/stringinfo.h"
14
- #include "limits.h"
15
14
  #include "nodes/nodes.h"
16
15
  #include "nodes/parsenodes.h"
17
16
  #include "nodes/pg_list.h"
@@ -32,6 +31,7 @@ typedef enum DeparseNodeContext {
32
31
  DEPARSE_NODE_CONTEXT_XMLNAMESPACES,
33
32
  DEPARSE_NODE_CONTEXT_CREATE_TYPE,
34
33
  DEPARSE_NODE_CONTEXT_ALTER_TYPE,
34
+ DEPARSE_NODE_CONTEXT_SET_STATEMENT,
35
35
  // Identifier vs constant context
36
36
  DEPARSE_NODE_CONTEXT_IDENTIFIER,
37
37
  DEPARSE_NODE_CONTEXT_CONSTANT
@@ -160,8 +160,9 @@ static void deparseRangeSubselect(StringInfo str, RangeSubselect *range_subselec
160
160
  static void deparseRangeFunction(StringInfo str, RangeFunction *range_func);
161
161
  static void deparseAArrayExpr(StringInfo str, A_ArrayExpr * array_expr);
162
162
  static void deparseRowExpr(StringInfo str, RowExpr *row_expr);
163
- static void deparseTypeCast(StringInfo str, TypeCast *type_cast);
163
+ static void deparseTypeCast(StringInfo str, TypeCast *type_cast, DeparseNodeContext context);
164
164
  static void deparseTypeName(StringInfo str, TypeName *type_name);
165
+ static void deparseIntervalTypmods(StringInfo str, TypeName *type_name);
165
166
  static void deparseNullTest(StringInfo str, NullTest *null_test);
166
167
  static void deparseCaseExpr(StringInfo str, CaseExpr *case_expr);
167
168
  static void deparseCaseWhen(StringInfo str, CaseWhen *case_when);
@@ -260,7 +261,7 @@ static void deparseExpr(StringInfo str, Node *node)
260
261
  deparseXmlExpr(str, castNode(XmlExpr, node));
261
262
  break;
262
263
  case T_TypeCast:
263
- deparseTypeCast(str, castNode(TypeCast, node));
264
+ deparseTypeCast(str, castNode(TypeCast, node), DEPARSE_NODE_CONTEXT_NONE);
264
265
  break;
265
266
  case T_A_Const:
266
267
  deparseAConst(str, castNode(A_Const, node));
@@ -341,7 +342,7 @@ static void deparseCExpr(StringInfo str, Node *node)
341
342
  deparseAConst(str, castNode(A_Const, node));
342
343
  break;
343
344
  case T_TypeCast:
344
- deparseTypeCast(str, castNode(TypeCast, node));
345
+ deparseTypeCast(str, castNode(TypeCast, node), DEPARSE_NODE_CONTEXT_NONE);
345
346
  break;
346
347
  case T_A_Expr:
347
348
  appendStringInfoChar(str, '(');
@@ -1334,6 +1335,10 @@ static void deparseVarList(StringInfo str, List *l)
1334
1335
  else
1335
1336
  Assert(false);
1336
1337
  }
1338
+ else if (IsA(lfirst(lc), TypeCast))
1339
+ {
1340
+ deparseTypeCast(str, castNode(TypeCast, lfirst(lc)), DEPARSE_NODE_CONTEXT_SET_STATEMENT);
1341
+ }
1337
1342
  else
1338
1343
  {
1339
1344
  Assert(false);
@@ -1779,7 +1784,7 @@ static void deparseFuncExprWindowless(StringInfo str, Node* node)
1779
1784
  deparseSQLValueFunction(str, castNode(SQLValueFunction, node));
1780
1785
  break;
1781
1786
  case T_TypeCast:
1782
- deparseTypeCast(str, castNode(TypeCast, node));
1787
+ deparseTypeCast(str, castNode(TypeCast, node), DEPARSE_NODE_CONTEXT_NONE);
1783
1788
  break;
1784
1789
  case T_CoalesceExpr:
1785
1790
  deparseCoalesceExpr(str, castNode(CoalesceExpr, node));
@@ -3534,7 +3539,7 @@ static void deparseRowExpr(StringInfo str, RowExpr *row_expr)
3534
3539
  appendStringInfoChar(str, ')');
3535
3540
  }
3536
3541
 
3537
- static void deparseTypeCast(StringInfo str, TypeCast *type_cast)
3542
+ static void deparseTypeCast(StringInfo str, TypeCast *type_cast, DeparseNodeContext context)
3538
3543
  {
3539
3544
  bool need_parens = false;
3540
3545
 
@@ -3582,6 +3587,13 @@ static void deparseTypeCast(StringInfo str, TypeCast *type_cast)
3582
3587
  return;
3583
3588
  }
3584
3589
  }
3590
+ else if (strcmp(typename, "interval") == 0 && context == DEPARSE_NODE_CONTEXT_SET_STATEMENT && IsA(&a_const->val, String))
3591
+ {
3592
+ appendStringInfoString(str, "interval ");
3593
+ deparseAConst(str, a_const);
3594
+ deparseIntervalTypmods(str, type_cast->typeName);
3595
+ return;
3596
+ }
3585
3597
  }
3586
3598
 
3587
3599
  // Ensure negative values have wrapping parentheses
@@ -3706,70 +3718,9 @@ static void deparseTypeName(StringInfo str, TypeName *type_name)
3706
3718
  }
3707
3719
  else if (strcmp(name, "interval") == 0 && list_length(type_name->typmods) >= 1)
3708
3720
  {
3709
- Assert(IsA(linitial(type_name->typmods), A_Const));
3710
- Assert(IsA(&castNode(A_Const, linitial(type_name->typmods))->val, Integer));
3711
-
3712
- int fields = intVal(&castNode(A_Const, linitial(type_name->typmods))->val);
3713
-
3714
3721
  appendStringInfoString(str, "interval");
3722
+ deparseIntervalTypmods(str, type_name);
3715
3723
 
3716
- // This logic is based on intervaltypmodout in timestamp.c
3717
- switch (fields)
3718
- {
3719
- case INTERVAL_MASK(YEAR):
3720
- appendStringInfoString(str, " year");
3721
- break;
3722
- case INTERVAL_MASK(MONTH):
3723
- appendStringInfoString(str, " month");
3724
- break;
3725
- case INTERVAL_MASK(DAY):
3726
- appendStringInfoString(str, " day");
3727
- break;
3728
- case INTERVAL_MASK(HOUR):
3729
- appendStringInfoString(str, " hour");
3730
- break;
3731
- case INTERVAL_MASK(MINUTE):
3732
- appendStringInfoString(str, " minute");
3733
- break;
3734
- case INTERVAL_MASK(SECOND):
3735
- appendStringInfoString(str, " second");
3736
- break;
3737
- case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
3738
- appendStringInfoString(str, " year to month");
3739
- break;
3740
- case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
3741
- appendStringInfoString(str, " day to hour");
3742
- break;
3743
- case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3744
- appendStringInfoString(str, " day to minute");
3745
- break;
3746
- case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3747
- appendStringInfoString(str, " day to second");
3748
- break;
3749
- case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3750
- appendStringInfoString(str, " hour to minute");
3751
- break;
3752
- case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3753
- appendStringInfoString(str, " hour to second");
3754
- break;
3755
- case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3756
- appendStringInfoString(str, " minute to second");
3757
- break;
3758
- case INTERVAL_FULL_RANGE:
3759
- // Nothing
3760
- break;
3761
- default:
3762
- Assert(false);
3763
- break;
3764
- }
3765
-
3766
- if (list_length(type_name->typmods) == 2)
3767
- {
3768
- int precision = intVal(&castNode(A_Const, lsecond(type_name->typmods))->val);
3769
- if (precision != INTERVAL_FULL_PRECISION)
3770
- appendStringInfo(str, "(%d)", precision);
3771
- }
3772
-
3773
3724
  skip_typmods = true;
3774
3725
  }
3775
3726
  else
@@ -3815,6 +3766,79 @@ static void deparseTypeName(StringInfo str, TypeName *type_name)
3815
3766
  appendStringInfoString(str, "%type");
3816
3767
  }
3817
3768
 
3769
+ // Handle typemods for Interval types separately
3770
+ // so that they can be applied appropriately for different contexts.
3771
+ // For example, when using `SET` a query like `INTERVAL 'x' hour TO minute`
3772
+ // the `INTERVAL` keyword is specified first.
3773
+ // In all other contexts, intervals use the `'x'::interval` style.
3774
+ static void deparseIntervalTypmods(StringInfo str, TypeName *type_name)
3775
+ {
3776
+ const char *name = strVal(lsecond(type_name->names));
3777
+ Assert(strcmp(name, "interval") == 0);
3778
+ Assert(list_length(type_name->typmods) >= 1);
3779
+ Assert(IsA(linitial(type_name->typmods), A_Const));
3780
+ Assert(IsA(&castNode(A_Const, linitial(type_name->typmods))->val, Integer));
3781
+
3782
+ int fields = intVal(&castNode(A_Const, linitial(type_name->typmods))->val);
3783
+
3784
+ // This logic is based on intervaltypmodout in timestamp.c
3785
+ switch (fields)
3786
+ {
3787
+ case INTERVAL_MASK(YEAR):
3788
+ appendStringInfoString(str, " year");
3789
+ break;
3790
+ case INTERVAL_MASK(MONTH):
3791
+ appendStringInfoString(str, " month");
3792
+ break;
3793
+ case INTERVAL_MASK(DAY):
3794
+ appendStringInfoString(str, " day");
3795
+ break;
3796
+ case INTERVAL_MASK(HOUR):
3797
+ appendStringInfoString(str, " hour");
3798
+ break;
3799
+ case INTERVAL_MASK(MINUTE):
3800
+ appendStringInfoString(str, " minute");
3801
+ break;
3802
+ case INTERVAL_MASK(SECOND):
3803
+ appendStringInfoString(str, " second");
3804
+ break;
3805
+ case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
3806
+ appendStringInfoString(str, " year to month");
3807
+ break;
3808
+ case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
3809
+ appendStringInfoString(str, " day to hour");
3810
+ break;
3811
+ case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3812
+ appendStringInfoString(str, " day to minute");
3813
+ break;
3814
+ case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3815
+ appendStringInfoString(str, " day to second");
3816
+ break;
3817
+ case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3818
+ appendStringInfoString(str, " hour to minute");
3819
+ break;
3820
+ case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3821
+ appendStringInfoString(str, " hour to second");
3822
+ break;
3823
+ case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3824
+ appendStringInfoString(str, " minute to second");
3825
+ break;
3826
+ case INTERVAL_FULL_RANGE:
3827
+ // Nothing
3828
+ break;
3829
+ default:
3830
+ Assert(false);
3831
+ break;
3832
+ }
3833
+
3834
+ if (list_length(type_name->typmods) == 2)
3835
+ {
3836
+ int precision = intVal(&castNode(A_Const, lsecond(type_name->typmods))->val);
3837
+ if (precision != INTERVAL_FULL_PRECISION)
3838
+ appendStringInfo(str, "(%d)", precision);
3839
+ }
3840
+ }
3841
+
3818
3842
  static void deparseNullTest(StringInfo str, NullTest *null_test)
3819
3843
  {
3820
3844
  // argisrow is always false in raw parser output
@@ -6899,6 +6923,22 @@ static void deparseTransactionStmt(StringInfo str, TransactionStmt *transaction_
6899
6923
  removeTrailingSpace(str);
6900
6924
  }
6901
6925
 
6926
+ // Determine if we hit SET TIME ZONE INTERVAL, that has special syntax not
6927
+ // supported for other SET statements
6928
+ static bool isSetTimeZoneInterval(VariableSetStmt* stmt)
6929
+ {
6930
+ if (!(strcmp(stmt->name, "timezone") == 0 &&
6931
+ list_length(stmt->args) == 1 &&
6932
+ IsA(linitial(stmt->args), TypeCast)))
6933
+ return false;
6934
+
6935
+ TypeName* typeName = castNode(TypeCast, linitial(stmt->args))->typeName;
6936
+
6937
+ return (list_length(typeName->names) == 2 &&
6938
+ strcmp(strVal(linitial(typeName->names)), "pg_catalog") == 0 &&
6939
+ strcmp(strVal(llast(typeName->names)), "interval") == 0);
6940
+ }
6941
+
6902
6942
  static void deparseVariableSetStmt(StringInfo str, VariableSetStmt* variable_set_stmt)
6903
6943
  {
6904
6944
  ListCell *lc;
@@ -6909,9 +6949,17 @@ static void deparseVariableSetStmt(StringInfo str, VariableSetStmt* variable_set
6909
6949
  appendStringInfoString(str, "SET ");
6910
6950
  if (variable_set_stmt->is_local)
6911
6951
  appendStringInfoString(str, "LOCAL ");
6912
- deparseVarName(str, variable_set_stmt->name);
6913
- appendStringInfoString(str, " TO ");
6914
- deparseVarList(str, variable_set_stmt->args);
6952
+ if (isSetTimeZoneInterval(variable_set_stmt))
6953
+ {
6954
+ appendStringInfoString(str, "TIME ZONE ");
6955
+ deparseVarList(str, variable_set_stmt->args);
6956
+ }
6957
+ else
6958
+ {
6959
+ deparseVarName(str, variable_set_stmt->name);
6960
+ appendStringInfoString(str, " TO ");
6961
+ deparseVarList(str, variable_set_stmt->args);
6962
+ }
6915
6963
  break;
6916
6964
  case VAR_SET_DEFAULT: /* SET var TO DEFAULT */
6917
6965
  appendStringInfoString(str, "SET ");
@@ -7938,7 +7986,7 @@ static void deparseIndexStmt(StringInfo str, IndexStmt *index_stmt)
7938
7986
 
7939
7987
  if (index_stmt->idxname != NULL)
7940
7988
  {
7941
- appendStringInfoString(str, index_stmt->idxname);
7989
+ appendStringInfoString(str, quote_identifier(index_stmt->idxname));
7942
7990
  appendStringInfoChar(str, ' ');
7943
7991
  }
7944
7992
 
@@ -139,6 +139,10 @@ module PgQuery
139
139
  @cte_names.concat(cte_names)
140
140
  statements.concat(cte_statements)
141
141
  end
142
+
143
+ if statement.select_stmt.into_clause
144
+ from_clause_items << { item: PgQuery::Node.new(range_var: statement.select_stmt.into_clause.rel), type: :ddl }
145
+ end
142
146
  # The following statements modify the contents of a table
143
147
  when :insert_stmt, :update_stmt, :delete_stmt
144
148
  value = statement.public_send(statement.node)
@@ -171,7 +175,12 @@ module PgQuery
171
175
  statements << statement.copy_stmt.query
172
176
  # The following statement types are DDL (changing table structure)
173
177
  when :alter_table_stmt
174
- from_clause_items << { item: PgQuery::Node.new(range_var: statement.alter_table_stmt.relation), type: :ddl }
178
+ case statement.alter_table_stmt.objtype
179
+ when :OBJECT_INDEX # Index # rubocop:disable Lint/EmptyWhen
180
+ # ignore `ALTER INDEX index_name`
181
+ else
182
+ from_clause_items << { item: PgQuery::Node.new(range_var: statement.alter_table_stmt.relation), type: :ddl }
183
+ end
175
184
  when :create_stmt
176
185
  from_clause_items << { item: PgQuery::Node.new(range_var: statement.create_stmt.relation), type: :ddl }
177
186
  when :create_table_as_stmt
@@ -1,3 +1,3 @@
1
1
  module PgQuery
2
- VERSION = '4.2.0'.freeze
2
+ VERSION = '4.2.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_query
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.0
4
+ version: 4.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lukas Fittl
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-16 00:00:00.000000000 Z
11
+ date: 2023-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: 3.19.2
75
+ version: 3.22.3
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: 3.19.2
82
+ version: 3.22.3
83
83
  description: Parses SQL queries using a copy of the PostgreSQL server query parser
84
84
  email: lukas@fittl.com
85
85
  executables: []
@@ -576,7 +576,7 @@ homepage: https://github.com/pganalyze/pg_query
576
576
  licenses:
577
577
  - BSD-3-Clause
578
578
  metadata: {}
579
- post_install_message:
579
+ post_install_message:
580
580
  rdoc_options:
581
581
  - "--main"
582
582
  - README.md
@@ -596,7 +596,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
596
596
  version: '0'
597
597
  requirements: []
598
598
  rubygems_version: 3.1.6
599
- signing_key:
599
+ signing_key:
600
600
  specification_version: 4
601
601
  summary: PostgreSQL query parsing and normalization library
602
602
  test_files: []