pg_query 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|