pg_query 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0db8e898d08ff3642a800ab296d7ac10dd9556e3c5a626f005bae86d35273372
4
- data.tar.gz: a869a1faeb3ae0bed9d5031f2a4d26263843fde368bfeb096c1c9f48ea30c9f8
3
+ metadata.gz: 9cb01b49c6ea65d1ff126562ce68adaa5635d37093ff726b1bc70d65c341ae9e
4
+ data.tar.gz: 4dc7fa1bf00d7ff6036a79d8a38fd9f3e383f449b21f0d53f0168be7d32df89a
5
5
  SHA512:
6
- metadata.gz: e0f51970e5f268d9d9d32d791288db0d7a048c4880f706ff7d483b0efacd1a355600f5b8ca8a0b57b75a05cda8c7b207402bb6680fad0fdaeaf7809fab06c441
7
- data.tar.gz: 6281530a32add88cd8a474a8e224e817723e288e850f9d9232aba69208b1178d89b7135d613d15800176bd611b3d6fcd0600fb48c3e8c880f6aa47f5296bae45
6
+ metadata.gz: ad48c885044147fcc9fa406543fc8a805c789fa227d71000bd1d25be0846aa1f7e183420e7ee86df262ff0d7ae668f2c4b1cd4ef15f5d305d5deeb5d86d6e8ad
7
+ data.tar.gz: 860aa2345238226eaf5594d20d34af5a6aed39177a0900e0b72e79771586f96bf2917ac73722d3672cf730e18c2977cd275617fe411e79b4af8266ac4e499124
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.1 2019-11-10
4
+
5
+ * Deparsing improvements by [@emin100](https://github.com/emin100)
6
+ * Deparse ILIKE, COLLATE and DISCARD (#133)
7
+ * CREATE CAST (#136)
8
+ * CREATE SCHEMA (#136)
9
+ * UNION, UNION ALL and EXCEPT in SELECT queries (#136)
10
+ * CREATE DOMAIN (#145)
11
+ * Subquery indirection (#157)
12
+ * Fix Type Cast Parentheses Problem (#152)
13
+ * SELECT INTO (#151)
14
+ * SET DEFAULT in INSERT INTO (#154)
15
+ * REVOKE (#155)
16
+ * PREPARE and EXECUTE (#148)
17
+ * INSERT INTO ... RETURNING (#153)
18
+ * Fix Alter .. RENAME SQL (#146)
19
+ * Deparsing improvements by [@herwinw](https://github.com/herwinw)
20
+ * Fix subquery in COPY in deparse (#112)
21
+ * Function call indirection (#116)
22
+ * Function without parameters (#117)
23
+ * CREATE AGGREGATE
24
+ * CREATE OPERATOR
25
+ * CREATE TYPE
26
+ * GRANT statements
27
+ * DROP SCHEMA
28
+ * Deparsing improvements by [@akiellor](https://github.com/akiellor)
29
+ * Named window functions (#150)
30
+ * Deparsing improvements by [@himanshu](https://github.com/himanshu)
31
+ * Arguments in custom types (#143)
32
+ * Use "double precision" instead of "double" type name (#139)
33
+ * Use explicit -z flag to support OpenBSD tar (#134) [@sirn](https://github.com/sirn)
34
+ * Add Ruby 2.6 to Travis tests
35
+ * Escape identifiers in more cases, if necessary
36
+
37
+
3
38
  ## 1.1.0 2018-10-04
4
39
 
5
40
  * Deparsing improvements by [@herwinw](https://github.com/herwinw)
@@ -19,7 +19,7 @@ unless File.exist?("#{workdir}/libpg_query.tar.gz")
19
19
  end
20
20
 
21
21
  unless Dir.exist?(libdir)
22
- system("tar -xf #{workdir}/libpg_query.tar.gz") || raise('ERROR')
22
+ system("tar -xzf #{workdir}/libpg_query.tar.gz") || raise('ERROR')
23
23
  end
24
24
 
25
25
  unless Dir.exist?(libfile)
@@ -1,5 +1,8 @@
1
- require_relative 'deparse/interval'
2
1
  require_relative 'deparse/alter_table'
2
+ require_relative 'deparse/rename'
3
+ require_relative 'deparse/interval'
4
+ require_relative 'deparse/keywords'
5
+
3
6
  class PgQuery
4
7
  # Reconstruct all of the parsed queries into their original form
5
8
  def deparse(tree = @tree)
@@ -32,10 +35,14 @@ class PgQuery
32
35
  case node['kind']
33
36
  when AEXPR_OP
34
37
  deparse_aexpr(node, context)
38
+ when AEXPR_OP_ALL
39
+ deparse_aexpr_all(node)
35
40
  when AEXPR_OP_ANY
36
41
  deparse_aexpr_any(node)
37
42
  when AEXPR_IN
38
43
  deparse_aexpr_in(node)
44
+ when AEXPR_ILIKE
45
+ deparse_aexpr_ilike(node)
39
46
  when CONSTR_TYPE_FOREIGN
40
47
  deparse_aexpr_like(node)
41
48
  when AEXPR_BETWEEN, AEXPR_NOT_BETWEEN, AEXPR_BETWEEN_SYM, AEXPR_NOT_BETWEEN_SYM
@@ -45,6 +52,8 @@ class PgQuery
45
52
  else
46
53
  raise format("Can't deparse: %s: %s", type, node.inspect)
47
54
  end
55
+ when ACCESS_PRIV
56
+ deparse_access_priv(node)
48
57
  when ALIAS
49
58
  deparse_alias(node)
50
59
  when ALTER_TABLE_STMT
@@ -78,18 +87,32 @@ class PgQuery
78
87
  deparse_case(node)
79
88
  when COALESCE_EXPR
80
89
  deparse_coalesce(node)
90
+ when COLLATE_CLAUSE
91
+ deparse_collate(node)
81
92
  when COLUMN_DEF
82
93
  deparse_columndef(node)
83
94
  when COLUMN_REF
84
95
  deparse_columnref(node)
85
96
  when COMMON_TABLE_EXPR
86
97
  deparse_cte(node)
98
+ when COMPOSITE_TYPE_STMT
99
+ deparse_composite_type(node)
87
100
  when CONSTRAINT
88
101
  deparse_constraint(node)
89
102
  when COPY_STMT
90
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)
91
110
  when CREATE_FUNCTION_STMT
92
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)
93
116
  when CREATE_STMT
94
117
  deparse_create_table(node)
95
118
  when CREATE_TABLE_AS_STMT
@@ -98,16 +121,26 @@ class PgQuery
98
121
  deparse_into_clause(node)
99
122
  when DEF_ELEM
100
123
  deparse_defelem(node)
124
+ when DEFINE_STMT
125
+ deparse_define_stmt(node)
101
126
  when DELETE_STMT
102
127
  deparse_delete_from(node)
128
+ when DISCARD_STMT
129
+ deparse_discard(node)
103
130
  when DROP_STMT
104
131
  deparse_drop(node)
105
132
  when EXPLAIN_STMT
106
133
  deparse_explain(node)
134
+ when EXECUTE_STMT
135
+ deparse_execute(node)
107
136
  when FUNC_CALL
108
137
  deparse_funccall(node)
109
138
  when FUNCTION_PARAMETER
110
139
  deparse_functionparameter(node)
140
+ when GRANT_ROLE_STMT
141
+ deparse_grant_role(node)
142
+ when GRANT_STMT
143
+ deparse_grant(node)
111
144
  when INSERT_STMT
112
145
  deparse_insert_into(node)
113
146
  when JOIN_EXPR
@@ -118,8 +151,12 @@ class PgQuery
118
151
  deparse_lockingclause(node)
119
152
  when NULL_TEST
120
153
  deparse_nulltest(node)
154
+ when OBJECT_WITH_ARGS
155
+ deparse_object_with_args(node)
121
156
  when PARAM_REF
122
157
  deparse_paramref(node)
158
+ when PREPARE_STMT
159
+ deparse_prepare(node)
123
160
  when RANGE_FUNCTION
124
161
  deparse_range_function(node)
125
162
  when RANGE_SUBSELECT
@@ -132,6 +169,8 @@ class PgQuery
132
169
  deparse_renamestmt(node)
133
170
  when RES_TARGET
134
171
  deparse_restarget(node, context)
172
+ when ROLE_SPEC
173
+ deparse_role_spec(node)
135
174
  when ROW_EXPR
136
175
  deparse_row(node)
137
176
  when SELECT_STMT
@@ -164,13 +203,15 @@ class PgQuery
164
203
  deparse_vacuum_stmt(node)
165
204
  when DO_STMT
166
205
  deparse_do_stmt(node)
206
+ when SET_TO_DEFAULT
207
+ 'DEFAULT'
167
208
  when STRING
168
209
  if context == A_CONST
169
210
  format("'%s'", node['str'].gsub("'", "''"))
170
211
  elsif [FUNC_CALL, TYPE_NAME, :operator, :defname_as].include?(context)
171
212
  node['str']
172
213
  else
173
- format('"%s"', node['str'].gsub('"', '""'))
214
+ deparse_identifier(node['str'], escape_always: true)
174
215
  end
175
216
  when INTEGER
176
217
  node['ival'].to_s
@@ -187,6 +228,15 @@ class PgQuery
187
228
  nodes.map { |n| deparse_item(n, context) }
188
229
  end
189
230
 
231
+ def deparse_identifier(ident, escape_always: false)
232
+ return if ident.nil?
233
+ if escape_always || !ident[/^\w+$/] || KEYWORDS.include?(ident.upcase)
234
+ format('"%s"', ident.gsub('"', '""'))
235
+ else
236
+ ident
237
+ end
238
+ end
239
+
190
240
  def deparse_rangevar(node)
191
241
  output = []
192
242
  output << 'ONLY' unless node['inh']
@@ -200,19 +250,32 @@ class PgQuery
200
250
  deparse_item(node[STMT_FIELD])
201
251
  end
202
252
 
203
- def deparse_renamestmt(node)
204
- output = []
205
-
206
- case node['renameType']
207
- when OBJECT_TYPE_TABLE
208
- output << 'ALTER TABLE'
209
- output << deparse_item(node['relation'])
210
- output << 'RENAME TO'
211
- output << node['newname']
253
+ def deparse_renamestmt_decision(node, type)
254
+ if node[type]
255
+ if node[type].is_a?(String)
256
+ deparse_identifier(node[type])
257
+ elsif node[type].is_a?(Array)
258
+ deparse_item_list(node[type])
259
+ elsif node[type].is_a?(Object)
260
+ deparse_item(node[type])
261
+ end
212
262
  else
213
- raise format("Can't deparse: %s", node.inspect)
263
+ type
214
264
  end
265
+ end
215
266
 
267
+ def deparse_renamestmt(node)
268
+ output = []
269
+ output << 'ALTER'
270
+ command, type, options, type2, options2 = Rename.commands(node)
271
+
272
+ output << command if command
273
+ output << deparse_renamestmt_decision(node, type)
274
+ output << options if options
275
+ output << deparse_renamestmt_decision(node, type2) if type2
276
+ output << deparse_renamestmt_decision(node, options2) if options2
277
+ output << 'TO'
278
+ output << deparse_identifier(node['newname'], escape_always: true)
216
279
  output.join(' ')
217
280
  end
218
281
 
@@ -223,7 +286,7 @@ class PgQuery
223
286
  end
224
287
 
225
288
  def deparse_a_arrayexp(node)
226
- 'ARRAY[' + node['elements'].map do |element|
289
+ 'ARRAY[' + (node['elements'] || []).map do |element|
227
290
  deparse_item(element)
228
291
  end.join(', ') + ']'
229
292
  end
@@ -237,7 +300,13 @@ class PgQuery
237
300
  end
238
301
 
239
302
  def deparse_a_indirection(node)
240
- output = [deparse_item(node['arg'])]
303
+ output = []
304
+ arg = deparse_item(node['arg'])
305
+ output << if node['arg'].key?(FUNC_CALL) || node['arg'].key?(SUB_LINK)
306
+ "(#{arg})."
307
+ else
308
+ arg
309
+ end
241
310
  node['indirection'].each do |subnode|
242
311
  output << deparse_item(subnode)
243
312
  end
@@ -253,13 +322,16 @@ class PgQuery
253
322
  if node['colnames']
254
323
  name + '(' + deparse_item_list(node['colnames']).join(', ') + ')'
255
324
  else
256
- name
325
+ deparse_identifier(name)
257
326
  end
258
327
  end
259
328
 
260
329
  def deparse_alter_table(node)
261
330
  output = []
262
- output << 'ALTER TABLE'
331
+ output << 'ALTER'
332
+
333
+ output << 'TABLE' if node['relkind'] == OBJECT_TYPE_TABLE
334
+ output << 'VIEW' if node['relkind'] == OBJECT_TYPE_VIEW
263
335
 
264
336
  output << deparse_item(node['relation'])
265
337
 
@@ -284,6 +356,16 @@ class PgQuery
284
356
  output.compact.join(' ')
285
357
  end
286
358
 
359
+ def deparse_object_with_args(node)
360
+ output = []
361
+ output += node['objname'].map(&method(:deparse_item))
362
+ unless node['args_unspecified']
363
+ args = node.fetch('objargs', []).map(&method(:deparse_item)).join(', ')
364
+ output << "(#{args})"
365
+ end
366
+ output.join('')
367
+ end
368
+
287
369
  def deparse_paramref(node)
288
370
  if node['number'].nil?
289
371
  '?'
@@ -292,9 +374,18 @@ class PgQuery
292
374
  end
293
375
  end
294
376
 
377
+ def deparse_prepare(node)
378
+ output = ['PREPARE']
379
+ output << deparse_identifier(node['name'])
380
+ output << "(#{deparse_item_list(node['argtypes']).join(', ')})" if node['argtypes']
381
+ output << 'AS'
382
+ output << deparse_item(node['query'])
383
+ output.join(' ')
384
+ end
385
+
295
386
  def deparse_restarget(node, context)
296
387
  if context == :select
297
- [deparse_item(node['val']), node['name']].compact.join(' AS ')
388
+ [deparse_item(node['val']), deparse_identifier(node['name'])].compact.join(' AS ')
298
389
  elsif context == :update
299
390
  [node['name'], deparse_item(node['val'])].compact.join(' = ')
300
391
  elsif node['val'].nil?
@@ -315,12 +406,14 @@ class PgQuery
315
406
  name = (node['funcname'].map { |n| deparse_item(n, FUNC_CALL) } - ['pg_catalog']).join('.')
316
407
  distinct = node['agg_distinct'] ? 'DISTINCT ' : ''
317
408
  output << format('%s(%s%s)', name, distinct, args.join(', '))
318
- output << format('OVER (%s)', deparse_item(node['over'])) if node['over']
409
+ output << format('OVER %s', deparse_item(node['over'])) if node['over']
319
410
 
320
411
  output.join(' ')
321
412
  end
322
413
 
323
414
  def deparse_windowdef(node)
415
+ return deparse_identifier(node['name']) if node['name']
416
+
324
417
  output = []
325
418
 
326
419
  if node['partitionClause']
@@ -337,13 +430,84 @@ class PgQuery
337
430
  end.join(', ')
338
431
  end
339
432
 
340
- output.join(' ')
433
+ format('(%s)', output.join(' '))
341
434
  end
342
435
 
343
436
  def deparse_functionparameter(node)
344
437
  deparse_item(node['argType'])
345
438
  end
346
439
 
440
+ def deparse_grant_role(node)
441
+ output = []
442
+ output << ['GRANT'] if node['is_grant']
443
+ output << ['REVOKE'] unless node['is_grant']
444
+ output << node['granted_roles'].map(&method(:deparse_item)).join(', ')
445
+ output << ['TO'] if node['is_grant']
446
+ output << ['FROM'] unless node['is_grant']
447
+ output << node['grantee_roles'].map(&method(:deparse_item)).join(', ')
448
+ output << 'WITH ADMIN OPTION' if node['admin_opt']
449
+ output.join(' ')
450
+ end
451
+
452
+ def deparse_grant(node) # rubocop:disable Metrics/CyclomaticComplexity
453
+ objtype, allow_all = deparse_grant_objtype(node)
454
+ output = []
455
+ output << ['GRANT'] if node['is_grant']
456
+ output << ['REVOKE'] unless node['is_grant']
457
+ output << if node.key?('privileges')
458
+ node['privileges'].map(&method(:deparse_item)).join(', ')
459
+ else
460
+ 'ALL'
461
+ end
462
+ output << 'ON'
463
+ objects = node['objects']
464
+ objects = objects[0] if %w[DOMAIN TYPE].include?(objtype)
465
+ objects = objects.map do |object|
466
+ deparsed = deparse_item(object)
467
+ if object.key?(RANGE_VAR) || object.key?(OBJECT_WITH_ARGS) || !allow_all
468
+ objtype == 'TABLE' ? deparsed : "#{objtype} #{deparsed}"
469
+ else
470
+ "ALL #{objtype}S IN SCHEMA #{deparsed}"
471
+ end
472
+ end
473
+ output << objects.join(', ')
474
+ output << ['TO'] if node['is_grant']
475
+ output << ['FROM'] unless node['is_grant']
476
+ output << node['grantees'].map(&method(:deparse_item)).join(', ')
477
+ output << 'WITH GRANT OPTION' if node['grant_option']
478
+ output.join(' ')
479
+ end
480
+
481
+ def deparse_grant_objtype(node)
482
+ {
483
+ 1 => ['TABLE', true],
484
+ 2 => ['SEQUENCE', true],
485
+ 3 => ['DATABASE', false],
486
+ 4 => ['DOMAIN', false],
487
+ 5 => ['FOREIGN DATA WRAPPER', false],
488
+ 6 => ['FOREIGN SERVER', false],
489
+ 7 => ['FUNCTION', true],
490
+ 8 => ['LANGUAGE', false],
491
+ 9 => ['LARGE OBJECT', false],
492
+ 10 => ['SCHEMA', false],
493
+ 11 => ['TABLESPACE', false],
494
+ 12 => ['TYPE', false]
495
+ }.fetch(node['objtype'])
496
+ end
497
+
498
+ def deparse_access_priv(node)
499
+ output = [node['priv_name']]
500
+ output << "(#{node['cols'].map(&method(:deparse_item)).join(', ')})" if node.key?('cols')
501
+ output.join(' ')
502
+ end
503
+
504
+ def deparse_role_spec(node)
505
+ return 'CURRENT_USER' if node['roletype'] == 1
506
+ return 'SESSION_USER' if node['roletype'] == 2
507
+ return 'PUBLIC' if node['roletype'] == 3
508
+ deparse_identifier(node['rolename'], escape_always: true)
509
+ end
510
+
347
511
  def deparse_aexpr_in(node)
348
512
  rexpr = Array(node['rexpr']).map { |arg| deparse_item(arg) }
349
513
  operator = node['name'].map { |n| deparse_item(n, :operator) } == ['='] ? 'IN' : 'NOT IN'
@@ -356,6 +520,12 @@ class PgQuery
356
520
  format('%s %s %s', deparse_item(node['lexpr']), operator, value)
357
521
  end
358
522
 
523
+ def deparse_aexpr_ilike(node)
524
+ value = deparse_item(node['rexpr'])
525
+ operator = node['name'][0]['String']['str'] == '~~*' ? 'ILIKE' : 'NOT ILIKE'
526
+ format('%s %s %s', deparse_item(node['lexpr']), operator, value)
527
+ end
528
+
359
529
  def deparse_bool_expr_not(node)
360
530
  format('NOT %s', deparse_item(node['args'][0]))
361
531
  end
@@ -422,6 +592,13 @@ class PgQuery
422
592
  output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
423
593
  end
424
594
 
595
+ def deparse_aexpr_all(node)
596
+ output = []
597
+ output << deparse_item(node['lexpr'])
598
+ output << format('ALL(%s)', deparse_item(node['rexpr']))
599
+ output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
600
+ end
601
+
425
602
  def deparse_aexpr_between(node)
426
603
  between = case node['kind']
427
604
  when AEXPR_BETWEEN
@@ -510,6 +687,14 @@ class PgQuery
510
687
  output.join(' ')
511
688
  end
512
689
 
690
+ def deparse_collate(node)
691
+ output = []
692
+ output << deparse_item(node['arg'])
693
+ output << 'COLLATE'
694
+ output << deparse_item_list(node['collname'])
695
+ output.join(' ')
696
+ end
697
+
513
698
  def deparse_with_clause(node)
514
699
  output = ['WITH']
515
700
  output << 'RECURSIVE' if node['recursive']
@@ -614,9 +799,22 @@ class PgQuery
614
799
  deparse_item(item)
615
800
  end
616
801
  end
802
+ if node['collClause']
803
+ output << 'COLLATE'
804
+ output += node['collClause']['CollateClause']['collname'].map(&method(:deparse_item))
805
+ end
617
806
  output.compact.join(' ')
618
807
  end
619
808
 
809
+ def deparse_composite_type(node)
810
+ output = ['CREATE TYPE']
811
+ output << deparse_rangevar(node['typevar'][RANGE_VAR].merge('inh' => true))
812
+ output << 'AS'
813
+ coldeflist = node['coldeflist'].map(&method(:deparse_item))
814
+ output << "(#{coldeflist.join(', ')})"
815
+ output.join(' ')
816
+ end
817
+
620
818
  def deparse_constraint(node) # rubocop:disable Metrics/CyclomaticComplexity
621
819
  output = []
622
820
  if node['conname']
@@ -645,6 +843,7 @@ class PgQuery
645
843
  if node['raw_expr']
646
844
  expression = deparse_item(node['raw_expr'])
647
845
  # Unless it's simple, put parentheses around it
846
+ expression = '(' + expression + ')' if node['raw_expr'][BOOL_EXPR]
648
847
  expression = '(' + expression + ')' if node['raw_expr'][A_EXPR] && node['raw_expr'][A_EXPR]['kind'] == AEXPR_OP
649
848
  output << expression
650
849
  end
@@ -658,16 +857,63 @@ class PgQuery
658
857
 
659
858
  def deparse_copy(node)
660
859
  output = ['COPY']
661
- output << deparse_item(node['relation'])
860
+ if node.key?('relation')
861
+ output << deparse_item(node['relation'])
862
+ elsif node.key?('query')
863
+ output << "(#{deparse_item(node['query'])})"
864
+ end
662
865
  columns = node.fetch('attlist', []).map { |column| deparse_item(column) }
663
866
  output << "(#{columns.join(', ')})" unless columns.empty?
664
867
  output << (node['is_from'] ? 'FROM' : 'TO')
665
868
  output << 'PROGRAM' if node['is_program']
666
- output << if node.key?('filename')
667
- "'#{node['filename']}'"
869
+ output << deparse_copy_output(node)
870
+ output.join(' ')
871
+ end
872
+
873
+ def deparse_copy_output(node)
874
+ return "'#{node['filename']}'" if node.key?('filename')
875
+ return 'STDIN' if node['is_from']
876
+ 'STDOUT'
877
+ end
878
+
879
+ def deparse_create_enum(node)
880
+ output = ['CREATE TYPE']
881
+ output << deparse_item(node['typeName'][0])
882
+ output << 'AS ENUM'
883
+ vals = node['vals'].map { |val| deparse_item(val, A_CONST) }
884
+ output << "(#{vals.join(', ')})"
885
+ output.join(' ')
886
+ end
887
+
888
+ def deparse_create_cast(node)
889
+ output = []
890
+ output << 'CREATE'
891
+ output << 'CAST'
892
+ output << format('(%s AS %s)', deparse_item(node['sourcetype']), deparse_item(node['targettype']))
893
+ output << if node['func']
894
+ function = node['func']['ObjectWithArgs']
895
+ name = deparse_item_list(function['objname']).join('.')
896
+ arguments = deparse_item_list(function['objargs']).join(', ')
897
+ format('WITH FUNCTION %s(%s)', name, arguments)
898
+ elsif node['inout']
899
+ 'WITH INOUT'
668
900
  else
669
- node['is_from'] ? 'STDIN' : 'STDOUT'
901
+ 'WITHOUT FUNCTION'
670
902
  end
903
+ output << 'AS IMPLICIT' if (node['context']).zero?
904
+ output << 'AS ASSIGNMENT' if node['context'] == 1
905
+ output.join(' ')
906
+ end
907
+
908
+ def deparse_create_domain(node)
909
+ output = []
910
+ output << 'CREATE'
911
+ output << 'DOMAIN'
912
+ output << deparse_item_list(node['domainname']).join('.')
913
+ output << 'AS'
914
+ output << deparse_item(node['typeName']) if node['typeName']
915
+ output << deparse_item(node['collClause']) if node['collClause']
916
+ output << deparse_item_list(node['constraints'])
671
917
  output.join(' ')
672
918
  end
673
919
 
@@ -677,7 +923,7 @@ class PgQuery
677
923
  output << 'OR REPLACE' if node['replace']
678
924
  output << 'FUNCTION'
679
925
 
680
- arguments = deparse_item_list(node['parameters']).join(', ')
926
+ arguments = deparse_item_list(node.fetch('parameters', [])).join(', ')
681
927
 
682
928
  output << deparse_item_list(node['funcname']).join('.') + '(' + arguments + ')'
683
929
 
@@ -688,6 +934,30 @@ class PgQuery
688
934
  output.join(' ')
689
935
  end
690
936
 
937
+ def deparse_create_range(node)
938
+ output = ['CREATE TYPE']
939
+ output << deparse_item(node['typeName'][0])
940
+ output << 'AS RANGE'
941
+ params = node['params'].map do |param|
942
+ param_out = [param['DefElem']['defname']]
943
+ if param['DefElem'].key?('arg')
944
+ param_out << deparse_item(param['DefElem']['arg'])
945
+ end
946
+ param_out.join('=')
947
+ end
948
+ output << "(#{params.join(', ')})"
949
+ output.join(' ')
950
+ end
951
+
952
+ def deparse_create_schema(node)
953
+ output = ['CREATE SCHEMA']
954
+ output << 'IF NOT EXISTS' if node['if_not_exists']
955
+ output << deparse_identifier(node['schemaname']) if node.key?('schemaname')
956
+ output << format('AUTHORIZATION %s', deparse_item(node['authrole'])) if node.key?('authrole')
957
+ output << deparse_item_list(node['schemaElts']) if node.key?('schemaElts')
958
+ output.join(' ')
959
+ end
960
+
691
961
  def deparse_create_table(node)
692
962
  output = []
693
963
  output << 'CREATE'
@@ -724,6 +994,13 @@ class PgQuery
724
994
  output.join(' ')
725
995
  end
726
996
 
997
+ def deparse_execute(node)
998
+ output = ['EXECUTE']
999
+ output << deparse_identifier(node['name'])
1000
+ output << "(#{deparse_item_list(node['params']).join(', ')})" if node['params']
1001
+ output.join(' ')
1002
+ end
1003
+
727
1004
  def deparse_into_clause(node)
728
1005
  deparse_item(node['rel'])
729
1006
  end
@@ -739,6 +1016,8 @@ class PgQuery
739
1016
  def deparse_sublink(node)
740
1017
  if node['subLinkType'] == SUBLINK_TYPE_ANY
741
1018
  format('%s IN (%s)', deparse_item(node['testexpr']), deparse_item(node['subselect']))
1019
+ elsif node['subLinkType'] == SUBLINK_TYPE_ALL
1020
+ format('%s %s ALL (%s)', deparse_item(node['testexpr']), deparse_item(node['operName'][0], :operator), deparse_item(node['subselect']))
742
1021
  elsif node['subLinkType'] == SUBLINK_TYPE_EXISTS
743
1022
  format('EXISTS(%s)', deparse_item(node['subselect']))
744
1023
  else
@@ -762,15 +1041,22 @@ class PgQuery
762
1041
  def deparse_select(node) # rubocop:disable Metrics/CyclomaticComplexity
763
1042
  output = []
764
1043
 
1044
+ output << deparse_item(node['withClause']) if node['withClause']
1045
+
765
1046
  if node['op'] == 1
766
1047
  output << deparse_item(node['larg'])
767
1048
  output << 'UNION'
768
1049
  output << 'ALL' if node['all']
769
1050
  output << deparse_item(node['rarg'])
770
- return output.join(' ')
1051
+ output.join(' ')
771
1052
  end
772
1053
 
773
- output << deparse_item(node['withClause']) if node['withClause']
1054
+ if node['op'] == 3
1055
+ output << deparse_item(node['larg'])
1056
+ output << 'EXCEPT'
1057
+ output << deparse_item(node['rarg'])
1058
+ output.join(' ')
1059
+ end
774
1060
 
775
1061
  if node[TARGET_LIST_FIELD]
776
1062
  output << 'SELECT'
@@ -784,6 +1070,11 @@ class PgQuery
784
1070
  output << node[TARGET_LIST_FIELD].map do |item|
785
1071
  deparse_item(item, :select)
786
1072
  end.join(', ')
1073
+
1074
+ if node['intoClause']
1075
+ output << 'INTO'
1076
+ output << deparse_item(node['intoClause'])
1077
+ end
787
1078
  end
788
1079
 
789
1080
  if node[FROM_CLAUSE_FIELD]
@@ -882,6 +1173,13 @@ class PgQuery
882
1173
 
883
1174
  output << deparse_item(node['selectStmt'])
884
1175
 
1176
+ if node['returningList']
1177
+ output << 'RETURNING'
1178
+ output << node['returningList'].map do |column|
1179
+ deparse_item(column, :select)
1180
+ end.join(', ')
1181
+ end
1182
+
885
1183
  output.join(' ')
886
1184
  end
887
1185
 
@@ -920,7 +1218,8 @@ class PgQuery
920
1218
  if deparse_item(node['typeName']) == 'boolean'
921
1219
  deparse_item(node['arg']) == "'t'" ? 'true' : 'false'
922
1220
  else
923
- deparse_item(node['arg']) + '::' + deparse_typename(node['typeName'][TYPE_NAME])
1221
+ context = true if node['arg']['A_Expr']
1222
+ deparse_item(node['arg'], context) + '::' + deparse_typename(node['typeName'][TYPE_NAME])
924
1223
  end
925
1224
  end
926
1225
 
@@ -950,7 +1249,7 @@ class PgQuery
950
1249
  # Just pass along any custom types.
951
1250
  # (The pg_catalog types are built-in Postgres system types and are
952
1251
  # handled in the case statement below)
953
- return names.join('.') if catalog != 'pg_catalog'
1252
+ return names.join('.') + (arguments.nil? ? '' : "(#{arguments})") if catalog != 'pg_catalog'
954
1253
 
955
1254
  case type
956
1255
  when 'bpchar'
@@ -972,7 +1271,7 @@ class PgQuery
972
1271
  when 'real', 'float4'
973
1272
  'real'
974
1273
  when 'float8'
975
- 'double'
1274
+ 'double precision'
976
1275
  when 'time'
977
1276
  'time'
978
1277
  when 'timetz'
@@ -1055,6 +1354,57 @@ class PgQuery
1055
1354
  end
1056
1355
  end
1057
1356
 
1357
+ def deparse_define_stmt(node)
1358
+ dispatch = {
1359
+ 1 => :deparse_create_aggregate,
1360
+ 25 => :deparse_create_operator,
1361
+ 45 => :deparse_create_type
1362
+ }
1363
+ method(dispatch.fetch(node['kind'])).call(node)
1364
+ end
1365
+
1366
+ def deparse_create_aggregate(node)
1367
+ output = ['CREATE AGGREGATE']
1368
+ output << node['defnames'].map(&method(:deparse_item))
1369
+ args = node['args'][0] || [{ A_STAR => nil }]
1370
+ output << "(#{args.map(&method(:deparse_item)).join(', ')})"
1371
+ definitions = node['definition'].map do |definition|
1372
+ definition_output = [definition['DefElem']['defname']]
1373
+ definition_output << definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item)).join(', ') if definition['DefElem'].key?('arg')
1374
+ definition_output.join('=')
1375
+ end
1376
+ output << "(#{definitions.join(', ')})"
1377
+ output.join(' ')
1378
+ end
1379
+
1380
+ def deparse_create_operator(node)
1381
+ output = ['CREATE OPERATOR']
1382
+ output << node['defnames'][0]['String']['str']
1383
+ definitions = node['definition'].map do |definition|
1384
+ definition_output = [definition['DefElem']['defname']]
1385
+ definition_output << definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item)).join(', ') if definition['DefElem'].key?('arg')
1386
+ definition_output.join('=')
1387
+ end
1388
+ output << "(#{definitions.join(', ')})"
1389
+ output.join(' ')
1390
+ end
1391
+
1392
+ def deparse_create_type(node)
1393
+ output = ['CREATE TYPE']
1394
+ output << node['defnames'].map(&method(:deparse_item))
1395
+ if node.key?('definition')
1396
+ definitions = node['definition'].map do |definition|
1397
+ definition_output = [definition['DefElem']['defname']]
1398
+ if definition['DefElem'].key?('arg')
1399
+ definition_output += definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item))
1400
+ end
1401
+ definition_output.join('=')
1402
+ end
1403
+ output << "(#{definitions.join(', ')})"
1404
+ end
1405
+ output.join(' ')
1406
+ end
1407
+
1058
1408
  def deparse_delete_from(node)
1059
1409
  output = []
1060
1410
  output << deparse_item(node['withClause']) if node['withClause']
@@ -1085,13 +1435,25 @@ class PgQuery
1085
1435
  output.join(' ')
1086
1436
  end
1087
1437
 
1088
- def deparse_drop(node)
1438
+ def deparse_discard(node)
1439
+ output = ['DISCARD']
1440
+ output << 'ALL' if (node['target']).zero?
1441
+ output << 'PLANS' if node['target'] == 1
1442
+ output << 'SEQUENCES' if node['target'] == 2
1443
+ output << 'TEMP' if node['target'] == 3
1444
+ output.join(' ')
1445
+ end
1446
+
1447
+ def deparse_drop(node) # rubocop:disable Metrics/CyclomaticComplexity
1089
1448
  output = ['DROP']
1090
1449
  output << 'TABLE' if node['removeType'] == OBJECT_TYPE_TABLE
1450
+ output << 'SCHEMA' if node['removeType'] == OBJECT_TYPE_SCHEMA
1091
1451
  output << 'CONCURRENTLY' if node['concurrent']
1092
1452
  output << 'IF EXISTS' if node['missing_ok']
1093
1453
 
1094
- output << node['objects'].map { |list| list.map { |object| deparse_item(object) } }.join(', ')
1454
+ objects = node['objects']
1455
+ objects = [objects] unless objects[0].is_a?(Array)
1456
+ output << objects.map { |list| list.map { |object| deparse_item(object) } }.join(', ')
1095
1457
 
1096
1458
  output << 'CASCADE' if node['behavior'] == 1
1097
1459