pg_query 1.0.2 → 2.0.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.
Files changed (107) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +162 -40
  3. data/README.md +80 -69
  4. data/Rakefile +85 -4
  5. data/ext/pg_query/extconf.rb +4 -32
  6. data/ext/pg_query/guc-file.c +0 -0
  7. data/ext/pg_query/pg_query.c +104 -0
  8. data/ext/pg_query/pg_query.pb-c.c +37628 -0
  9. data/ext/pg_query/pg_query_deparse.c +9953 -0
  10. data/ext/pg_query/pg_query_fingerprint.c +292 -0
  11. data/ext/pg_query/pg_query_fingerprint.h +8 -0
  12. data/ext/pg_query/pg_query_internal.h +24 -0
  13. data/ext/pg_query/pg_query_json_plpgsql.c +738 -0
  14. data/ext/pg_query/pg_query_json_plpgsql.h +9 -0
  15. data/ext/pg_query/pg_query_normalize.c +437 -0
  16. data/ext/pg_query/pg_query_outfuncs.h +10 -0
  17. data/ext/pg_query/pg_query_outfuncs_json.c +297 -0
  18. data/ext/pg_query/pg_query_outfuncs_protobuf.c +237 -0
  19. data/ext/pg_query/pg_query_parse.c +148 -0
  20. data/ext/pg_query/pg_query_parse_plpgsql.c +460 -0
  21. data/ext/pg_query/pg_query_readfuncs.h +11 -0
  22. data/ext/pg_query/pg_query_readfuncs_protobuf.c +142 -0
  23. data/ext/pg_query/pg_query_ruby.c +108 -12
  24. data/ext/pg_query/pg_query_scan.c +173 -0
  25. data/ext/pg_query/pg_query_split.c +221 -0
  26. data/ext/pg_query/protobuf-c.c +3660 -0
  27. data/ext/pg_query/src_backend_catalog_namespace.c +1051 -0
  28. data/ext/pg_query/src_backend_catalog_pg_proc.c +142 -0
  29. data/ext/pg_query/src_backend_commands_define.c +117 -0
  30. data/ext/pg_query/src_backend_libpq_pqcomm.c +651 -0
  31. data/ext/pg_query/src_backend_nodes_bitmapset.c +513 -0
  32. data/ext/pg_query/src_backend_nodes_copyfuncs.c +6013 -0
  33. data/ext/pg_query/src_backend_nodes_equalfuncs.c +4003 -0
  34. data/ext/pg_query/src_backend_nodes_extensible.c +99 -0
  35. data/ext/pg_query/src_backend_nodes_list.c +922 -0
  36. data/ext/pg_query/src_backend_nodes_makefuncs.c +417 -0
  37. data/ext/pg_query/src_backend_nodes_nodeFuncs.c +1363 -0
  38. data/ext/pg_query/src_backend_nodes_value.c +84 -0
  39. data/ext/pg_query/src_backend_parser_gram.c +47456 -0
  40. data/ext/pg_query/src_backend_parser_parse_expr.c +313 -0
  41. data/ext/pg_query/src_backend_parser_parser.c +497 -0
  42. data/ext/pg_query/src_backend_parser_scan.c +7091 -0
  43. data/ext/pg_query/src_backend_parser_scansup.c +160 -0
  44. data/ext/pg_query/src_backend_postmaster_postmaster.c +2230 -0
  45. data/ext/pg_query/src_backend_storage_ipc_ipc.c +192 -0
  46. data/ext/pg_query/src_backend_storage_lmgr_s_lock.c +370 -0
  47. data/ext/pg_query/src_backend_tcop_postgres.c +776 -0
  48. data/ext/pg_query/src_backend_utils_adt_datum.c +326 -0
  49. data/ext/pg_query/src_backend_utils_adt_expandeddatum.c +98 -0
  50. data/ext/pg_query/src_backend_utils_adt_format_type.c +136 -0
  51. data/ext/pg_query/src_backend_utils_adt_ruleutils.c +1683 -0
  52. data/ext/pg_query/src_backend_utils_error_assert.c +74 -0
  53. data/ext/pg_query/src_backend_utils_error_elog.c +1748 -0
  54. data/ext/pg_query/src_backend_utils_fmgr_fmgr.c +570 -0
  55. data/ext/pg_query/src_backend_utils_hash_dynahash.c +1086 -0
  56. data/ext/pg_query/src_backend_utils_init_globals.c +168 -0
  57. data/ext/pg_query/src_backend_utils_mb_mbutils.c +839 -0
  58. data/ext/pg_query/src_backend_utils_misc_guc.c +1831 -0
  59. data/ext/pg_query/src_backend_utils_mmgr_aset.c +1560 -0
  60. data/ext/pg_query/src_backend_utils_mmgr_mcxt.c +1006 -0
  61. data/ext/pg_query/src_common_encnames.c +158 -0
  62. data/ext/pg_query/src_common_keywords.c +39 -0
  63. data/ext/pg_query/src_common_kwlist_d.h +1081 -0
  64. data/ext/pg_query/src_common_kwlookup.c +91 -0
  65. data/ext/pg_query/src_common_psprintf.c +158 -0
  66. data/ext/pg_query/src_common_string.c +86 -0
  67. data/ext/pg_query/src_common_stringinfo.c +336 -0
  68. data/ext/pg_query/src_common_wchar.c +1651 -0
  69. data/ext/pg_query/src_pl_plpgsql_src_pl_comp.c +1133 -0
  70. data/ext/pg_query/src_pl_plpgsql_src_pl_funcs.c +877 -0
  71. data/ext/pg_query/src_pl_plpgsql_src_pl_gram.c +6533 -0
  72. data/ext/pg_query/src_pl_plpgsql_src_pl_handler.c +107 -0
  73. data/ext/pg_query/src_pl_plpgsql_src_pl_reserved_kwlist_d.h +123 -0
  74. data/ext/pg_query/src_pl_plpgsql_src_pl_scanner.c +671 -0
  75. data/ext/pg_query/src_pl_plpgsql_src_pl_unreserved_kwlist_d.h +255 -0
  76. data/ext/pg_query/src_port_erand48.c +127 -0
  77. data/ext/pg_query/src_port_pg_bitutils.c +246 -0
  78. data/ext/pg_query/src_port_pgsleep.c +69 -0
  79. data/ext/pg_query/src_port_pgstrcasecmp.c +83 -0
  80. data/ext/pg_query/src_port_qsort.c +240 -0
  81. data/ext/pg_query/src_port_random.c +31 -0
  82. data/ext/pg_query/src_port_snprintf.c +1449 -0
  83. data/ext/pg_query/src_port_strerror.c +324 -0
  84. data/ext/pg_query/src_port_strnlen.c +39 -0
  85. data/ext/pg_query/xxhash.c +43 -0
  86. data/lib/pg_query.rb +7 -4
  87. data/lib/pg_query/constants.rb +21 -0
  88. data/lib/pg_query/deparse.rb +16 -991
  89. data/lib/pg_query/filter_columns.rb +86 -85
  90. data/lib/pg_query/fingerprint.rb +122 -87
  91. data/lib/pg_query/json_field_names.rb +1402 -0
  92. data/lib/pg_query/node.rb +31 -0
  93. data/lib/pg_query/param_refs.rb +42 -37
  94. data/lib/pg_query/parse.rb +220 -200
  95. data/lib/pg_query/parse_error.rb +1 -1
  96. data/lib/pg_query/pg_query_pb.rb +3211 -0
  97. data/lib/pg_query/scan.rb +23 -0
  98. data/lib/pg_query/treewalker.rb +24 -40
  99. data/lib/pg_query/truncate.rb +64 -43
  100. data/lib/pg_query/version.rb +2 -2
  101. metadata +102 -11
  102. data/ext/pg_query/pg_query_ruby.h +0 -10
  103. data/lib/pg_query/deep_dup.rb +0 -16
  104. data/lib/pg_query/deparse/alter_table.rb +0 -42
  105. data/lib/pg_query/deparse/interval.rb +0 -105
  106. data/lib/pg_query/legacy_parsetree.rb +0 -109
  107. data/lib/pg_query/node_types.rb +0 -282
