pg_query 4.2.0 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|