pg_query 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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