pg_query 1.2.0 → 1.3.0
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 +28 -0
- data/README.md +8 -4
- data/ext/pg_query/extconf.rb +15 -6
- data/lib/pg_query/deparse.rb +216 -21
- data/lib/pg_query/node_types.rb +4 -0
- data/lib/pg_query/parse.rb +3 -0
- data/lib/pg_query/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3d7de9759711a5e173db293a6120a15ee6ab1f0f4c5804e7f68aaba99ec8ff8
|
4
|
+
data.tar.gz: 974e3fb0b709021a771803e52de2a8ca19309a679d81c1ee2a3deb105f297769
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c13b36c353e6de6aaaba2b6428be7e21c933bf8ef159212cdd64ee4a765f8a664defe735f2f3e94ef6441f3403d7be6fbf19ca4d6b98e07a6b42a68a635238a
|
7
|
+
data.tar.gz: cc742d80bdf934c89e1faa1dd6e6560764339ecabb6fe2af67fd219b2a71b4fb6756e649ddc237d995413a90c6b036d33fe8492eb91030d8a891f970ca10510a
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,32 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.3.0 2020-12-28
|
4
|
+
|
5
|
+
* Incorporate newer libpg_query updates in 10-1.0.3 and 10-1.0.4
|
6
|
+
* Adds support for running on ARM
|
7
|
+
* Fixes asprintf warning during builds
|
8
|
+
* Updates to newer Postgres 10 patch release (10.15)
|
9
|
+
* Deparsing improvements by [@emin100](https://github.com/emin100)
|
10
|
+
* Add support for additional DROP statements (#147)
|
11
|
+
* Fix CREATE TABLE AS - Support without TEMP, Add ON COMMIT (#149)
|
12
|
+
* Empty target list support (#156)
|
13
|
+
* UNION parentheses (#158)
|
14
|
+
* OVERLAY keyword function (#161)
|
15
|
+
* Array indirection (#162)
|
16
|
+
* ARRAY functions (#163)
|
17
|
+
* Correctly handle column names that need escaping in INSERT and UPDATE statements (#164)
|
18
|
+
* INSERT INTO ON CONFLICT (#166)
|
19
|
+
* LATERAL JOIN (#168)
|
20
|
+
* UPDATE FROM clause (#170)
|
21
|
+
* SELECT aggregate FILTER (#175)
|
22
|
+
* INTERSECT operator (#176)
|
23
|
+
* Deparsing: Improve handling of boolean type casts [@himanshu-pro](https://github.com/himanshu-pro) & [@emin100](https://github.com/emin100)
|
24
|
+
* `tables` method: Find tables in the subquery of CREATE TABLE AS (#172) [@Tassosb](https://github.com/Tassosb)
|
25
|
+
* Support Ruby 3.0, verify SHA256 checksum of downloaded libpg_query (#178) [@stanhu](https://github.com/stanhu)
|
26
|
+
* Verify SHA256 checksum to guard against any malicious attempts to change the archive
|
27
|
+
* Use `URI.open` to fix Ruby 3.0 support
|
28
|
+
|
29
|
+
|
3
30
|
## 1.2.0 2019-11-10
|
4
31
|
|
5
32
|
* Reduce escaped keywords to Postgres-specific keywords, and ignore unreserved keywords
|
@@ -8,6 +35,7 @@
|
|
8
35
|
* Note that this will lead to different output than in earlier pg_query versions,
|
9
36
|
in some cases
|
10
37
|
|
38
|
+
|
11
39
|
## 1.1.1 2019-11-10
|
12
40
|
|
13
41
|
* Deparsing improvements by [@emin100](https://github.com/emin100)
|
data/README.md
CHANGED
@@ -151,11 +151,15 @@ to support parsing normalized queries containing `?` replacement characters.
|
|
151
151
|
|
152
152
|
Currently tested and officially supported Ruby versions:
|
153
153
|
|
154
|
-
*
|
155
|
-
*
|
156
|
-
*
|
157
|
-
*
|
154
|
+
* CRuby 2.5
|
155
|
+
* CRuby 2.6
|
156
|
+
* CRuby 2.7
|
157
|
+
* CRuby 3.0
|
158
158
|
|
159
|
+
Not supported:
|
160
|
+
|
161
|
+
* JRuby: `pg_query` relies on a C extension, which is discouraged / not properly supported for JRuby
|
162
|
+
* TruffleRuby: GraalVM [does not support sigjmp](https://www.graalvm.org/reference-manual/llvm/NativeExecution/), which is used by the Postgres error handling code (`pg_query` uses a copy of the Postgres parser & error handling code)
|
159
163
|
|
160
164
|
## Resources
|
161
165
|
|
data/ext/pg_query/extconf.rb
CHANGED
@@ -1,30 +1,39 @@
|
|
1
1
|
# rubocop:disable Style/GlobalVars
|
2
2
|
|
3
|
+
require 'digest'
|
3
4
|
require 'mkmf'
|
4
5
|
require 'open-uri'
|
5
6
|
|
6
|
-
LIB_PG_QUERY_TAG = '10-1.0.
|
7
|
+
LIB_PG_QUERY_TAG = '10-1.0.4'.freeze
|
8
|
+
LIB_PG_QUERY_SHA256SUM = '88cc90296e5fcaaebd0b360c46698b7c5badddf86f120e249ef682a820d41338'.freeze
|
7
9
|
|
8
10
|
workdir = Dir.pwd
|
9
11
|
libdir = File.join(workdir, 'libpg_query-' + LIB_PG_QUERY_TAG)
|
10
12
|
gemdir = File.join(__dir__, '../..')
|
11
13
|
libfile = libdir + '/libpg_query.a'
|
14
|
+
filename = File.join(workdir, 'libpg_query-' + LIB_PG_QUERY_TAG + '.tar.gz')
|
12
15
|
|
13
|
-
unless File.exist?(
|
14
|
-
File.open(
|
15
|
-
open('https://codeload.github.com/lfittl/libpg_query/tar.gz/' + LIB_PG_QUERY_TAG, 'rb') do |read_file|
|
16
|
+
unless File.exist?(filename)
|
17
|
+
File.open(filename, 'wb') do |target_file|
|
18
|
+
URI.open('https://codeload.github.com/lfittl/libpg_query/tar.gz/' + LIB_PG_QUERY_TAG, 'rb') do |read_file|
|
16
19
|
target_file.write(read_file.read)
|
17
20
|
end
|
18
21
|
end
|
22
|
+
|
23
|
+
checksum = Digest::SHA256.hexdigest(File.read(filename))
|
24
|
+
|
25
|
+
if checksum != LIB_PG_QUERY_SHA256SUM
|
26
|
+
raise "SHA256 of #{filename} does not match: got #{checksum}, expected #{expected_sha256}"
|
27
|
+
end
|
19
28
|
end
|
20
29
|
|
21
30
|
unless Dir.exist?(libdir)
|
22
|
-
system("tar -xzf #{
|
31
|
+
system("tar -xzf #{filename}") || raise('ERROR')
|
23
32
|
end
|
24
33
|
|
25
34
|
unless Dir.exist?(libfile)
|
26
35
|
# Build libpg_query (and parts of PostgreSQL)
|
27
|
-
system("cd
|
36
|
+
system(format("cd %s; %s build", libdir, ENV['MAKE'] || (RUBY_PLATFORM =~ /bsd/ ? 'gmake' : 'make')))
|
28
37
|
end
|
29
38
|
|
30
39
|
# Copy test files (this intentionally overwrites existing files!)
|
data/lib/pg_query/deparse.rb
CHANGED
@@ -92,7 +92,7 @@ class PgQuery
|
|
92
92
|
when COLUMN_DEF
|
93
93
|
deparse_columndef(node)
|
94
94
|
when COLUMN_REF
|
95
|
-
deparse_columnref(node)
|
95
|
+
deparse_columnref(node, context)
|
96
96
|
when COMMON_TABLE_EXPR
|
97
97
|
deparse_cte(node)
|
98
98
|
when COMPOSITE_TYPE_STMT
|
@@ -127,8 +127,14 @@ class PgQuery
|
|
127
127
|
deparse_delete_from(node)
|
128
128
|
when DISCARD_STMT
|
129
129
|
deparse_discard(node)
|
130
|
+
when DROP_ROLE
|
131
|
+
deparse_drop_role(node)
|
130
132
|
when DROP_STMT
|
131
133
|
deparse_drop(node)
|
134
|
+
when DROP_SUBSCRIPTION
|
135
|
+
deparse_drop_subscription(node)
|
136
|
+
when DROP_TABLESPACE
|
137
|
+
deparse_drop_tablespace(node)
|
132
138
|
when EXPLAIN_STMT
|
133
139
|
deparse_explain(node)
|
134
140
|
when EXECUTE_STMT
|
@@ -210,6 +216,8 @@ class PgQuery
|
|
210
216
|
format("'%s'", node['str'].gsub("'", "''"))
|
211
217
|
elsif [FUNC_CALL, TYPE_NAME, :operator, :defname_as].include?(context)
|
212
218
|
node['str']
|
219
|
+
elsif context == :excluded
|
220
|
+
node['str'].casecmp('EXCLUDED').zero? ? node['str'].upcase : deparse_identifier(node['str'], escape_always: true)
|
213
221
|
else
|
214
222
|
deparse_identifier(node['str'], escape_always: true)
|
215
223
|
end
|
@@ -279,9 +287,9 @@ class PgQuery
|
|
279
287
|
output.join(' ')
|
280
288
|
end
|
281
289
|
|
282
|
-
def deparse_columnref(node)
|
290
|
+
def deparse_columnref(node, context = false)
|
283
291
|
node['fields'].map do |field|
|
284
|
-
field.is_a?(String) ? '"' + field + '"' : deparse_item(field)
|
292
|
+
field.is_a?(String) ? '"' + field + '"' : deparse_item(field, context)
|
285
293
|
end.join('.')
|
286
294
|
end
|
287
295
|
|
@@ -302,7 +310,11 @@ class PgQuery
|
|
302
310
|
def deparse_a_indirection(node)
|
303
311
|
output = []
|
304
312
|
arg = deparse_item(node['arg'])
|
305
|
-
|
313
|
+
array_indirection = []
|
314
|
+
if node['indirection']
|
315
|
+
array_indirection = node['indirection'].select { |a| a.key?(A_INDICES) }
|
316
|
+
end
|
317
|
+
output << if node['arg'].key?(FUNC_CALL) || node['arg'].key?(A_EXPR) || (node['arg'].key?(SUB_LINK) && array_indirection.count.zero?)
|
306
318
|
"(#{arg})."
|
307
319
|
else
|
308
320
|
arg
|
@@ -358,7 +370,7 @@ class PgQuery
|
|
358
370
|
|
359
371
|
def deparse_object_with_args(node)
|
360
372
|
output = []
|
361
|
-
output
|
373
|
+
output << deparse_item_list(node['objname']).join('.')
|
362
374
|
unless node['args_unspecified']
|
363
375
|
args = node.fetch('objargs', []).map(&method(:deparse_item)).join(', ')
|
364
376
|
output << "(#{args})"
|
@@ -387,7 +399,9 @@ class PgQuery
|
|
387
399
|
if context == :select
|
388
400
|
[deparse_item(node['val']), deparse_identifier(node['name'])].compact.join(' AS ')
|
389
401
|
elsif context == :update
|
390
|
-
[node['name'], deparse_item(node['val'])].compact.join(' = ')
|
402
|
+
[deparse_identifier(node['name']), deparse_item(node['val'])].compact.join(' = ')
|
403
|
+
elsif context == :excluded
|
404
|
+
[deparse_identifier(node['name'], escape_always: true), deparse_item(node['val'], context)].compact.join(' = ')
|
391
405
|
elsif node['val'].nil?
|
392
406
|
node['name']
|
393
407
|
else
|
@@ -403,10 +417,17 @@ class PgQuery
|
|
403
417
|
# COUNT(*)
|
404
418
|
args << '*' if node['agg_star']
|
405
419
|
|
406
|
-
name = (node['funcname'].map { |n| deparse_item(n, FUNC_CALL) }
|
407
|
-
|
408
|
-
|
409
|
-
|
420
|
+
name = (node['funcname'].map { |n| deparse_item(n, FUNC_CALL) }).join('.')
|
421
|
+
if name == 'pg_catalog.overlay'
|
422
|
+
# Note that this is a bit odd, but "OVERLAY" is a keyword on its own merit, and only accepts the
|
423
|
+
# keyword parameter style when its called as a keyword, not as a regular function (i.e. pg_catalog.overlay)
|
424
|
+
output << format('OVERLAY(%s PLACING %s FROM %s FOR %s)', args[0], args[1], args[2], args[3])
|
425
|
+
else
|
426
|
+
distinct = node['agg_distinct'] ? 'DISTINCT ' : ''
|
427
|
+
output << format('%s(%s%s)', name, distinct, args.join(', '))
|
428
|
+
output << format('FILTER (WHERE %s)', deparse_item(node['agg_filter'])) if node['agg_filter']
|
429
|
+
output << format('OVER %s', deparse_item(node['over'])) if node['over']
|
430
|
+
end
|
410
431
|
|
411
432
|
output.join(' ')
|
412
433
|
end
|
@@ -987,8 +1008,22 @@ class PgQuery
|
|
987
1008
|
|
988
1009
|
def deparse_create_table_as(node)
|
989
1010
|
output = []
|
990
|
-
output << 'CREATE
|
1011
|
+
output << 'CREATE'
|
1012
|
+
|
1013
|
+
into = node['into']['IntoClause']
|
1014
|
+
persistence = relpersistence(into['rel'])
|
1015
|
+
output << persistence if persistence
|
1016
|
+
|
1017
|
+
output << 'TABLE'
|
1018
|
+
|
991
1019
|
output << deparse_item(node['into'])
|
1020
|
+
|
1021
|
+
if into['onCommit'] > 0
|
1022
|
+
output << 'ON COMMIT'
|
1023
|
+
output << 'DELETE ROWS' if into['onCommit'] == 2
|
1024
|
+
output << 'DROP' if into['onCommit'] == 3
|
1025
|
+
end
|
1026
|
+
|
992
1027
|
output << 'AS'
|
993
1028
|
output << deparse_item(node['query'])
|
994
1029
|
output.join(' ')
|
@@ -1020,13 +1055,17 @@ class PgQuery
|
|
1020
1055
|
format('%s %s ALL (%s)', deparse_item(node['testexpr']), deparse_item(node['operName'][0], :operator), deparse_item(node['subselect']))
|
1021
1056
|
elsif node['subLinkType'] == SUBLINK_TYPE_EXISTS
|
1022
1057
|
format('EXISTS(%s)', deparse_item(node['subselect']))
|
1058
|
+
elsif node['subLinkType'] == SUBLINK_TYPE_ARRAY
|
1059
|
+
format('ARRAY(%s)', deparse_item(node['subselect']))
|
1023
1060
|
else
|
1024
1061
|
format('(%s)', deparse_item(node['subselect']))
|
1025
1062
|
end
|
1026
1063
|
end
|
1027
1064
|
|
1028
1065
|
def deparse_rangesubselect(node)
|
1029
|
-
output = '
|
1066
|
+
output = ''
|
1067
|
+
output = 'LATERAL ' if node['lateral']
|
1068
|
+
output = output + '(' + deparse_item(node['subquery']) + ')'
|
1030
1069
|
if node['alias']
|
1031
1070
|
output + ' ' + deparse_item(node['alias'])
|
1032
1071
|
else
|
@@ -1044,10 +1083,14 @@ class PgQuery
|
|
1044
1083
|
output << deparse_item(node['withClause']) if node['withClause']
|
1045
1084
|
|
1046
1085
|
if node['op'] == 1
|
1086
|
+
output << '(' if node['larg']['SelectStmt']['sortClause']
|
1047
1087
|
output << deparse_item(node['larg'])
|
1088
|
+
output << ')' if node['larg']['SelectStmt']['sortClause']
|
1048
1089
|
output << 'UNION'
|
1049
1090
|
output << 'ALL' if node['all']
|
1091
|
+
output << '(' if node['rarg']['SelectStmt']['sortClause']
|
1050
1092
|
output << deparse_item(node['rarg'])
|
1093
|
+
output << ')' if node['rarg']['SelectStmt']['sortClause']
|
1051
1094
|
output.join(' ')
|
1052
1095
|
end
|
1053
1096
|
|
@@ -1058,8 +1101,16 @@ class PgQuery
|
|
1058
1101
|
output.join(' ')
|
1059
1102
|
end
|
1060
1103
|
|
1104
|
+
if node['op'] == 2
|
1105
|
+
output << deparse_item(node['larg'])
|
1106
|
+
output << 'INTERSECT'
|
1107
|
+
output << deparse_item(node['rarg'])
|
1108
|
+
output.join(' ')
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
output << 'SELECT' if node[FROM_CLAUSE_FIELD] || node[TARGET_LIST_FIELD]
|
1112
|
+
|
1061
1113
|
if node[TARGET_LIST_FIELD]
|
1062
|
-
output << 'SELECT'
|
1063
1114
|
if node['distinctClause']
|
1064
1115
|
output << 'DISTINCT'
|
1065
1116
|
unless node['distinctClause'].compact.empty?
|
@@ -1167,12 +1218,16 @@ class PgQuery
|
|
1167
1218
|
|
1168
1219
|
if node['cols']
|
1169
1220
|
output << '(' + node['cols'].map do |column|
|
1170
|
-
deparse_item(column)
|
1221
|
+
deparse_item(column, :select)
|
1171
1222
|
end.join(', ') + ')'
|
1172
1223
|
end
|
1173
1224
|
|
1174
1225
|
output << deparse_item(node['selectStmt'])
|
1175
1226
|
|
1227
|
+
if node['onConflictClause']
|
1228
|
+
output << deparse_insert_onconflict(node['onConflictClause'][ON_CONFLICT_CLAUSE])
|
1229
|
+
end
|
1230
|
+
|
1176
1231
|
if node['returningList']
|
1177
1232
|
output << 'RETURNING'
|
1178
1233
|
output << node['returningList'].map do |column|
|
@@ -1183,6 +1238,40 @@ class PgQuery
|
|
1183
1238
|
output.join(' ')
|
1184
1239
|
end
|
1185
1240
|
|
1241
|
+
def deparse_insert_onconflict(node) # rubocop:disable Metrics/CyclomaticComplexity
|
1242
|
+
output = []
|
1243
|
+
output << 'ON CONFLICT'
|
1244
|
+
|
1245
|
+
infer = node['infer']['InferClause']
|
1246
|
+
if infer['indexElems']
|
1247
|
+
output << '(' + infer['indexElems'].map do |column|
|
1248
|
+
'"' + column['IndexElem']['name'] + '"'
|
1249
|
+
end.join(', ') + ')'
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
if infer['conname']
|
1253
|
+
output << 'ON CONSTRAINT'
|
1254
|
+
output << deparse_identifier(infer['conname'], escape_always: true)
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
output << 'DO UPDATE' if node['action'] == 2
|
1258
|
+
|
1259
|
+
if node[TARGET_LIST_FIELD]
|
1260
|
+
output << 'SET ' + node[TARGET_LIST_FIELD].map do |column|
|
1261
|
+
deparse_item(column, :excluded)
|
1262
|
+
end.join(', ')
|
1263
|
+
end
|
1264
|
+
|
1265
|
+
if infer['whereClause']
|
1266
|
+
output << 'WHERE'
|
1267
|
+
output << deparse_item(infer['whereClause'])
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
output << 'DO NOTHING' if node['action'] == 1
|
1271
|
+
|
1272
|
+
output.join(' ')
|
1273
|
+
end
|
1274
|
+
|
1186
1275
|
def deparse_update(node)
|
1187
1276
|
output = []
|
1188
1277
|
output << deparse_item(node['withClause']) if node['withClause']
|
@@ -1198,6 +1287,13 @@ class PgQuery
|
|
1198
1287
|
output << columns.join(', ')
|
1199
1288
|
end
|
1200
1289
|
|
1290
|
+
if node[FROM_CLAUSE_FIELD]
|
1291
|
+
output << 'FROM'
|
1292
|
+
output << node[FROM_CLAUSE_FIELD].map do |item|
|
1293
|
+
deparse_item(item)
|
1294
|
+
end.join(', ')
|
1295
|
+
end
|
1296
|
+
|
1201
1297
|
if node['whereClause']
|
1202
1298
|
output << 'WHERE'
|
1203
1299
|
output << deparse_item(node['whereClause'])
|
@@ -1214,13 +1310,34 @@ class PgQuery
|
|
1214
1310
|
output.join(' ')
|
1215
1311
|
end
|
1216
1312
|
|
1313
|
+
# Builds a properly-qualified reference to a built-in Postgres type.
|
1314
|
+
#
|
1315
|
+
# Inspired by SystemTypeName in Postgres' gram.y, but without node name
|
1316
|
+
# and locations, to simplify comparison.
|
1317
|
+
def make_system_type_name(name)
|
1318
|
+
{
|
1319
|
+
'names' => [
|
1320
|
+
{ 'String' => { 'str' => 'pg_catalog' } },
|
1321
|
+
{ 'String' => { 'str' => name } }
|
1322
|
+
],
|
1323
|
+
'typemod' => -1
|
1324
|
+
}
|
1325
|
+
end
|
1326
|
+
|
1327
|
+
def make_string(str)
|
1328
|
+
{ 'String' => { 'str' => str } }
|
1329
|
+
end
|
1330
|
+
|
1217
1331
|
def deparse_typecast(node)
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1332
|
+
# Handle "bool" or "false" in the statement, which is represented as a typecast
|
1333
|
+
# (other boolean casts should be represented as a cast, i.e. don't need special handling)
|
1334
|
+
if node['arg'][A_CONST] && node['typeName'][TYPE_NAME].slice('names', 'typemod') == make_system_type_name('bool')
|
1335
|
+
return 'true' if node['arg'][A_CONST]['val'] == make_string('t')
|
1336
|
+
return 'false' if node['arg'][A_CONST]['val'] == make_string('f')
|
1223
1337
|
end
|
1338
|
+
|
1339
|
+
context = true if node['arg']['A_Expr']
|
1340
|
+
deparse_item(node['arg'], context) + '::' + deparse_typename(node['typeName'][TYPE_NAME])
|
1224
1341
|
end
|
1225
1342
|
|
1226
1343
|
def deparse_typename(node)
|
@@ -1446,20 +1563,98 @@ class PgQuery
|
|
1446
1563
|
|
1447
1564
|
def deparse_drop(node) # rubocop:disable Metrics/CyclomaticComplexity
|
1448
1565
|
output = ['DROP']
|
1449
|
-
|
1566
|
+
|
1567
|
+
output << 'ACCESS METHOD' if node['removeType'] == OBJECT_TYPE_ACCESS_METHOD
|
1568
|
+
output << 'AGGREGATE' if node['removeType'] == OBJECT_TYPE_AGGREGATE
|
1569
|
+
output << 'CAST' if node['removeType'] == OBJECT_TYPE_CAST
|
1570
|
+
output << 'COLLATION' if node['removeType'] == OBJECT_TYPE_COLLATION
|
1571
|
+
output << 'DOMAIN' if node['removeType'] == OBJECT_TYPE_DOMAIN
|
1572
|
+
output << 'CONVERSION' if node['removeType'] == OBJECT_TYPE_CONVERSION
|
1573
|
+
output << 'EVENT TRIGGER' if node['removeType'] == OBJECT_TYPE_EVENT_TRIGGER
|
1574
|
+
output << 'EXTENSION' if node['removeType'] == OBJECT_TYPE_EXTENSION
|
1575
|
+
output << 'FOREIGN DATA WRAPPER' if node['removeType'] == OBJECT_TYPE_FDW
|
1576
|
+
output << 'FOREIGN TABLE' if node['removeType'] == OBJECT_TYPE_FOREIGN_TABLE
|
1577
|
+
output << 'FUNCTION' if node['removeType'] == OBJECT_TYPE_FUNCTION
|
1578
|
+
output << 'INDEX' if node['removeType'] == OBJECT_TYPE_INDEX
|
1579
|
+
output << 'MATERIALIZED VIEW' if node['removeType'] == OBJECT_TYPE_MATVIEW
|
1580
|
+
output << 'OPERATOR CLASS' if node['removeType'] == OBJECT_TYPE_OPCLASS
|
1581
|
+
output << 'OPERATOR FAMILY' if node['removeType'] == OBJECT_TYPE_OPFAMILY
|
1582
|
+
output << 'POLICY' if node['removeType'] == OBJECT_TYPE_POLICY
|
1583
|
+
output << 'PUBLICATION' if node['removeType'] == OBJECT_TYPE_PUBLICATION
|
1584
|
+
output << 'RULE' if node['removeType'] == OBJECT_TYPE_RULE
|
1450
1585
|
output << 'SCHEMA' if node['removeType'] == OBJECT_TYPE_SCHEMA
|
1586
|
+
output << 'SERVER' if node['removeType'] == OBJECT_TYPE_FOREIGN_SERVER
|
1587
|
+
output << 'SEQUENCE' if node['removeType'] == OBJECT_TYPE_SEQUENCE
|
1588
|
+
output << 'STATISTICS' if node['removeType'] == OBJECT_TYPE_STATISTIC_EXT
|
1589
|
+
output << 'TABLE' if node['removeType'] == OBJECT_TYPE_TABLE
|
1590
|
+
output << 'TRANSFORM' if node['removeType'] == OBJECT_TYPE_TRANSFORM
|
1591
|
+
output << 'TRIGGER' if node['removeType'] == OBJECT_TYPE_TRIGGER
|
1592
|
+
output << 'TEXT SEARCH CONFIGURATION' if node['removeType'] == OBJECT_TYPE_TSCONFIGURATION
|
1593
|
+
output << 'TEXT SEARCH DICTIONARY' if node['removeType'] == OBJECT_TYPE_TSDICTIONARY
|
1594
|
+
output << 'TEXT SEARCH PARSER' if node['removeType'] == OBJECT_TYPE_TSPARSER
|
1595
|
+
output << 'TEXT SEARCH TEMPLATE' if node['removeType'] == OBJECT_TYPE_TSTEMPLATE
|
1596
|
+
output << 'TYPE' if node['removeType'] == OBJECT_TYPE_TYPE
|
1597
|
+
output << 'VIEW' if node['removeType'] == OBJECT_TYPE_VIEW
|
1598
|
+
|
1451
1599
|
output << 'CONCURRENTLY' if node['concurrent']
|
1452
1600
|
output << 'IF EXISTS' if node['missing_ok']
|
1453
1601
|
|
1454
1602
|
objects = node['objects']
|
1455
1603
|
objects = [objects] unless objects[0].is_a?(Array)
|
1456
|
-
|
1604
|
+
case node['removeType']
|
1605
|
+
when OBJECT_TYPE_CAST
|
1606
|
+
object = objects[0]
|
1607
|
+
output << format('(%s)', deparse_item_list(object).join(' AS '))
|
1608
|
+
when OBJECT_TYPE_FUNCTION, OBJECT_TYPE_AGGREGATE, OBJECT_TYPE_SCHEMA, OBJECT_TYPE_EXTENSION
|
1609
|
+
output << objects.map { |list| list.map { |object_line| deparse_item(object_line) } }.join(', ')
|
1610
|
+
when OBJECT_TYPE_OPFAMILY, OBJECT_TYPE_OPCLASS
|
1611
|
+
object = objects[0]
|
1612
|
+
output << deparse_item(object[1]) if object.length == 2
|
1613
|
+
output << deparse_item_list(object[1..-1]).join('.') if object.length == 3
|
1614
|
+
output << 'USING'
|
1615
|
+
output << deparse_item(object[0])
|
1616
|
+
when OBJECT_TYPE_TRIGGER, OBJECT_TYPE_RULE, OBJECT_TYPE_POLICY
|
1617
|
+
object = objects[0]
|
1618
|
+
output << deparse_item(object[-1])
|
1619
|
+
output << 'ON'
|
1620
|
+
output << deparse_item(object[0]) if object.length == 2
|
1621
|
+
output << deparse_item_list(object[0..1]).join('.') if object.length == 3
|
1622
|
+
when OBJECT_TYPE_TRANSFORM
|
1623
|
+
object = objects[0]
|
1624
|
+
output << 'FOR'
|
1625
|
+
output << deparse_item(object[0])
|
1626
|
+
output << 'LANGUAGE'
|
1627
|
+
output << deparse_item(object[1])
|
1628
|
+
else
|
1629
|
+
output << objects.map { |list| list.map { |object_line| deparse_item(object_line) }.join('.') }.join(', ')
|
1630
|
+
end
|
1457
1631
|
|
1458
1632
|
output << 'CASCADE' if node['behavior'] == 1
|
1459
1633
|
|
1460
1634
|
output.join(' ')
|
1461
1635
|
end
|
1462
1636
|
|
1637
|
+
def deparse_drop_role(node)
|
1638
|
+
output = ['DROP ROLE']
|
1639
|
+
output << 'IF EXISTS' if node['missing_ok']
|
1640
|
+
output << node['roles'].map { |role| deparse_identifier(role['RoleSpec']['rolename']) }.join(', ')
|
1641
|
+
output.join(' ')
|
1642
|
+
end
|
1643
|
+
|
1644
|
+
def deparse_drop_subscription(node)
|
1645
|
+
output = ['DROP SUBSCRIPTION']
|
1646
|
+
output << 'IF EXISTS' if node['missing_ok']
|
1647
|
+
output << deparse_identifier(node['subname'])
|
1648
|
+
output.join(' ')
|
1649
|
+
end
|
1650
|
+
|
1651
|
+
def deparse_drop_tablespace(node)
|
1652
|
+
output = ['DROP TABLESPACE']
|
1653
|
+
output << 'IF EXISTS' if node['missing_ok']
|
1654
|
+
output << node['tablespacename']
|
1655
|
+
output.join(' ')
|
1656
|
+
end
|
1657
|
+
|
1463
1658
|
def deparse_explain(node)
|
1464
1659
|
output = ['EXPLAIN']
|
1465
1660
|
options = node.fetch('options', []).map { |option| option['DefElem']['defname'].upcase }
|
data/lib/pg_query/node_types.rb
CHANGED
@@ -44,6 +44,9 @@ class PgQuery
|
|
44
44
|
DISCARD_STMT = 'DiscardStmt'.freeze
|
45
45
|
DO_STMT = 'DoStmt'.freeze
|
46
46
|
DROP_STMT = 'DropStmt'.freeze
|
47
|
+
DROP_SUBSCRIPTION = 'DropSubscriptionStmt'.freeze
|
48
|
+
DROP_TABLESPACE = 'DropTableSpaceStmt'.freeze
|
49
|
+
DROP_ROLE = 'DropRoleStmt'.freeze
|
47
50
|
EXECUTE_STMT = 'ExecuteStmt'.freeze
|
48
51
|
EXPLAIN_STMT = 'ExplainStmt'.freeze
|
49
52
|
FETCH_STMT = 'FetchStmt'.freeze
|
@@ -65,6 +68,7 @@ class PgQuery
|
|
65
68
|
NULL_TEST = 'NullTest'.freeze
|
66
69
|
OBJECT_WITH_ARGS = 'ObjectWithArgs'.freeze
|
67
70
|
OID_LIST = 'OidList'.freeze
|
71
|
+
ON_CONFLICT_CLAUSE = 'OnConflictClause'.freeze
|
68
72
|
PARAM_REF = 'ParamRef'.freeze
|
69
73
|
PREPARE_STMT = 'PrepareStmt'.freeze
|
70
74
|
RANGE_FUNCTION = 'RangeFunction'.freeze
|
data/lib/pg_query/parse.rb
CHANGED
@@ -124,6 +124,9 @@ class PgQuery
|
|
124
124
|
if statement[CREATE_TABLE_AS_STMT]['into'] && statement[CREATE_TABLE_AS_STMT]['into'][INTO_CLAUSE]['rel']
|
125
125
|
from_clause_items << { item: statement[CREATE_TABLE_AS_STMT]['into'][INTO_CLAUSE]['rel'], type: :ddl }
|
126
126
|
end
|
127
|
+
if statement[CREATE_TABLE_AS_STMT]['query']
|
128
|
+
statements << statement[CREATE_TABLE_AS_STMT]['query']
|
129
|
+
end
|
127
130
|
when TRUNCATE_STMT
|
128
131
|
from_clause_items += statement.values[0]['relations'].map { |r| { item: r, type: :ddl } }
|
129
132
|
when VIEW_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: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lukas Fittl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
requirements: []
|
126
|
-
rubygems_version: 3.0.
|
126
|
+
rubygems_version: 3.0.3
|
127
127
|
signing_key:
|
128
128
|
specification_version: 4
|
129
129
|
summary: PostgreSQL query parsing and normalization library
|