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 +4 -4
- data/CHANGELOG.md +17 -2
- data/README.md +29 -33
- data/Rakefile +2 -2
- data/ext/pg_query/pg_query_deparse.c +120 -72
- data/lib/pg_query/parse.rb +10 -1
- data/lib/pg_query/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32a51d0f608ebfe0e27991db81924289d86c7f0b9a90ab56a321b9ef5c21794e
|
4
|
+
data.tar.gz: 0d56fc7fd092c3406a40d102f420d637347d45f77203cc4de2f4613a111438dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1adefd8ebb6ab5595bf97f79ca027fb5e3c6cc351fbac310252a1a0f6d13c566c2a09b268ff5c173d0ab968ce7171f6b29dc2c071814b407883d77c315d50ae
|
7
|
+
data.tar.gz: 824faa4086e30b42199491049a16c4e7287a9c5b6146aba89ef353b77cfe6a3452ae4f2f09169b5851ff3b266df71bd02a948141f2e4bd05e02425e09db5b6f1
|
data/CHANGELOG.md
CHANGED
@@ -4,17 +4,32 @@
|
|
4
4
|
|
5
5
|
* ...
|
6
6
|
|
7
|
-
|
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
|
-
|
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
|
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
|
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:
|
28
|
+
=> #<PgQuery::ParserResult:0x000000012000c438
|
29
29
|
@query="SELECT 1",
|
30
30
|
@tree=<PgQuery::ParseResult:
|
31
|
-
version:
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
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
|
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:
|
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/
|
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
|
-
|
232
|
-
|
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
|
-
|
235
|
-
Copyright (c)
|
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.
|
9
|
-
LIB_PG_QUERY_SHA256SUM = '
|
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
|
-
|
6913
|
-
|
6914
|
-
|
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
|
|
data/lib/pg_query/parse.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/pg_query/version.rb
CHANGED
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.
|
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-
|
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.
|
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.
|
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: []
|