@@ -1,997 +1,22 @@
1
- require_relative 'deparse/interval'
2
- require_relative 'deparse/alter_table'
3
- class PgQuery
4
- # Reconstruct all of the parsed queries into their original form
5
- def deparse(tree = @tree)
6
- tree.map do |item|
7
- Deparse.from(item)
8
- end.join('; ')
9
- end
10
-
11
- # rubocop:disable Metrics/ModuleLength
12
- module Deparse
13
- extend self
14
-
15
- # Given one element of the PgQuery#parsetree reconstruct it back into the
16
- # original query.
17
- def from(item)
18
- deparse_item(item)
19
- end
20
-
21
- private
22
-
23
- def deparse_item(item, context = nil) # rubocop:disable Metrics/CyclomaticComplexity
24
- return if item.nil?
25
- return item if item.is_a?(Integer)
26
-
27
- type = item.keys[0]
28
- node = item.values[0]
29
-
30
- case type
31
- when A_EXPR
32
- case node['kind']
33
- when AEXPR_OP
34
- deparse_aexpr(node, context)
35
- when AEXPR_OP_ANY
36
- deparse_aexpr_any(node)
37
- when AEXPR_IN
38
- deparse_aexpr_in(node)
39
- when CONSTR_TYPE_FOREIGN
40
- deparse_aexpr_like(node)
41
- when AEXPR_BETWEEN, AEXPR_NOT_BETWEEN, AEXPR_BETWEEN_SYM, AEXPR_NOT_BETWEEN_SYM
42
- deparse_aexpr_between(node)
43
- when AEXPR_NULLIF
44
- deparse_aexpr_nullif(node)
45
- else
46
- raise format("Can't deparse: %s: %s", type, node.inspect)
47
- end
48
- when ALIAS
49
- deparse_alias(node)
50
- when ALTER_TABLE_STMT
51
- deparse_alter_table(node)
52
- when ALTER_TABLE_CMD
53
- deparse_alter_table_cmd(node)
54
- when A_ARRAY_EXPR
55
- deparse_a_arrayexp(node)
56
- when A_CONST
57
- deparse_a_const(node)
58
- when A_INDICES
59
- deparse_a_indices(node)
60
- when A_INDIRECTION
61
- deparse_a_indirection(node)
62
- when A_STAR
63
- deparse_a_star(node)
64
- when A_TRUNCATED
65
- '...' # pg_query internal
66
- when BOOL_EXPR
67
- case node['boolop']
68
- when BOOL_EXPR_AND
69
- deparse_bool_expr_and(node)
70
- when BOOL_EXPR_OR
71
- deparse_bool_expr_or(node)
72
- when BOOL_EXPR_NOT
73
- deparse_bool_expr_not(node)
74
- end
75
- when BOOLEAN_TEST
76
- deparse_boolean_test(node)
77
- when CASE_EXPR
78
- deparse_case(node)
79
- when COALESCE_EXPR
80
- deparse_coalesce(node)
81
- when COLUMN_DEF
82
- deparse_columndef(node)
83
- when COLUMN_REF
84
- deparse_columnref(node)
85
- when COMMON_TABLE_EXPR
86
- deparse_cte(node)
87
- when CONSTRAINT
88
- deparse_constraint(node)
89
- when CREATE_FUNCTION_STMT
90
- deparse_create_function(node)
91
- when CREATE_STMT
92
- deparse_create_table(node)
93
- when DEF_ELEM
94
- deparse_defelem(node)
95
- when DELETE_STMT
96
- deparse_delete_from(node)
97
- when DROP_STMT
98
- deparse_drop(node)
99
- when FUNC_CALL
100
- deparse_funccall(node)
101
- when FUNCTION_PARAMETER
102
- deparse_functionparameter(node)
103
- when INSERT_STMT
104
- deparse_insert_into(node)
105
- when JOIN_EXPR
106
- deparse_joinexpr(node)
107
- when LOCKING_CLAUSE
108
- deparse_lockingclause(node)
109
- when NULL_TEST
110
- deparse_nulltest(node)
111
- when PARAM_REF
112
- deparse_paramref(node)
113
- when RANGE_FUNCTION
114
- deparse_range_function(node)
115
- when RANGE_SUBSELECT
116
- deparse_rangesubselect(node)
117
- when RANGE_VAR
118
- deparse_rangevar(node)
119
- when RAW_STMT
120
- deparse_raw_stmt(node)
121
- when RENAME_STMT
122
- deparse_renamestmt(node)
123
- when RES_TARGET
124
- deparse_restarget(node, context)
125
- when ROW_EXPR
126
- deparse_row(node)
127
- when SELECT_STMT
128
- deparse_select(node)
129
- when SORT_BY
130
- deparse_sortby(node)
131
- when SUB_LINK
132
- deparse_sublink(node)
133
- when TRANSACTION_STMT
134
- deparse_transaction(node)
135
- when TYPE_CAST
136
- deparse_typecast(node)
137
- when TYPE_NAME
138
- deparse_typename(node)
139
- when UPDATE_STMT
140
- deparse_update(node)
141
- when CASE_WHEN
142
- deparse_when(node)
143
- when WINDOW_DEF
144
- deparse_windowdef(node)
145
- when WITH_CLAUSE
146
- deparse_with_clause(node)
147
- when VIEW_STMT
148
- deparse_viewstmt(node)
149
- when VARIABLE_SET_STMT
150
- deparse_variable_set_stmt(node)
151
- when STRING
152
- if context == A_CONST
153
- format("'%s'", node['str'].gsub("'", "''"))
154
- elsif [FUNC_CALL, TYPE_NAME, :operator, :defname_as].include?(context)
155
- node['str']
156
- else
157
- format('"%s"', node['str'].gsub('"', '""'))
158
- end
159
- when INTEGER
160
- node['ival'].to_s
161
- when FLOAT
162
- node['str']
163
- when NULL
164
- 'NULL'
165
- else
166
- raise format("Can't deparse: %s: %s", type, node.inspect)
167
- end
168
- end
169
-
170
- def deparse_item_list(nodes, context = nil)
171
- nodes.map { |n| deparse_item(n, context) }
172
- end
173
-
174
- def deparse_rangevar(node)
175
- output = []
176
- output << 'ONLY' unless node['inh']
177
- schema = node['schemaname'] ? '"' + node['schemaname'] + '".' : ''
178
- output << schema + '"' + node['relname'] + '"'
179
- output << deparse_item(node['alias']) if node['alias']
180
- output.join(' ')
181
- end
182
-
183
- def deparse_raw_stmt(node)
184
- deparse_item(node[STMT_FIELD])
185
- end
186
-
187
- def deparse_renamestmt(node)
188
- output = []
189
-
190
- case node['renameType']
191
- when OBJECT_TYPE_TABLE
192
- output << 'ALTER TABLE'
193
- output << deparse_item(node['relation'])
194
- output << 'RENAME TO'
195
- output << node['newname']
196
- else
197
- raise format("Can't deparse: %s", node.inspect)
198
- end
199
-
200
- output.join(' ')
201
- end
202
-
203
- def deparse_columnref(node)
204
- node['fields'].map do |field|
205
- field.is_a?(String) ? '"' + field + '"' : deparse_item(field)
206
- end.join('.')
207
- end
208
-
209
- def deparse_a_arrayexp(node)
210
- 'ARRAY[' + node['elements'].map do |element|
211
- deparse_item(element)
212
- end.join(', ') + ']'
213
- end
214
-
215
- def deparse_a_const(node)
216
- deparse_item(node['val'], A_CONST)
217
- end
218
-
219
- def deparse_a_star(_node)
220
- '*'
221
- end
222
-
223
- def deparse_a_indirection(node)
224
- output = [deparse_item(node['arg'])]
225
- node['indirection'].each do |subnode|
226
- output << deparse_item(subnode)
227
- end
228
- output.join
229
- end
230
-
231
- def deparse_a_indices(node)
232
- format('[%s]', deparse_item(node['uidx']))
233
- end
234
-
235
- def deparse_alias(node)
236
- name = node['aliasname']
237
- if node['colnames']
238
- name + '(' + deparse_item_list(node['colnames']).join(', ') + ')'
239
- else
240
- name
241
- end
242
- end
243
-
244
- def deparse_alter_table(node)
245
- output = []
246
- output << 'ALTER TABLE'
247
-
248
- output << deparse_item(node['relation'])
249
-
250
- output << node['cmds'].map do |item|
251
- deparse_item(item)
252
- end.join(', ')
253
-
254
- output.join(' ')
255
- end
256
-
257
- def deparse_alter_table_cmd(node)
258
- command, options = AlterTable.commands(node)
259
-
260
- output = []
261
- output << command if command
262
- output << 'IF EXISTS' if node['missing_ok']
263
- output << node['name']
264
- output << options if options
265
- output << deparse_item(node['def']) if node['def']
266
- output << 'CASCADE' if node['behavior'] == 1
267
-
268
- output.compact.join(' ')
269
- end
270
-
271
- def deparse_paramref(node)
272
- if node['number'].nil?
273
- '?'
274
- else
275
- format('$%d', node['number'])
276
- end
277
- end
278
-
279
- def deparse_restarget(node, context)
280
- if context == :select
281
- [deparse_item(node['val']), node['name']].compact.join(' AS ')
282
- elsif context == :update
283
- [node['name'], deparse_item(node['val'])].compact.join(' = ')
284
- elsif node['val'].nil?
285
- node['name']
286
- else
287
- raise format("Can't deparse %s in context %s", node.inspect, context)
288
- end
289
- end
290
-
291
- def deparse_funccall(node)
292
- output = []
293
-
294
- # SUM(a, b)
295
- args = Array(node['args']).map { |arg| deparse_item(arg) }
296
- # COUNT(*)
297
- args << '*' if node['agg_star']
298
-
299
- name = (node['funcname'].map { |n| deparse_item(n, FUNC_CALL) } - ['pg_catalog']).join('.')
300
- distinct = node['agg_distinct'] ? 'DISTINCT ' : ''
301
- output << format('%s(%s%s)', name, distinct, args.join(', '))
302
- output << format('OVER (%s)', deparse_item(node['over'])) if node['over']
303
-
304
- output.join(' ')
305
- end
306
-
307
- def deparse_windowdef(node)
308
- output = []
309
-
310
- if node['partitionClause']
311
- output << 'PARTITION BY'
312
- output << node['partitionClause'].map do |item|
313
- deparse_item(item)
314
- end.join(', ')
315
- end
316
-
317
- if node['orderClause']
318
- output << 'ORDER BY'
319
- output << node['orderClause'].map do |item|
320
- deparse_item(item)
321
- end.join(', ')
322
- end
323
-
324
- output.join(' ')
325
- end
326
-
327
- def deparse_functionparameter(node)
328
- deparse_item(node['argType'])
329
- end
330
-
331
- def deparse_aexpr_in(node)
332
- rexpr = Array(node['rexpr']).map { |arg| deparse_item(arg) }
333
- operator = node['name'].map { |n| deparse_item(n, :operator) } == ['='] ? 'IN' : 'NOT IN'
334
- format('%s %s (%s)', deparse_item(node['lexpr']), operator, rexpr.join(', '))
335
- end
336
-
337
- def deparse_aexpr_like(node)
338
- value = deparse_item(node['rexpr'])
339
- operator = node['name'].map { |n| deparse_item(n, :operator) } == ['~~'] ? 'LIKE' : 'NOT LIKE'
340
- format('%s %s %s', deparse_item(node['lexpr']), operator, value)
341
- end
342
-
343
- def deparse_bool_expr_not(node)
344
- format('NOT %s', deparse_item(node['args'][0]))
345
- end
346
-
347
- BOOLEAN_TEST_TYPE_TO_STRING = {
348
- BOOLEAN_TEST_TRUE => ' IS TRUE',
349
- BOOLEAN_TEST_NOT_TRUE => ' IS NOT TRUE',
350
- BOOLEAN_TEST_FALSE => ' IS FALSE',
351
- BOOLEAN_TEST_NOT_FALSE => ' IS NOT FALSE',
352
- BOOLEAN_TEST_UNKNOWN => ' IS UNKNOWN',
353
- BOOLEAN_TEST_NOT_UNKNOWN => ' IS NOT UNKNOWN'
354
- }.freeze
355
- def deparse_boolean_test(node)
356
- deparse_item(node['arg']) + BOOLEAN_TEST_TYPE_TO_STRING[node['booltesttype']]
357
- end
358
-
359
- def deparse_range_function(node)
360
- output = []
361
- output << 'LATERAL' if node['lateral']
362
- output << deparse_item(node['functions'][0][0]) # FIXME: Needs more test cases
363
- output << deparse_item(node['alias']) if node['alias']
364
- output << "#{node['alias'] ? '' : 'AS '}(#{deparse_item_list(node['coldeflist']).join(', ')})" if node['coldeflist']
365
- output.join(' ')
366
- end
367
-
368
- def deparse_aexpr(node, context = false)
369
- output = []
370
- output << deparse_item(node['lexpr'], context || true)
371
- output << deparse_item(node['rexpr'], context || true)
372
- output = output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
373
- if context
374
- # This is a nested expression, add parentheses.
375
- output = '(' + output + ')'
376
- end
377
- output
378
- end
379
-
380
- def deparse_bool_expr_and(node)
381
- # Only put parantheses around OR nodes that are inside this one
382
- node['args'].map do |arg|
383
- if [BOOL_EXPR_OR].include?(arg.values[0]['boolop'])
384
- format('(%s)', deparse_item(arg))
385
- else
386
- deparse_item(arg)
387
- end
388
- end.join(' AND ')
389
- end
390
-
391
- def deparse_bool_expr_or(node)
392
- # Put parantheses around AND + OR nodes that are inside
393
- node['args'].map do |arg|
394
- if [BOOL_EXPR_AND, BOOL_EXPR_OR].include?(arg.values[0]['boolop'])
395
- format('(%s)', deparse_item(arg))
396
- else
397
- deparse_item(arg)
398
- end
399
- end.join(' OR ')
400
- end
401
-
402
- def deparse_aexpr_any(node)
403
- output = []
404
- output << deparse_item(node['lexpr'])
405
- output << format('ANY(%s)', deparse_item(node['rexpr']))
406
- output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
407
- end
408
-
409
- def deparse_aexpr_between(node)
410
- between = case node['kind']
411
- when AEXPR_BETWEEN
412
- ' BETWEEN '
413
- when AEXPR_NOT_BETWEEN
414
- ' NOT BETWEEN '
415
- when AEXPR_BETWEEN_SYM
416
- ' BETWEEN SYMMETRIC '
417
- when AEXPR_NOT_BETWEEN_SYM
418
- ' NOT BETWEEN SYMMETRIC '
419
- end
420
- name = deparse_item(node['lexpr'])
421
- output = node['rexpr'].map { |n| deparse_item(n) }
422
- name << between << output.join(' AND ')
1
+ module PgQuery
2
+ class ParserResult
3
+ def deparse
4
+ PgQuery.deparse(@tree)
423
5
  end
