pg_query 1.3.0 → 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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -52
  3. data/README.md +72 -65
  4. data/Rakefile +82 -1
  5. data/ext/pg_query/extconf.rb +2 -39
  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 +15 -1673
  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 -203
  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 +101 -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/deparse/keywords.rb +0 -159
  107. data/lib/pg_query/deparse/rename.rb +0 -41
  108. data/lib/pg_query/legacy_parsetree.rb +0 -109
  109. data/lib/pg_query/node_types.rb +0 -297
@@ -1,1680 +1,22 @@
1
- require_relative 'deparse/alter_table'
2
- require_relative 'deparse/rename'
3
- require_relative 'deparse/interval'
4
- require_relative 'deparse/keywords'
1
+ module PgQuery
2
+ class ParserResult
3
+ def deparse
4
+ PgQuery.deparse(@tree)
5
+ end
6
+ end
5
7
 
6
- class PgQuery
7
8
  # Reconstruct all of the parsed queries into their original form
8
- def deparse(tree = @tree)
9
- tree.map do |item|
10
- Deparse.from(item)
11
- end.join('; ')
9
+ def self.deparse(tree)
10
+ PgQuery.deparse_protobuf(PgQuery::ParseResult.encode(tree)).force_encoding('UTF-8')
12
11
  end
13
12
 