6
+ end
424
7
 
425
- def deparse_aexpr_nullif(node)
426
- lexpr = deparse_item(node['lexpr'])
427
- rexpr = deparse_item(node['rexpr'])
428
- format('NULLIF(%s, %s)', lexpr, rexpr)
429
- end
430
-
431
- def deparse_joinexpr(node) # rubocop:disable Metrics/CyclomaticComplexity
432
- output = []
433
- output << deparse_item(node['larg'])
434
- case node['jointype']
435
- when 0
436
- if node['isNatural']
437
- output << 'NATURAL'
438
- elsif node['quals'].nil? && node['usingClause'].nil?
439
- output << 'CROSS'
440
- end
441
- when 1
442
- output << 'LEFT'
443
- when 2
444
- output << 'FULL'
445
- when 3
446
- output << 'RIGHT'
447
- end
448
- output << 'JOIN'
449
- output << deparse_item(node['rarg'])
450
-
451
- if node['quals']
452
- output << 'ON'
453
- output << deparse_item(node['quals'])
454
- end
455
-
456
- output << format('USING (%s)', node['usingClause'].map { |n| deparse_item(n) }.join(', ')) if node['usingClause']
457
-
458
- output.join(' ')
459
- end
460
-
461
- LOCK_CLAUSE_STRENGTH = {
462
- LCS_FORKEYSHARE => 'FOR KEY SHARE',
463
- LCS_FORSHARE => 'FOR SHARE',
464
- LCS_FORNOKEYUPDATE => 'FOR NO KEY UPDATE',
465
- LCS_FORUPDATE => 'FOR UPDATE'
466
- }.freeze
467
- def deparse_lockingclause(node)
468
- output = []
469
- output << LOCK_CLAUSE_STRENGTH[node['strength']]
470
- if node['lockedRels']
471
- output << 'OF'
472
- output << node['lockedRels'].map do |item|
473
- deparse_item(item)
474
- end.join(', ')
475
- end
476
- output.join(' ')
477
- end
478
-
479
- def deparse_sortby(node)
480
- output = []
481
- output << deparse_item(node['node'])
482
- output << 'ASC' if node['sortby_dir'] == 1
483
- output << 'DESC' if node['sortby_dir'] == 2
484
- output.join(' ')
485
- end
486
-
487
- def deparse_with_clause(node)
488
- output = ['WITH']
489
- output << 'RECURSIVE' if node['recursive']
490
- output << node['ctes'].map do |cte|
491
- deparse_item(cte)
492
- end.join(', ')
493
- output.join(' ')
494
- end
495
-
496
- def deparse_viewstmt(node)
497
- output = []
498
- output << 'CREATE'
499
- output << 'OR REPLACE' if node['replace']
500
-
501
- persistence = relpersistence(node['view'])
502
- output << persistence if persistence
503
-
504
- output << 'VIEW'
505
- output << node['view'][RANGE_VAR]['relname']
506
- output << format('(%s)', deparse_item_list(node['aliases']).join(', ')) if node['aliases']
507
-
508
- output << 'AS'
509
- output << deparse_item(node['query'])
510
-
511
- case node['withCheckOption']
512
- when 1
513
- output << 'WITH CHECK OPTION'
514
- when 2
515
- output << 'WITH CASCADED CHECK OPTION'
516
- end
517
- output.join(' ')
518
- end
519
-
520
- def deparse_variable_set_stmt(node)
521
- output = []
522
- output << 'SET'
523
- output << 'LOCAL' if node['is_local']
524
- output << node['name']
525
- output << 'TO'
526
- output << node['args'].map { |arg| deparse_item(arg) }.join(', ')
527
- output.join(' ')
528
- end
529
-
530
- def deparse_cte(node)
531
- output = []
532
- output << node['ctename']
533
- output << format('(%s)', node['aliascolnames'].map { |n| deparse_item(n) }.join(', ')) if node['aliascolnames']
534
- output << format('AS (%s)', deparse_item(node['ctequery']))
535
- output.join(' ')
536
- end
537
-
538
- def deparse_case(node)
539
- output = ['CASE']
540
- output << deparse_item(node['arg']) if node['arg']
541
- output += node['args'].map { |arg| deparse_item(arg) }
542
- if node['defresult']
543
- output << 'ELSE'
544
- output << deparse_item(node['defresult'])
545
- end
546
- output << 'END'
547
- output.join(' ')
548
- end
549
-
550
- def deparse_columndef(node)
551
- output = [node['colname']]
552
- output << deparse_item(node['typeName'])
553
- if node['raw_default']
554
- output << 'USING'
555
- output << deparse_item(node['raw_default'])
556
- end
557
- if node['constraints']
558
- output += node['constraints'].map do |item|
559
- deparse_item(item)
560
- end
561
- end
562
- output.compact.join(' ')
563
- end
564
-
565
- def deparse_constraint(node) # rubocop:disable Metrics/CyclomaticComplexity
566
- output = []
567
- if node['conname']
568
- output << 'CONSTRAINT'
569
- output << node['conname']
570
- end
571
- case node['contype']
572
- when CONSTR_TYPE_NULL
573
- output << 'NULL'
574
- when CONSTR_TYPE_NOTNULL
575
- output << 'NOT NULL'
576
- when CONSTR_TYPE_DEFAULT
577
- output << 'DEFAULT'
578
- when CONSTR_TYPE_CHECK
579
- output << 'CHECK'
580
- when CONSTR_TYPE_PRIMARY
581
- output << 'PRIMARY KEY'
582
- when CONSTR_TYPE_UNIQUE
583
- output << 'UNIQUE'
584
- when CONSTR_TYPE_EXCLUSION
585
- output << 'EXCLUSION'
586
- when CONSTR_TYPE_FOREIGN
587
- output << 'FOREIGN KEY'
588
- end
589
-
590
- if node['raw_expr']
591
- expression = deparse_item(node['raw_expr'])
592
- # Unless it's simple, put parentheses around it
593
- expression = '(' + expression + ')' if node['raw_expr'][A_EXPR] && node['raw_expr'][A_EXPR]['kind'] == AEXPR_OP
594
- output << expression
595
- end
596
- output << '(' + deparse_item_list(node['keys']).join(', ') + ')' if node['keys']
597
- output << '(' + deparse_item_list(node['fk_attrs']).join(', ') + ')' if node['fk_attrs']
598
- output << 'REFERENCES ' + deparse_item(node['pktable']) + ' (' + deparse_item_list(node['pk_attrs']).join(', ') + ')' if node['pktable']
599
- output << 'NOT VALID' if node['skip_validation']
600
- output << "USING INDEX #{node['indexname']}" if node['indexname']
601
- output.join(' ')
602
- end
603
-
604
- def deparse_create_function(node)
605
- output = []
606
- output << 'CREATE'
607
- output << 'OR REPLACE' if node['replace']
608
- output << 'FUNCTION'
609
-
610
- arguments = deparse_item_list(node['parameters']).join(', ')
611
-
612
- output << deparse_item_list(node['funcname']).join('.') + '(' + arguments + ')'
613
-
614
- output << 'RETURNS'
615
- output << deparse_item(node['returnType'])
616
- output += node['options'].map { |item| deparse_item(item) }
617
-
618
- output.join(' ')
619
- end
620
-
621
- def deparse_create_table(node)
622
- output = []
623
- output << 'CREATE'
624
-
625
- persistence = relpersistence(node['relation'])
626
- output << persistence if persistence
627
-
628
- output << 'TABLE'
629
-
630
- output << 'IF NOT EXISTS' if node['if_not_exists']
631
-
632
- output << deparse_item(node['relation'])
633
-
634
- output << '(' + node['tableElts'].map do |item|
635
- deparse_item(item)
636
- end.join(', ') + ')'
637
-
638
- if node['inhRelations']
639
- output << 'INHERITS'
640
- output << '(' + node['inhRelations'].map do |relation|
641
- deparse_item(relation)
642
- end.join(', ') + ')'
643
- end
644
-
645
- output.join(' ')
646
- end
647
-
648
- def deparse_when(node)
649
- output = ['WHEN']
650
- output << deparse_item(node['expr'])
651
- output << 'THEN'
652
- output << deparse_item(node['result'])
653
- output.join(' ')
654
- end
655
-
656
- def deparse_sublink(node)
657
- if node['subLinkType'] == SUBLINK_TYPE_ANY
658
- format('%s IN (%s)', deparse_item(node['testexpr']), deparse_item(node['subselect']))
659
- elsif node['subLinkType'] == SUBLINK_TYPE_EXISTS
660
- format('EXISTS(%s)', deparse_item(node['subselect']))
661
- else
662
- format('(%s)', deparse_item(node['subselect']))
663
- end
664
- end
665
-
666
- def deparse_rangesubselect(node)
667
- output = '(' + deparse_item(node['subquery']) + ')'
668
- if node['alias']
669
- output + ' ' + deparse_item(node['alias'])
670
- else
671
- output
672
- end
673
- end
674
-
675
- def deparse_row(node)
676
- 'ROW(' + node['args'].map { |arg| deparse_item(arg) }.join(', ') + ')'
677
- end
678
-
679
- def deparse_select(node) # rubocop:disable Metrics/CyclomaticComplexity
680
- output = []
681
-
682
- if node['op'] == 1
683
- output << deparse_item(node['larg'])
684
- output << 'UNION'
685
- output << 'ALL' if node['all']
686
- output << deparse_item(node['rarg'])
687
- return output.join(' ')
688
- end
689
-
690
- output << deparse_item(node['withClause']) if node['withClause']
691
-
692
- if node[TARGET_LIST_FIELD]
693
- output << 'SELECT'
694
- output << 'DISTINCT' if node['distinctClause']
695
- output << node[TARGET_LIST_FIELD].map do |item|
696
- deparse_item(item, :select)
697
- end.join(', ')
698
- end
699
-
700
- if node[FROM_CLAUSE_FIELD]
701
- output << 'FROM'
702
- output << node[FROM_CLAUSE_FIELD].map do |item|
703
- deparse_item(item)
704
- end.join(', ')
705
- end
706
-
707
- if node['whereClause']
708
- output << 'WHERE'
709
- output << deparse_item(node['whereClause'])
710
- end
711
-
712
- if node['valuesLists']
713
- output << 'VALUES'
714
- output << node['valuesLists'].map do |value_list|
715
- '(' + value_list.map { |v| deparse_item(v) }.join(', ') + ')'
716
- end.join(', ')
717
- end
718
-
719
- if node['groupClause']
720
- output << 'GROUP BY'
721
- output << node['groupClause'].map do |item|
722
- deparse_item(item)
723
- end.join(', ')
724
- end
725
-
726
- if node['havingClause']
727
- output << 'HAVING'
728
- output << deparse_item(node['havingClause'])
729
- end
730
-
731
- if node['sortClause']
732
- output << 'ORDER BY'
733
- output << node['sortClause'].map do |item|
734
- deparse_item(item)
735
- end.join(', ')
736
- end
737
-
738
- if node['limitCount']
739
- output << 'LIMIT'
740
- output << deparse_item(node['limitCount'])
741
- end
742
-
743
- if node['limitOffset']
744
- output << 'OFFSET'
745
- output << deparse_item(node['limitOffset'])
746
- end
747
-
748
- if node['lockingClause']
749
- node['lockingClause'].map do |item|
750
- output << deparse_item(item)
751
- end
752
- end
753
-
754
- output.join(' ')
755
- end
756
-
757
- def deparse_insert_into(node)
758
- output = []
759
- output << deparse_item(node['withClause']) if node['withClause']
760
-
761
- output << 'INSERT INTO'
762
- output << deparse_item(node['relation'])
763
-
764
- if node['cols']
765
- output << '(' + node['cols'].map do |column|
766
- deparse_item(column)
767
- end.join(', ') + ')'
768
- end
769
-
770
- output << deparse_item(node['selectStmt'])
771
-
772
- output.join(' ')
773
- end
774
-
775
- def deparse_update(node)
776
- output = []
777
- output << deparse_item(node['withClause']) if node['withClause']
778
-
779
- output << 'UPDATE'
780
- output << deparse_item(node['relation'])
781
-
782
- if node[TARGET_LIST_FIELD]
783
- output << 'SET'
784
- node[TARGET_LIST_FIELD].each do |item|
785
- output << deparse_item(item, :update)
786
- end
787
- end
788
-
789
- if node['whereClause']
790
- output << 'WHERE'
791
- output << deparse_item(node['whereClause'])
792
- end
793
-
794
- if node['returningList']
795
- output << 'RETURNING'
796
- output << node['returningList'].map do |item|
797
- # RETURNING is formatted like a SELECT
798
- deparse_item(item, :select)
799
- end.join(', ')
800
- end
801
-
802
- output.join(' ')
803
- end
804
-
805
- def deparse_typecast(node)
806
- if deparse_item(node['typeName']) == 'boolean'
807
- deparse_item(node['arg']) == "'t'" ? 'true' : 'false'
808
- else
809
- deparse_item(node['arg']) + '::' + deparse_typename(node['typeName'][TYPE_NAME])
810
- end
811
- end
812
-
813
- def deparse_typename(node)
814
- names = node['names'].map { |n| deparse_item(n, TYPE_NAME) }
815
-
816
- # Intervals are tricky and should be handled in a separate method because
817
- # they require performing some bitmask operations.
818
- return deparse_interval_type(node) if names == %w[pg_catalog interval]
819
-
820
- output = []
821
- output << 'SETOF' if node['setof']
822
-
823
- if node['typmods']
824
- arguments = node['typmods'].map do |item|
825
- deparse_item(item)
826
- end.join(', ')
827
- end
828
- output << deparse_typename_cast(names, arguments)
829
- output.last << '[]' if node['arrayBounds']
830
-
831
- output.join(' ')
832
- end
833
-
834
- def deparse_typename_cast(names, arguments) # rubocop:disable Metrics/CyclomaticComplexity
835
- catalog, type = names
836
- # Just pass along any custom types.
837
- # (The pg_catalog types are built-in Postgres system types and are
838
- # handled in the case statement below)
839
- return names.join('.') if catalog != 'pg_catalog'
840
-
841
- case type
842
- when 'bpchar'
843
- # char(2) or char(9)
844
- "char(#{arguments})"
845
- when 'varchar'
846
- arguments.nil? ? 'varchar' : "varchar(#{arguments})"
847
- when 'numeric'
848
- # numeric(3, 5)
849
- arguments.nil? ? 'numeric' : "numeric(#{arguments})"
850
- when 'bool'
851
- 'boolean'
852
- when 'int2'
853
- 'smallint'
854
- when 'int4'
855
- 'int'
856
- when 'int8'
857
- 'bigint'
858
- when 'real', 'float4'
859
- 'real'
860
- when 'float8'
861
- 'double'
862
- when 'time'
863
- 'time'
864
- when 'timetz'
865
- 'time with time zone'
866
- when 'timestamp'
867
- 'timestamp'
868
- when 'timestamptz'
869
- 'timestamp with time zone'
870
- else
871
- raise format("Can't deparse type: %s", type)
872
- end
873
- end
874
-
875
- # Deparses interval type expressions like `interval year to month` or
876
- # `interval hour to second(5)`
877
- def deparse_interval_type(node)
878
- type = ['interval']
879
-
880
- if node['typmods']
881
- typmods = node['typmods'].map { |typmod| deparse_item(typmod) }
882
- type << Interval.from_int(typmods.first.to_i).map do |part|
883
- # only the `second` type can take an argument.
884
- if part == 'second' && typmods.size == 2
885
- "second(#{typmods.last})"
886
- else
887
- part
888
- end.downcase
889
- end.join(' to ')
890
- end
891
-
892
- type.join(' ')
893
- end
894
-
895
- def deparse_nulltest(node)
896
- output = [deparse_item(node['arg'])]
897
- case node['nulltesttype']
898
- when 0
899
- output << 'IS NULL'
900
- when 1
901
- output << 'IS NOT NULL'
902
- end
903
- output.join(' ')
904
- end
905
-
906
- TRANSACTION_CMDS = {
907
- TRANS_STMT_BEGIN => 'BEGIN',
908
- TRANS_STMT_COMMIT => 'COMMIT',
909
- TRANS_STMT_ROLLBACK => 'ROLLBACK',
910
- TRANS_STMT_SAVEPOINT => 'SAVEPOINT',
911
- TRANS_STMT_RELEASE => 'RELEASE',
912
- TRANS_STMT_ROLLBACK_TO => 'ROLLBACK TO SAVEPOINT'
913
- }.freeze
914
- def deparse_transaction(node)
915
- output = []
916
- output << TRANSACTION_CMDS[node['kind']] || raise(format("Can't deparse TRANSACTION %s", node.inspect))
917
-
918
- if node['options']
919
- output += node['options'].map { |item| deparse_item(item) }
920
- end
921
-
922
- output.join(' ')
923
- end
924
-
925
- def deparse_coalesce(node)
926
- format('COALESCE(%s)', node['args'].map { |a| deparse_item(a) }.join(', '))
927
- end
928
-
929
- def deparse_defelem(node)
930
- case node['defname']
931
- when 'as'
932
- "AS $$#{deparse_item_list(node['arg'], :defname_as).join("\n")}$$"
933
- when 'language'
934
- "language #{deparse_item(node['arg'])}"
935
- when 'volatility'
936
- node['arg']['String']['str'].upcase # volatility does not need to be quoted
937
- when 'strict'
938
- deparse_item(node['arg']) == '1' ? 'RETURNS NULL ON NULL INPUT' : 'CALLED ON NULL INPUT'
939
- else
940
- deparse_item(node['arg'])
941
- end
942
- end
943
-
944
- def deparse_delete_from(node)
945
- output = []
946
- output << deparse_item(node['withClause']) if node['withClause']
947
-
948
- output << 'DELETE FROM'
949
- output << deparse_item(node['relation'])
950
-
951
- if node['usingClause']
952
- output << 'USING'
953
- output << node['usingClause'].map do |item|
954
- deparse_item(item)
955
- end.join(', ')
956
- end
957
-
958
- if node['whereClause']
959
- output << 'WHERE'
960
- output << deparse_item(node['whereClause'])
961
- end
962
-
963
- if node['returningList']
964
- output << 'RETURNING'
965
- output << node['returningList'].map do |item|
966
- # RETURNING is formatted like a SELECT
967
- deparse_item(item, :select)
968
- end.join(', ')
969
- end
970
-
971
- output.join(' ')
972
- end
973
-
974
- def deparse_drop(node)
975
- output = ['DROP']
976
- output << 'TABLE' if node['removeType'] == OBJECT_TYPE_TABLE
977
- output << 'CONCURRENTLY' if node['concurrent']
978
- output << 'IF EXISTS' if node['missing_ok']
979
-
980
- output << node['objects'].map { |list| list.map { |object| deparse_item(object) } }.join(', ')
981
-
982
- output << 'CASCADE' if node['behavior'] == 1
8
+ # Reconstruct all of the parsed queries into their original form
9
+ def self.deparse(tree)
10
+ PgQuery.deparse_protobuf(PgQuery::ParseResult.encode(tree)).force_encoding('UTF-8')
11
+ end
983
12
 
984
- output.join(' ')
985
- end
13
+ # Convenience method for deparsing a statement of a specific type
14
+ def self.deparse_stmt(stmt)
15
+ deparse(PgQuery::ParseResult.new(version: PG_VERSION_NUM, stmts: [PgQuery::RawStmt.new(stmt: PgQuery::Node.from(stmt))]))
16
+ end
986
17
 
987
- # The PG parser adds several pieces of view data onto the RANGEVAR
988
- # that need to be printed before deparse_rangevar is called.
989
- def relpersistence(rangevar)
990
- if rangevar[RANGE_VAR]['relpersistence'] == 't'
991
- 'TEMPORARY'
992
- elsif rangevar[RANGE_VAR]['relpersistence'] == 'u'
993
- 'UNLOGGED'
994
- end
995
- end
18
+ # Convenience method for deparsing an expression
19
+ def self.deparse_expr(expr)
20
+ deparse_stmt(PgQuery::SelectStmt.new(where_clause: expr, op: :SETOP_NONE)).gsub('SELECT WHERE ', '')
996
21
  end
997
22
  end