14
- # rubocop:disable Metrics/ModuleLength
15
- module Deparse
16
- extend self
17
-
18
- # Given one element of the PgQuery#parsetree reconstruct it back into the
19
- # original query.
20
- def from(item)
21
- deparse_item(item)
22
- end
23
-
24
- private
25
-
26
- def deparse_item(item, context = nil) # rubocop:disable Metrics/CyclomaticComplexity
27
- return if item.nil?
28
- return item if item.is_a?(Integer)
29
-
30
- type = item.keys[0]
31
- node = item.values[0]
32
-
33
- case type
34
- when A_EXPR
35
- case node['kind']
36
- when AEXPR_OP
37
- deparse_aexpr(node, context)
38
- when AEXPR_OP_ALL
39
- deparse_aexpr_all(node)
40
- when AEXPR_OP_ANY
41
- deparse_aexpr_any(node)
42
- when AEXPR_IN
43
- deparse_aexpr_in(node)
44
- when AEXPR_ILIKE
45
- deparse_aexpr_ilike(node)
46
- when CONSTR_TYPE_FOREIGN
47
- deparse_aexpr_like(node)
48
- when AEXPR_BETWEEN, AEXPR_NOT_BETWEEN, AEXPR_BETWEEN_SYM, AEXPR_NOT_BETWEEN_SYM
49
- deparse_aexpr_between(node)
50
- when AEXPR_NULLIF
51
- deparse_aexpr_nullif(node)
52
- else
53
- raise format("Can't deparse: %s: %s", type, node.inspect)
54
- end
55
- when ACCESS_PRIV
56
- deparse_access_priv(node)
57
- when ALIAS
58
- deparse_alias(node)
59
- when ALTER_TABLE_STMT
60
- deparse_alter_table(node)
61
- when ALTER_TABLE_CMD
62
- deparse_alter_table_cmd(node)
63
- when A_ARRAY_EXPR
64
- deparse_a_arrayexp(node)
65
- when A_CONST
66
- deparse_a_const(node)
67
- when A_INDICES
68
- deparse_a_indices(node)
69
- when A_INDIRECTION
70
- deparse_a_indirection(node)
71
- when A_STAR
72
- deparse_a_star(node)
73
- when A_TRUNCATED
74
- '...' # pg_query internal
75
- when BOOL_EXPR
76
- case node['boolop']
77
- when BOOL_EXPR_AND
78
- deparse_bool_expr_and(node)
79
- when BOOL_EXPR_OR
80
- deparse_bool_expr_or(node)
81
- when BOOL_EXPR_NOT
82
- deparse_bool_expr_not(node)
83
- end
84
- when BOOLEAN_TEST
85
- deparse_boolean_test(node)
86
- when CASE_EXPR
87
- deparse_case(node)
88
- when COALESCE_EXPR
89
- deparse_coalesce(node)
90
- when COLLATE_CLAUSE
91
- deparse_collate(node)
92
- when COLUMN_DEF
93
- deparse_columndef(node)
94
- when COLUMN_REF
95
- deparse_columnref(node, context)
96
- when COMMON_TABLE_EXPR
97
- deparse_cte(node)
98
- when COMPOSITE_TYPE_STMT
99
- deparse_composite_type(node)
100
- when CONSTRAINT
101
- deparse_constraint(node)
102
- when COPY_STMT
103
- deparse_copy(node)
104
- when CREATE_CAST_STMT
105
- deparse_create_cast(node)
106
- when CREATE_DOMAIN_STMT
107
- deparse_create_domain(node)
108
- when CREATE_ENUM_STMT
109
- deparse_create_enum(node)
110
- when CREATE_FUNCTION_STMT
111
- deparse_create_function(node)
112
- when CREATE_RANGE_STMT
113
- deparse_create_range(node)
114
- when CREATE_SCHEMA_STMT
115
- deparse_create_schema(node)
116
- when CREATE_STMT
117
- deparse_create_table(node)
118
- when CREATE_TABLE_AS_STMT
119
- deparse_create_table_as(node)
120
- when INTO_CLAUSE
121
- deparse_into_clause(node)
122
- when DEF_ELEM
123
- deparse_defelem(node)
124
- when DEFINE_STMT
125
- deparse_define_stmt(node)
126
- when DELETE_STMT
127
- deparse_delete_from(node)
128
- when DISCARD_STMT
129
- deparse_discard(node)
130
- when DROP_ROLE
131
- deparse_drop_role(node)
132
- when DROP_STMT
133
- deparse_drop(node)
134
- when DROP_SUBSCRIPTION
135
- deparse_drop_subscription(node)
136
- when DROP_TABLESPACE
137
- deparse_drop_tablespace(node)
138
- when EXPLAIN_STMT
139
- deparse_explain(node)
140
- when EXECUTE_STMT
141
- deparse_execute(node)
142
- when FUNC_CALL
143
- deparse_funccall(node)
144
- when FUNCTION_PARAMETER
145
- deparse_functionparameter(node)
146
- when GRANT_ROLE_STMT
147
- deparse_grant_role(node)
148
- when GRANT_STMT
149
- deparse_grant(node)
150
- when INSERT_STMT
151
- deparse_insert_into(node)
152
- when JOIN_EXPR
153
- deparse_joinexpr(node)
154
- when LOCK_STMT
155
- deparse_lock(node)
156
- when LOCKING_CLAUSE
157
- deparse_lockingclause(node)
158
- when NULL_TEST
159
- deparse_nulltest(node)
160
- when OBJECT_WITH_ARGS
161
- deparse_object_with_args(node)
162
- when PARAM_REF
163
- deparse_paramref(node)
164
- when PREPARE_STMT
165
- deparse_prepare(node)
166
- when RANGE_FUNCTION
167
- deparse_range_function(node)
168
- when RANGE_SUBSELECT
169
- deparse_rangesubselect(node)
170
- when RANGE_VAR
171
- deparse_rangevar(node)
172
- when RAW_STMT
173
- deparse_raw_stmt(node)
174
- when RENAME_STMT
175
- deparse_renamestmt(node)
176
- when RES_TARGET
177
- deparse_restarget(node, context)
178
- when ROLE_SPEC
179
- deparse_role_spec(node)
180
- when ROW_EXPR
181
- deparse_row(node)
182
- when SELECT_STMT
183
- deparse_select(node)
184
- when SQL_VALUE_FUNCTION
185
- deparse_sql_value_function(node)
186
- when SORT_BY
187
- deparse_sortby(node)
188
- when SUB_LINK
189
- deparse_sublink(node)
190
- when TRANSACTION_STMT
191
- deparse_transaction(node)
192
- when TYPE_CAST
193
- deparse_typecast(node)
194
- when TYPE_NAME
195
- deparse_typename(node)
196
- when UPDATE_STMT
197
- deparse_update(node)
198
- when CASE_WHEN
199
- deparse_when(node)
200
- when WINDOW_DEF
201
- deparse_windowdef(node)
202
- when WITH_CLAUSE
203
- deparse_with_clause(node)
204
- when VIEW_STMT
205
- deparse_viewstmt(node)
206
- when VARIABLE_SET_STMT
207
- deparse_variable_set_stmt(node)
208
- when VACUUM_STMT
209
- deparse_vacuum_stmt(node)
210
- when DO_STMT
211
- deparse_do_stmt(node)
212
- when SET_TO_DEFAULT
213
- 'DEFAULT'
214
- when STRING
215
- if context == A_CONST
216
- format("'%s'", node['str'].gsub("'", "''"))
217
- elsif [FUNC_CALL, TYPE_NAME, :operator, :defname_as].include?(context)
218
- node['str']
219
- elsif context == :excluded
220
- node['str'].casecmp('EXCLUDED').zero? ? node['str'].upcase : deparse_identifier(node['str'], escape_always: true)
221
- else
222
- deparse_identifier(node['str'], escape_always: true)
223
- end
224
- when INTEGER
225
- node['ival'].to_s
226
- when FLOAT
227
- node['str']
228
- when NULL
229
- 'NULL'
230
- else
231
- raise format("Can't deparse: %s: %s", type, node.inspect)
232
- end
233
- end
234
-
235
- def deparse_item_list(nodes, context = nil)
236
- nodes.map { |n| deparse_item(n, context) }
237
- end
238
-
239
- def deparse_identifier(ident, escape_always: false)
240
- return if ident.nil?
241
- if escape_always || !ident[/^\w+$/] || KEYWORDS.include?(ident.upcase)
242
- format('"%s"', ident.gsub('"', '""'))
243
- else
244
- ident
245
- end
246
- end
247
-
248
- def deparse_rangevar(node)
249
- output = []
250
- output << 'ONLY' unless node['inh']
251
- schema = node['schemaname'] ? '"' + node['schemaname'] + '".' : ''
252
- output << schema + '"' + node['relname'] + '"'
253
- output << deparse_item(node['alias']) if node['alias']
254
- output.join(' ')
255
- end
256
-
257
- def deparse_raw_stmt(node)
258
- deparse_item(node[STMT_FIELD])
259
- end
260
-
261
- def deparse_renamestmt_decision(node, type)
262
- if node[type]
263
- if node[type].is_a?(String)
264
- deparse_identifier(node[type])
265
- elsif node[type].is_a?(Array)
266
- deparse_item_list(node[type])
267
- elsif node[type].is_a?(Object)
268
- deparse_item(node[type])
269
- end
270
- else
271
- type
272
- end
273
- end
274
-
275
- def deparse_renamestmt(node)
276
- output = []
277
- output << 'ALTER'
278
- command, type, options, type2, options2 = Rename.commands(node)
279
-
280
- output << command if command
281
- output << deparse_renamestmt_decision(node, type)
282
- output << options if options
283
- output << deparse_renamestmt_decision(node, type2) if type2
284
- output << deparse_renamestmt_decision(node, options2) if options2
285
- output << 'TO'
286
- output << deparse_identifier(node['newname'], escape_always: true)
287
- output.join(' ')
288
- end
289
-
290
- def deparse_columnref(node, context = false)
291
- node['fields'].map do |field|
292
- field.is_a?(String) ? '"' + field + '"' : deparse_item(field, context)
293
- end.join('.')
294
- end
295
-
296
- def deparse_a_arrayexp(node)
297
- 'ARRAY[' + (node['elements'] || []).map do |element|
298
- deparse_item(element)
299
- end.join(', ') + ']'
300
- end
301
-
302
- def deparse_a_const(node)
303
- deparse_item(node['val'], A_CONST)
304
- end
305
-
306
- def deparse_a_star(_node)
307
- '*'
308
- end
309
-
310
- def deparse_a_indirection(node)
311
- output = []
312
- arg = deparse_item(node['arg'])
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?)
318
- "(#{arg})."
319
- else
320
- arg
321
- end
322
- node['indirection'].each do |subnode|
323
- output << deparse_item(subnode)
324
- end
325
- output.join
326
- end
327
-
328
- def deparse_a_indices(node)
329
- format('[%s]', deparse_item(node['uidx']))
330
- end
331
-
332
- def deparse_alias(node)
333
- name = node['aliasname']
334
- if node['colnames']
335
- name + '(' + deparse_item_list(node['colnames']).join(', ') + ')'
336
- else
337
- deparse_identifier(name)
338
- end
339
- end
340
-
341
- def deparse_alter_table(node)
342
- output = []
343
- output << 'ALTER'
344
-
345
- output << 'TABLE' if node['relkind'] == OBJECT_TYPE_TABLE
346
- output << 'VIEW' if node['relkind'] == OBJECT_TYPE_VIEW
347
-
348
- output << deparse_item(node['relation'])
349
-
350
- output << node['cmds'].map do |item|
351
- deparse_item(item)
352
- end.join(', ')
353
-
354
- output.join(' ')
355
- end
356
-
357
- def deparse_alter_table_cmd(node)
358
- command, options = AlterTable.commands(node)
359
-
360
- output = []
361
- output << command if command
362
- output << 'IF EXISTS' if node['missing_ok']
363
- output << node['name']
364
- output << options if options
365
- output << deparse_item(node['def']) if node['def']
366
- output << 'CASCADE' if node['behavior'] == 1
367
-
368
- output.compact.join(' ')
369
- end
370
-
371
- def deparse_object_with_args(node)
372
- output = []
373
- output << deparse_item_list(node['objname']).join('.')
374
- unless node['args_unspecified']
375
- args = node.fetch('objargs', []).map(&method(:deparse_item)).join(', ')
376
- output << "(#{args})"
377
- end
378
- output.join('')
379
- end
380
-
381
- def deparse_paramref(node)
382
- if node['number'].nil?
383
- '?'
384
- else
385
- format('$%d', node['number'])
386
- end
387
- end
388
-
389
- def deparse_prepare(node)
390
- output = ['PREPARE']
391
- output << deparse_identifier(node['name'])
392
- output << "(#{deparse_item_list(node['argtypes']).join(', ')})" if node['argtypes']
393
- output << 'AS'
394
- output << deparse_item(node['query'])
395
- output.join(' ')
396
- end
397
-
398
- def deparse_restarget(node, context)
399
- if context == :select
400
- [deparse_item(node['val']), deparse_identifier(node['name'])].compact.join(' AS ')
401
- elsif context == :update
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(' = ')
405
- elsif node['val'].nil?
406
- node['name']
407
- else
408
- raise format("Can't deparse %s in context %s", node.inspect, context)
409
- end
410
- end
411
-
412
- def deparse_funccall(node)
413
- output = []
414
-
415
- # SUM(a, b)
416
- args = Array(node['args']).map { |arg| deparse_item(arg) }
417
- # COUNT(*)
418
- args << '*' if node['agg_star']
419
-
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
431
-
432
- output.join(' ')
433
- end
434
-
435
- def deparse_windowdef(node)
436
- return deparse_identifier(node['name']) if node['name']
437
-
438
- output = []
439
-
440
- if node['partitionClause']
441
- output << 'PARTITION BY'
442
- output << node['partitionClause'].map do |item|
443
- deparse_item(item)
444
- end.join(', ')
445
- end
446
-
447
- if node['orderClause']
448
- output << 'ORDER BY'
449
- output << node['orderClause'].map do |item|
450
- deparse_item(item)
451
- end.join(', ')
452
- end
453
-
454
- format('(%s)', output.join(' '))
455
- end
456
-
457
- def deparse_functionparameter(node)
458
- deparse_item(node['argType'])
459
- end
460
-
461
- def deparse_grant_role(node)
462
- output = []
463
- output << ['GRANT'] if node['is_grant']
464
- output << ['REVOKE'] unless node['is_grant']
465
- output << node['granted_roles'].map(&method(:deparse_item)).join(', ')
466
- output << ['TO'] if node['is_grant']
467
- output << ['FROM'] unless node['is_grant']
468
- output << node['grantee_roles'].map(&method(:deparse_item)).join(', ')
469
- output << 'WITH ADMIN OPTION' if node['admin_opt']
470
- output.join(' ')
471
- end
472
-
473
- def deparse_grant(node) # rubocop:disable Metrics/CyclomaticComplexity
474
- objtype, allow_all = deparse_grant_objtype(node)
475
- output = []
476
- output << ['GRANT'] if node['is_grant']
477
- output << ['REVOKE'] unless node['is_grant']
478
- output << if node.key?('privileges')
479
- node['privileges'].map(&method(:deparse_item)).join(', ')
480
- else
481
- 'ALL'
482
- end
483
- output << 'ON'
484
- objects = node['objects']
485
- objects = objects[0] if %w[DOMAIN TYPE].include?(objtype)
486
- objects = objects.map do |object|
487
- deparsed = deparse_item(object)
488
- if object.key?(RANGE_VAR) || object.key?(OBJECT_WITH_ARGS) || !allow_all
489
- objtype == 'TABLE' ? deparsed : "#{objtype} #{deparsed}"
490
- else
491
- "ALL #{objtype}S IN SCHEMA #{deparsed}"
492
- end
493
- end
494
- output << objects.join(', ')
495
- output << ['TO'] if node['is_grant']
496
- output << ['FROM'] unless node['is_grant']
497
- output << node['grantees'].map(&method(:deparse_item)).join(', ')
498
- output << 'WITH GRANT OPTION' if node['grant_option']
499
- output.join(' ')
500
- end
501
-
502
- def deparse_grant_objtype(node)
503
- {
504
- 1 => ['TABLE', true],
505
- 2 => ['SEQUENCE', true],
506
- 3 => ['DATABASE', false],
507
- 4 => ['DOMAIN', false],
508
- 5 => ['FOREIGN DATA WRAPPER', false],
509
- 6 => ['FOREIGN SERVER', false],
510
- 7 => ['FUNCTION', true],
511
- 8 => ['LANGUAGE', false],
512
- 9 => ['LARGE OBJECT', false],
513
- 10 => ['SCHEMA', false],
514
- 11 => ['TABLESPACE', false],
515
- 12 => ['TYPE', false]
516
- }.fetch(node['objtype'])
517
- end
518
-
519
- def deparse_access_priv(node)
520
- output = [node['priv_name']]
521
- output << "(#{node['cols'].map(&method(:deparse_item)).join(', ')})" if node.key?('cols')
522
- output.join(' ')
523
- end
524
-
525
- def deparse_role_spec(node)
526
- return 'CURRENT_USER' if node['roletype'] == 1
527
- return 'SESSION_USER' if node['roletype'] == 2
528
- return 'PUBLIC' if node['roletype'] == 3
529
- deparse_identifier(node['rolename'], escape_always: true)
530
- end
531
-
532
- def deparse_aexpr_in(node)
533
- rexpr = Array(node['rexpr']).map { |arg| deparse_item(arg) }
534
- operator = node['name'].map { |n| deparse_item(n, :operator) } == ['='] ? 'IN' : 'NOT IN'
535
- format('%s %s (%s)', deparse_item(node['lexpr']), operator, rexpr.join(', '))
536
- end
537
-
538
- def deparse_aexpr_like(node)
539
- value = deparse_item(node['rexpr'])
540
- operator = node['name'].map { |n| deparse_item(n, :operator) } == ['~~'] ? 'LIKE' : 'NOT LIKE'
541
- format('%s %s %s', deparse_item(node['lexpr']), operator, value)
542
- end
543
-
544
- def deparse_aexpr_ilike(node)
545
- value = deparse_item(node['rexpr'])
546
- operator = node['name'][0]['String']['str'] == '~~*' ? 'ILIKE' : 'NOT ILIKE'
547
- format('%s %s %s', deparse_item(node['lexpr']), operator, value)
548
- end
549
-
550
- def deparse_bool_expr_not(node)
551
- format('NOT %s', deparse_item(node['args'][0]))
552
- end
553
-
554
- BOOLEAN_TEST_TYPE_TO_STRING = {
555
- BOOLEAN_TEST_TRUE => ' IS TRUE',
556
- BOOLEAN_TEST_NOT_TRUE => ' IS NOT TRUE',
557
- BOOLEAN_TEST_FALSE => ' IS FALSE',
558
- BOOLEAN_TEST_NOT_FALSE => ' IS NOT FALSE',
559
- BOOLEAN_TEST_UNKNOWN => ' IS UNKNOWN',
560
- BOOLEAN_TEST_NOT_UNKNOWN => ' IS NOT UNKNOWN'
561
- }.freeze
562
- def deparse_boolean_test(node)
563
- deparse_item(node['arg']) + BOOLEAN_TEST_TYPE_TO_STRING[node['booltesttype']]
564
- end
565
-
566
- def deparse_range_function(node)
567
- output = []
568
- output << 'LATERAL' if node['lateral']
569
- output << deparse_item(node['functions'][0][0]) # FIXME: Needs more test cases
570
- output << deparse_item(node['alias']) if node['alias']
571
- output << "#{node['alias'] ? '' : 'AS '}(#{deparse_item_list(node['coldeflist']).join(', ')})" if node['coldeflist']
572
- output.join(' ')
573
- end
574
-
575
- def deparse_aexpr(node, context = false)
576
- output = []
577
- output << deparse_item(node['lexpr'], context || true)
578
- output << deparse_item(node['rexpr'], context || true)
579
- output = output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
580
- if context
581
- # This is a nested expression, add parentheses.
582
- output = '(' + output + ')'
583
- end
584
- output
585
- end
586
-
587
- def deparse_bool_expr_and(node)
588
- # Only put parantheses around OR nodes that are inside this one
589
- node['args'].map do |arg|
590
- if [BOOL_EXPR_OR].include?(arg.values[0]['boolop'])
591
- format('(%s)', deparse_item(arg))
592
- else
593
- deparse_item(arg)
594
- end
595
- end.join(' AND ')
596
- end
597
-
598
- def deparse_bool_expr_or(node)
599
- # Put parantheses around AND + OR nodes that are inside
600
- node['args'].map do |arg|
601
- if [BOOL_EXPR_AND, BOOL_EXPR_OR].include?(arg.values[0]['boolop'])
602
- format('(%s)', deparse_item(arg))
603
- else
604
- deparse_item(arg)
605
- end
606
- end.join(' OR ')
607
- end
608
-
609
- def deparse_aexpr_any(node)
610
- output = []
611
- output << deparse_item(node['lexpr'])
612
- output << format('ANY(%s)', deparse_item(node['rexpr']))
613
- output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
614
- end
615
-
616
- def deparse_aexpr_all(node)
617
- output = []
618
- output << deparse_item(node['lexpr'])
619
- output << format('ALL(%s)', deparse_item(node['rexpr']))
620
- output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
621
- end
622
-
623
- def deparse_aexpr_between(node)
624
- between = case node['kind']
625
- when AEXPR_BETWEEN
626
- ' BETWEEN '
627
- when AEXPR_NOT_BETWEEN
628
- ' NOT BETWEEN '
629
- when AEXPR_BETWEEN_SYM
630
- ' BETWEEN SYMMETRIC '
631
- when AEXPR_NOT_BETWEEN_SYM
632
- ' NOT BETWEEN SYMMETRIC '
633
- end
634
- name = deparse_item(node['lexpr'])
635
- output = node['rexpr'].map { |n| deparse_item(n) }
636
- name << between << output.join(' AND ')
637
- end
638
-
639
- def deparse_aexpr_nullif(node)
640
- lexpr = deparse_item(node['lexpr'])
641
- rexpr = deparse_item(node['rexpr'])
642
- format('NULLIF(%s, %s)', lexpr, rexpr)
643
- end
644
-
645
- def deparse_joinexpr(node) # rubocop:disable Metrics/CyclomaticComplexity
646
- output = []
647
- output << deparse_item(node['larg'])
648
- case node['jointype']
649
- when 0
650
- if node['isNatural']
651
- output << 'NATURAL'
652
- elsif node['quals'].nil? && node['usingClause'].nil?
653
- output << 'CROSS'
654
- end
655
- when 1
656
- output << 'LEFT'
657
- when 2
658
- output << 'FULL'
659
- when 3
660
- output << 'RIGHT'
661
- end
662
- output << 'JOIN'
663
- output << deparse_item(node['rarg'])
664
-
665
- if node['quals']
666
- output << 'ON'
667
- output << deparse_item(node['quals'])
668
- end
669
-
670
- output << format('USING (%s)', node['usingClause'].map { |n| deparse_item(n) }.join(', ')) if node['usingClause']
671
-
672
- output.join(' ')
673
- end
674
-
675
- def deparse_lock(node)
676
- output = []
677
- output << 'LOCK TABLE'
678
- tables = node['relations'].map { |table| deparse_item(table) }
679
- output << tables.join(', ')
680
- output.join(' ')
681
- end
682
-
683
- LOCK_CLAUSE_STRENGTH = {
684
- LCS_FORKEYSHARE => 'FOR KEY SHARE',
685
- LCS_FORSHARE => 'FOR SHARE',
686
- LCS_FORNOKEYUPDATE => 'FOR NO KEY UPDATE',
687
- LCS_FORUPDATE => 'FOR UPDATE'
688
- }.freeze
689
- def deparse_lockingclause(node)
690
- output = []
691
- output << LOCK_CLAUSE_STRENGTH[node['strength']]
692
- if node['lockedRels']
693
- output << 'OF'
694
- output << node['lockedRels'].map do |item|
695
- deparse_item(item)
696
- end.join(', ')
697
- end
698
- output.join(' ')
699
- end
700
-
701
- def deparse_sortby(node)
702
- output = []
703
- output << deparse_item(node['node'])
704
- output << 'ASC' if node['sortby_dir'] == 1
705
- output << 'DESC' if node['sortby_dir'] == 2
706
- output << 'NULLS FIRST' if node['sortby_nulls'] == 1
707
- output << 'NULLS LAST' if node['sortby_nulls'] == 2
708
- output.join(' ')
709
- end
710
-
711
- def deparse_collate(node)
712
- output = []
713
- output << deparse_item(node['arg'])
714
- output << 'COLLATE'
715
- output << deparse_item_list(node['collname'])
716
- output.join(' ')
717
- end
718
-
719
- def deparse_with_clause(node)
720
- output = ['WITH']
721
- output << 'RECURSIVE' if node['recursive']
722
- output << node['ctes'].map do |cte|
723
- deparse_item(cte)
724
- end.join(', ')
725
- output.join(' ')
726
- end
727
-
728
- def deparse_viewstmt(node)
729
- output = []
730
- output << 'CREATE'
731
- output << 'OR REPLACE' if node['replace']
732
-
733
- persistence = relpersistence(node['view'])
734
- output << persistence if persistence
735
-
736
- output << 'VIEW'
737
- output << node['view'][RANGE_VAR]['relname']
738
- output << format('(%s)', deparse_item_list(node['aliases']).join(', ')) if node['aliases']
739
-
740
- output << 'AS'
741
- output << deparse_item(node['query'])
742
-
743
- case node['withCheckOption']
744
- when 1
745
- output << 'WITH CHECK OPTION'
746
- when 2
747
- output << 'WITH CASCADED CHECK OPTION'
748
- end
749
- output.join(' ')
750
- end
751
-
752
- def deparse_variable_set_stmt(node)
753
- output = []
754
- output << 'SET'
755
- output << 'LOCAL' if node['is_local']
756
- output << node['name']
757
- output << 'TO'
758
- output << node['args'].map { |arg| deparse_item(arg) }.join(', ')
759
- output.join(' ')
760
- end
761
-
762
- def deparse_vacuum_stmt(node)
763
- output = []
764
- output << 'VACUUM'
765
- output.concat(deparse_vacuum_options(node))
766
- output << deparse_item(node['relation']) if node.key?('relation')
767
- if node.key?('va_cols')
768
- output << "(#{node['va_cols'].map(&method(:deparse_item)).join(', ')})"
769
- end
770
- output.join(' ')
771
- end
772
-
773
- def deparse_vacuum_options(node)
774
- output = []
775
- output << 'FULL' if node['options'][4] == 1
776
- output << 'FREEZE' if node['options'][3] == 1
777
- output << 'VERBOSE' if node['options'][2] == 1
778
- output << 'ANALYZE' if node['options'][1] == 1
779
- output
780
- end
781
-
782
- def deparse_do_stmt(node)
783
- output = []
784
- output << 'DO'
785
- statement, *rest = node['args']
786
- output << "$$#{statement['DefElem']['arg']['String']['str']}$$"
787
- output += rest.map { |item| deparse_item(item) }
788
- output.join(' ')
789
- end
790
-
791
- def deparse_cte(node)
792
- output = []
793
- output << node['ctename']
794
- output << format('(%s)', node['aliascolnames'].map { |n| deparse_item(n) }.join(', ')) if node['aliascolnames']
795
- output << format('AS (%s)', deparse_item(node['ctequery']))
796
- output.join(' ')
797
- end
798
-
799
- def deparse_case(node)
800
- output = ['CASE']
801
- output << deparse_item(node['arg']) if node['arg']
802
- output += node['args'].map { |arg| deparse_item(arg) }
803
- if node['defresult']
804
- output << 'ELSE'
805
- output << deparse_item(node['defresult'])
806
- end
807
- output << 'END'
808
- output.join(' ')
809
- end
810
-
811
- def deparse_columndef(node)
812
- output = [node['colname']]
813
- output << deparse_item(node['typeName'])
814
- if node['raw_default']
815
- output << 'USING'
816
- output << deparse_item(node['raw_default'])
817
- end
818
- if node['constraints']
819
- output += node['constraints'].map do |item|
820
- deparse_item(item)
821
- end
822
- end
823
- if node['collClause']
824
- output << 'COLLATE'
825
- output += node['collClause']['CollateClause']['collname'].map(&method(:deparse_item))
826
- end
827
- output.compact.join(' ')
828
- end
829
-
830
- def deparse_composite_type(node)
831
- output = ['CREATE TYPE']
832
- output << deparse_rangevar(node['typevar'][RANGE_VAR].merge('inh' => true))
833
- output << 'AS'
834
- coldeflist = node['coldeflist'].map(&method(:deparse_item))
835
- output << "(#{coldeflist.join(', ')})"
836
- output.join(' ')
837
- end
838
-
839
- def deparse_constraint(node) # rubocop:disable Metrics/CyclomaticComplexity
840
- output = []
841
- if node['conname']
842
- output << 'CONSTRAINT'
843
- output << node['conname']
844
- end
845
- case node['contype']
846
- when CONSTR_TYPE_NULL
847
- output << 'NULL'
848
- when CONSTR_TYPE_NOTNULL
849
- output << 'NOT NULL'
850
- when CONSTR_TYPE_DEFAULT
851
- output << 'DEFAULT'
852
- when CONSTR_TYPE_CHECK
853
- output << 'CHECK'
854
- when CONSTR_TYPE_PRIMARY
855
- output << 'PRIMARY KEY'
856
- when CONSTR_TYPE_UNIQUE
857
- output << 'UNIQUE'
858
- when CONSTR_TYPE_EXCLUSION
859
- output << 'EXCLUSION'
860
- when CONSTR_TYPE_FOREIGN
861
- output << 'FOREIGN KEY'
862
- end
863
-
864
- if node['raw_expr']
865
- expression = deparse_item(node['raw_expr'])
866
- # Unless it's simple, put parentheses around it
867
- expression = '(' + expression + ')' if node['raw_expr'][BOOL_EXPR]
868
- expression = '(' + expression + ')' if node['raw_expr'][A_EXPR] && node['raw_expr'][A_EXPR]['kind'] == AEXPR_OP
869
- output << expression
870
- end
871
- output << '(' + deparse_item_list(node['keys']).join(', ') + ')' if node['keys']
872
- output << '(' + deparse_item_list(node['fk_attrs']).join(', ') + ')' if node['fk_attrs']
873
- output << 'REFERENCES ' + deparse_item(node['pktable']) + ' (' + deparse_item_list(node['pk_attrs']).join(', ') + ')' if node['pktable']
874
- output << 'NOT VALID' if node['skip_validation']
875
- output << "USING INDEX #{node['indexname']}" if node['indexname']
876
- output.join(' ')
877
- end
878
-
879
- def deparse_copy(node)
880
- output = ['COPY']
881
- if node.key?('relation')
882
- output << deparse_item(node['relation'])
883
- elsif node.key?('query')
884
- output << "(#{deparse_item(node['query'])})"
885
- end
886
- columns = node.fetch('attlist', []).map { |column| deparse_item(column) }
887
- output << "(#{columns.join(', ')})" unless columns.empty?
888
- output << (node['is_from'] ? 'FROM' : 'TO')
889
- output << 'PROGRAM' if node['is_program']
890
- output << deparse_copy_output(node)
891
- output.join(' ')
892
- end
893
-
894
- def deparse_copy_output(node)
895
- return "'#{node['filename']}'" if node.key?('filename')
896
- return 'STDIN' if node['is_from']
897
- 'STDOUT'
898
- end
899
-
900
- def deparse_create_enum(node)
901
- output = ['CREATE TYPE']
902
- output << deparse_item(node['typeName'][0])
903
- output << 'AS ENUM'
904
- vals = node['vals'].map { |val| deparse_item(val, A_CONST) }
905
- output << "(#{vals.join(', ')})"
906
- output.join(' ')
907
- end
908
-
909
- def deparse_create_cast(node)
910
- output = []
911
- output << 'CREATE'
912
- output << 'CAST'
913
- output << format('(%s AS %s)', deparse_item(node['sourcetype']), deparse_item(node['targettype']))
914
- output << if node['func']
915
- function = node['func']['ObjectWithArgs']
916
- name = deparse_item_list(function['objname']).join('.')
917
- arguments = deparse_item_list(function['objargs']).join(', ')
918
- format('WITH FUNCTION %s(%s)', name, arguments)
919
- elsif node['inout']
920
- 'WITH INOUT'
921
- else
922
- 'WITHOUT FUNCTION'
923
- end
924
- output << 'AS IMPLICIT' if (node['context']).zero?
925
- output << 'AS ASSIGNMENT' if node['context'] == 1
926
- output.join(' ')
927
- end
928
-
929
- def deparse_create_domain(node)
930
- output = []
931
- output << 'CREATE'
932
- output << 'DOMAIN'
933
- output << deparse_item_list(node['domainname']).join('.')
934
- output << 'AS'
935
- output << deparse_item(node['typeName']) if node['typeName']
936
- output << deparse_item(node['collClause']) if node['collClause']
937
- output << deparse_item_list(node['constraints'])
938
- output.join(' ')
939
- end
940
-
941
- def deparse_create_function(node)
942
- output = []
943
- output << 'CREATE'
944
- output << 'OR REPLACE' if node['replace']
945
- output << 'FUNCTION'
946
-
947
- arguments = deparse_item_list(node.fetch('parameters', [])).join(', ')
948
-
949
- output << deparse_item_list(node['funcname']).join('.') + '(' + arguments + ')'
950
-
951
- output << 'RETURNS'
952
- output << deparse_item(node['returnType'])
953
- output += node['options'].map { |item| deparse_item(item) }
954
-
955
- output.join(' ')
956
- end
957
-
958
- def deparse_create_range(node)
959
- output = ['CREATE TYPE']
960
- output << deparse_item(node['typeName'][0])
961
- output << 'AS RANGE'
962
- params = node['params'].map do |param|
963
- param_out = [param['DefElem']['defname']]
964
- if param['DefElem'].key?('arg')
965
- param_out << deparse_item(param['DefElem']['arg'])
966
- end
967
- param_out.join('=')
968
- end
969
- output << "(#{params.join(', ')})"
970
- output.join(' ')
971
- end
972
-
973
- def deparse_create_schema(node)
974
- output = ['CREATE SCHEMA']
975
- output << 'IF NOT EXISTS' if node['if_not_exists']
976
- output << deparse_identifier(node['schemaname']) if node.key?('schemaname')
977
- output << format('AUTHORIZATION %s', deparse_item(node['authrole'])) if node.key?('authrole')
978
- output << deparse_item_list(node['schemaElts']) if node.key?('schemaElts')
979
- output.join(' ')
980
- end
981
-
982
- def deparse_create_table(node)
983
- output = []
984
- output << 'CREATE'
985
-
986
- persistence = relpersistence(node['relation'])
987
- output << persistence if persistence
988
-
989
- output << 'TABLE'
990
-
991
- output << 'IF NOT EXISTS' if node['if_not_exists']
992
-
993
- output << deparse_item(node['relation'])
994
-
995
- output << '(' + node['tableElts'].map do |item|
996
- deparse_item(item)
997
- end.join(', ') + ')'
998
-
999
- if node['inhRelations']
1000
- output << 'INHERITS'
1001
- output << '(' + node['inhRelations'].map do |relation|
1002
- deparse_item(relation)
1003
- end.join(', ') + ')'
1004
- end
1005
-
1006
- output.join(' ')
1007
- end
1008
-
1009
- def deparse_create_table_as(node)
1010
- output = []
1011
- output << 'CREATE'
1012
-
1013
- into = node['into']['IntoClause']
1014
- persistence = relpersistence(into['rel'])
1015
- output << persistence if persistence
1016
-
1017
- output << 'TABLE'
1018
-
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
-
1027
- output << 'AS'
1028
- output << deparse_item(node['query'])
1029
- output.join(' ')
1030
- end
1031
-
1032
- def deparse_execute(node)
1033
- output = ['EXECUTE']
1034
- output << deparse_identifier(node['name'])
1035
- output << "(#{deparse_item_list(node['params']).join(', ')})" if node['params']
1036
- output.join(' ')
1037
- end
1038
-
1039
- def deparse_into_clause(node)
1040
- deparse_item(node['rel'])
1041
- end
1042
-
1043
- def deparse_when(node)
1044
- output = ['WHEN']
1045
- output << deparse_item(node['expr'])
1046
- output << 'THEN'
1047
- output << deparse_item(node['result'])
1048
- output.join(' ')
1049
- end
1050
-
1051
- def deparse_sublink(node)
1052
- if node['subLinkType'] == SUBLINK_TYPE_ANY
1053
- format('%s IN (%s)', deparse_item(node['testexpr']), deparse_item(node['subselect']))
1054
- elsif node['subLinkType'] == SUBLINK_TYPE_ALL
1055
- format('%s %s ALL (%s)', deparse_item(node['testexpr']), deparse_item(node['operName'][0], :operator), deparse_item(node['subselect']))
1056
- elsif node['subLinkType'] == SUBLINK_TYPE_EXISTS
1057
- format('EXISTS(%s)', deparse_item(node['subselect']))
1058
- elsif node['subLinkType'] == SUBLINK_TYPE_ARRAY
1059
- format('ARRAY(%s)', deparse_item(node['subselect']))
1060
- else
1061
- format('(%s)', deparse_item(node['subselect']))
1062
- end
1063
- end
1064
-
1065
- def deparse_rangesubselect(node)
1066
- output = ''
1067
- output = 'LATERAL ' if node['lateral']
1068
- output = output + '(' + deparse_item(node['subquery']) + ')'
1069
- if node['alias']
1070
- output + ' ' + deparse_item(node['alias'])
1071
- else
1072
- output
1073
- end
1074
- end
1075
-
1076
- def deparse_row(node)
1077
- 'ROW(' + node['args'].map { |arg| deparse_item(arg) }.join(', ') + ')'
1078
- end
1079
-
1080
- def deparse_select(node) # rubocop:disable Metrics/CyclomaticComplexity
1081
- output = []
1082
-
1083
- output << deparse_item(node['withClause']) if node['withClause']
1084
-
1085
- if node['op'] == 1
1086
- output << '(' if node['larg']['SelectStmt']['sortClause']
1087
- output << deparse_item(node['larg'])
1088
- output << ')' if node['larg']['SelectStmt']['sortClause']
1089
- output << 'UNION'
1090
- output << 'ALL' if node['all']
1091
- output << '(' if node['rarg']['SelectStmt']['sortClause']
1092
- output << deparse_item(node['rarg'])
1093
- output << ')' if node['rarg']['SelectStmt']['sortClause']
1094
- output.join(' ')
1095
- end
1096
-
1097
- if node['op'] == 3
1098
- output << deparse_item(node['larg'])
1099
- output << 'EXCEPT'
1100
- output << deparse_item(node['rarg'])
1101
- output.join(' ')
1102
- end
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
-
1113
- if node[TARGET_LIST_FIELD]
1114
- if node['distinctClause']
1115
- output << 'DISTINCT'
1116
- unless node['distinctClause'].compact.empty?
1117
- columns = node['distinctClause'].map { |item| deparse_item(item, :select) }
1118
- output << "ON (#{columns.join(', ')})"
1119
- end
1120
- end
1121
- output << node[TARGET_LIST_FIELD].map do |item|
1122
- deparse_item(item, :select)
1123
- end.join(', ')
1124
-
1125
- if node['intoClause']
1126
- output << 'INTO'
1127
- output << deparse_item(node['intoClause'])
1128
- end
1129
- end
1130
-
1131
- if node[FROM_CLAUSE_FIELD]
1132
- output << 'FROM'
1133
- output << node[FROM_CLAUSE_FIELD].map do |item|
1134
- deparse_item(item)
1135
- end.join(', ')
1136
- end
1137
-
1138
- if node['whereClause']
1139
- output << 'WHERE'
1140
- output << deparse_item(node['whereClause'])
1141
- end
1142
-
1143
- if node['valuesLists']
1144
- output << 'VALUES'
1145
- output << node['valuesLists'].map do |value_list|
1146
- '(' + value_list.map { |v| deparse_item(v) }.join(', ') + ')'
1147
- end.join(', ')
1148
- end
1149
-
1150
- if node['groupClause']
1151
- output << 'GROUP BY'
1152
- output << node['groupClause'].map do |item|
1153
- deparse_item(item)
1154
- end.join(', ')
1155
- end
1156
-
1157
- if node['havingClause']
1158
- output << 'HAVING'
1159
- output << deparse_item(node['havingClause'])
1160
- end
1161
-
1162
- if node['sortClause']
1163
- output << 'ORDER BY'
1164
- output << node['sortClause'].map do |item|
1165
- deparse_item(item)
1166
- end.join(', ')
1167
- end
1168
-
1169
- if node['limitCount']
1170
- output << 'LIMIT'
1171
- output << deparse_item(node['limitCount'])
1172
- end
1173
-
1174
- if node['limitOffset']
1175
- output << 'OFFSET'
1176
- output << deparse_item(node['limitOffset'])
1177
- end
1178
-
1179
- if node['lockingClause']
1180
- node['lockingClause'].map do |item|
1181
- output << deparse_item(item)
1182
- end
1183
- end
1184
-
1185
- output.join(' ')
1186
- end
1187
-
1188
- def deparse_sql_value_function(node)
1189
- output = []
1190
- lookup = [
1191
- 'current_date',
1192
- 'current_time',
1193
- 'current_time', # with precision
1194
- 'current_timestamp',
1195
- 'current_timestamp', # with precision
1196
- 'localtime',
1197
- 'localtime', # with precision
1198
- 'localtimestamp',
1199
- 'localtimestamp', # with precision
1200
- 'current_role',
1201
- 'current_user',
1202
- 'session_user',
1203
- 'user',
1204
- 'current_catalog',
1205
- 'current_schema'
1206
- ]
1207
- output << lookup[node['op']]
1208
- output << "(#{node['typmod']})" unless node.fetch('typmod', -1) == -1
1209
- output.join('')
1210
- end
1211
-
1212
- def deparse_insert_into(node)
1213
- output = []
1214
- output << deparse_item(node['withClause']) if node['withClause']
1215
-
1216
- output << 'INSERT INTO'
1217
- output << deparse_item(node['relation'])
1218
-
1219
- if node['cols']
1220
- output << '(' + node['cols'].map do |column|
1221
- deparse_item(column, :select)
1222
- end.join(', ') + ')'
1223
- end
1224
-
1225
- output << deparse_item(node['selectStmt'])
1226
-
1227
- if node['onConflictClause']
1228
- output << deparse_insert_onconflict(node['onConflictClause'][ON_CONFLICT_CLAUSE])
1229
- end
1230
-
1231
- if node['returningList']
1232
- output << 'RETURNING'
1233
- output << node['returningList'].map do |column|
1234
- deparse_item(column, :select)
1235
- end.join(', ')
1236
- end
1237
-
1238
- output.join(' ')
1239
- end
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
-
1275
- def deparse_update(node)
1276
- output = []
1277
- output << deparse_item(node['withClause']) if node['withClause']
1278
-
1279
- output << 'UPDATE'
1280
- output << deparse_item(node['relation'])
1281
-
1282
- if node[TARGET_LIST_FIELD]
1283
- output << 'SET'
1284
- columns = node[TARGET_LIST_FIELD].map do |item|
1285
- deparse_item(item, :update)
1286
- end
1287
- output << columns.join(', ')
1288
- end
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
-
1297
- if node['whereClause']
1298
- output << 'WHERE'
1299
- output << deparse_item(node['whereClause'])
1300
- end
1301
-
1302
- if node['returningList']
1303
- output << 'RETURNING'
1304
- output << node['returningList'].map do |item|
1305
- # RETURNING is formatted like a SELECT
1306
- deparse_item(item, :select)
1307
- end.join(', ')
1308
- end
1309
-
1310
- output.join(' ')
1311
- end
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
-
1331
- def deparse_typecast(node)
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')
1337
- end
1338
-
1339
- context = true if node['arg']['A_Expr']
1340
- deparse_item(node['arg'], context) + '::' + deparse_typename(node['typeName'][TYPE_NAME])
1341
- end
1342
-
1343
- def deparse_typename(node)
1344
- names = node['names'].map { |n| deparse_item(n, TYPE_NAME) }
1345
-
1346
- # Intervals are tricky and should be handled in a separate method because
1347
- # they require performing some bitmask operations.
1348
- return deparse_interval_type(node) if names == %w[pg_catalog interval]
1349
-
1350
- output = []
1351
- output << 'SETOF' if node['setof']
1352
-
1353
- if node['typmods']
1354
- arguments = node['typmods'].map do |item|
1355
- deparse_item(item)
1356
- end.join(', ')
1357
- end
1358
- output << deparse_typename_cast(names, arguments)
1359
- output.last << '[]' if node['arrayBounds']
1360
-
1361
- output.join(' ')
1362
- end
1363
-
1364
- def deparse_typename_cast(names, arguments) # rubocop:disable Metrics/CyclomaticComplexity
1365
- catalog, type = names
1366
- # Just pass along any custom types.
1367
- # (The pg_catalog types are built-in Postgres system types and are
1368
- # handled in the case statement below)
1369
- return names.join('.') + (arguments.nil? ? '' : "(#{arguments})") if catalog != 'pg_catalog'
1370
-
1371
- case type
1372
- when 'bpchar'
1373
- # char(2) or char(9)
1374
- "char(#{arguments})"
1375
- when 'varchar'
1376
- arguments.nil? ? 'varchar' : "varchar(#{arguments})"
1377
- when 'numeric'
1378
- # numeric(3, 5)
1379
- arguments.nil? ? 'numeric' : "numeric(#{arguments})"
1380
- when 'bool'
1381
- 'boolean'
1382
- when 'int2'
1383
- 'smallint'
1384
- when 'int4'
1385
- 'int'
1386
- when 'int8'
1387
- 'bigint'
1388
- when 'real', 'float4'
1389
- 'real'
1390
- when 'float8'
1391
- 'double precision'
1392
- when 'time'
1393
- 'time'
1394
- when 'timetz'
1395
- 'time with time zone'
1396
- when 'timestamp'
1397
- 'timestamp'
1398
- when 'timestamptz'
1399
- 'timestamp with time zone'
1400
- else
1401
- raise format("Can't deparse type: %s", type)
1402
- end
1403
- end
1404
-
1405
- # Deparses interval type expressions like `interval year to month` or
1406
- # `interval hour to second(5)`
1407
- def deparse_interval_type(node)
1408
- type = ['interval']
1409
-
1410
- if node['typmods']
1411
- typmods = node['typmods'].map { |typmod| deparse_item(typmod) }
1412
- type << Interval.from_int(typmods.first.to_i).map do |part|
1413
- # only the `second` type can take an argument.
1414
- if part == 'second' && typmods.size == 2
1415
- "second(#{typmods.last})"
1416
- else
1417
- part
1418
- end.downcase
1419
- end.join(' to ')
1420
- end
1421
-
1422
- type.join(' ')
1423
- end
1424
-
1425
- def deparse_nulltest(node)
1426
- output = [deparse_item(node['arg'])]
1427
- case node['nulltesttype']
1428
- when 0
1429
- output << 'IS NULL'
1430
- when 1
1431
- output << 'IS NOT NULL'
1432
- end
1433
- output.join(' ')
1434
- end
1435
-
1436
- TRANSACTION_CMDS = {
1437
- TRANS_STMT_BEGIN => 'BEGIN',
1438
- TRANS_STMT_COMMIT => 'COMMIT',
1439
- TRANS_STMT_ROLLBACK => 'ROLLBACK',
1440
- TRANS_STMT_SAVEPOINT => 'SAVEPOINT',
1441
- TRANS_STMT_RELEASE => 'RELEASE',
1442
- TRANS_STMT_ROLLBACK_TO => 'ROLLBACK TO SAVEPOINT'
1443
- }.freeze
1444
- def deparse_transaction(node)
1445
- output = []
1446
- output << TRANSACTION_CMDS[node['kind']] || raise(format("Can't deparse TRANSACTION %s", node.inspect))
1447
-
1448
- if node['options']
1449
- output += node['options'].map { |item| deparse_item(item) }
1450
- end
1451
-
1452
- output.join(' ')
1453
- end
1454
-
1455
- def deparse_coalesce(node)
1456
- format('COALESCE(%s)', node['args'].map { |a| deparse_item(a) }.join(', '))
1457
- end
1458
-
1459
- def deparse_defelem(node)
1460
- case node['defname']
1461
- when 'as'
1462
- "AS $$#{deparse_item_list(node['arg'], :defname_as).join("\n")}$$"
1463
- when 'language'
1464
- "language #{deparse_item(node['arg'])}"
1465
- when 'volatility'
1466
- node['arg']['String']['str'].upcase # volatility does not need to be quoted
1467
- when 'strict'
1468
- deparse_item(node['arg']) == '1' ? 'RETURNS NULL ON NULL INPUT' : 'CALLED ON NULL INPUT'
1469
- else
1470
- deparse_item(node['arg'])
1471
- end
1472
- end
1473
-
1474
- def deparse_define_stmt(node)
1475
- dispatch = {
1476
- 1 => :deparse_create_aggregate,
1477
- 25 => :deparse_create_operator,
1478
- 45 => :deparse_create_type
1479
- }
1480
- method(dispatch.fetch(node['kind'])).call(node)
1481
- end
1482
-
1483
- def deparse_create_aggregate(node)
1484
- output = ['CREATE AGGREGATE']
1485
- output << node['defnames'].map(&method(:deparse_item))
1486
- args = node['args'][0] || [{ A_STAR => nil }]
1487
- output << "(#{args.map(&method(:deparse_item)).join(', ')})"
1488
- definitions = node['definition'].map do |definition|
1489
- definition_output = [definition['DefElem']['defname']]
1490
- definition_output << definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item)).join(', ') if definition['DefElem'].key?('arg')
1491
- definition_output.join('=')
1492
- end
1493
- output << "(#{definitions.join(', ')})"
1494
- output.join(' ')
1495
- end
1496
-
1497
- def deparse_create_operator(node)
1498
- output = ['CREATE OPERATOR']
1499
- output << node['defnames'][0]['String']['str']
1500
- definitions = node['definition'].map do |definition|
1501
- definition_output = [definition['DefElem']['defname']]
1502
- definition_output << definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item)).join(', ') if definition['DefElem'].key?('arg')
1503
- definition_output.join('=')
1504
- end
1505
- output << "(#{definitions.join(', ')})"
1506
- output.join(' ')
1507
- end
1508
-
1509
- def deparse_create_type(node)
1510
- output = ['CREATE TYPE']
1511
- output << node['defnames'].map(&method(:deparse_item))
1512
- if node.key?('definition')
1513
- definitions = node['definition'].map do |definition|
1514
- definition_output = [definition['DefElem']['defname']]
1515
- if definition['DefElem'].key?('arg')
1516
- definition_output += definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item))
1517
- end
1518
- definition_output.join('=')
1519
- end
1520
- output << "(#{definitions.join(', ')})"
1521
- end
1522
- output.join(' ')
1523
- end
1524
-
1525
- def deparse_delete_from(node)
1526
- output = []
1527
- output << deparse_item(node['withClause']) if node['withClause']
1528
-
1529
- output << 'DELETE FROM'
1530
- output << deparse_item(node['relation'])
1531
-
1532
- if node['usingClause']
1533
- output << 'USING'
1534
- output << node['usingClause'].map do |item|
1535
- deparse_item(item)
1536
- end.join(', ')
1537
- end
1538
-
1539
- if node['whereClause']
1540
- output << 'WHERE'
1541
- output << deparse_item(node['whereClause'])
1542
- end
1543
-
1544
- if node['returningList']
1545
- output << 'RETURNING'
1546
- output << node['returningList'].map do |item|
1547
- # RETURNING is formatted like a SELECT
1548
- deparse_item(item, :select)
1549
- end.join(', ')
1550
- end
1551
-
1552
- output.join(' ')
1553
- end
1554
-
1555
- def deparse_discard(node)
1556
- output = ['DISCARD']
1557
- output << 'ALL' if (node['target']).zero?
1558
- output << 'PLANS' if node['target'] == 1
1559
- output << 'SEQUENCES' if node['target'] == 2
1560
- output << 'TEMP' if node['target'] == 3
1561
- output.join(' ')
1562
- end
1563
-
1564
- def deparse_drop(node) # rubocop:disable Metrics/CyclomaticComplexity
1565
- output = ['DROP']
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
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
-
1599
- output << 'CONCURRENTLY' if node['concurrent']
1600
- output << 'IF EXISTS' if node['missing_ok']
1601
-
1602
- objects = node['objects']
1603
- objects = [objects] unless objects[0].is_a?(Array)
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
1631
-
1632
- output << 'CASCADE' if node['behavior'] == 1
1633
-
1634
- output.join(' ')
1635
- end
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
-
1658
- def deparse_explain(node)
1659
- output = ['EXPLAIN']
1660
- options = node.fetch('options', []).map { |option| option['DefElem']['defname'].upcase }
1661
- if options.size == 1
1662
- output.concat(options)
1663
- elsif options.size > 1
1664
- output << "(#{options.join(', ')})"
1665
- end
1666
- output << deparse_item(node['query'])
1667
- output.join(' ')
1668
- 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
1669
17
 
1670
- # The PG parser adds several pieces of view data onto the RANGEVAR
1671
- # that need to be printed before deparse_rangevar is called.
1672
- def relpersistence(rangevar)
1673
- if rangevar[RANGE_VAR]['relpersistence'] == 't'
1674
- 'TEMPORARY'
1675
- elsif rangevar[RANGE_VAR]['relpersistence'] == 'u'
1676
- 'UNLOGGED'
1677
- end
1678
- 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 ', '')
1679
21
  end
1680
22
  end