pg_query_pg_ddm 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/extensiontask'
3
+ require 'rspec/core/rake_task'
4
+ require 'rubocop/rake_task'
5
+
6
+ Rake::ExtensionTask.new 'pg_query' do |ext|
7
+ ext.lib_dir = 'lib/pg_query'
8
+ end
9
+
10
+ RSpec::Core::RakeTask.new
11
+ RuboCop::RakeTask.new
12
+
13
+ task spec: :compile
14
+
15
+ task default: %i[lint spec]
16
+ task test: :spec
17
+ task lint: :rubocop
18
+
19
+ task :clean do
20
+ FileUtils.rm_rf File.join(__dir__, 'tmp/')
21
+ FileUtils.rm_f Dir.glob(File.join(__dir__, 'ext/pg_query/*.o'))
22
+ FileUtils.rm_f File.join(__dir__, 'lib/pg_query/pg_query.bundle')
23
+ end
@@ -0,0 +1,46 @@
1
+ # rubocop:disable Style/GlobalVars
2
+
3
+ require 'mkmf'
4
+ require 'open-uri'
5
+
6
+ LIB_PG_QUERY_TAG = '10-1.0.1'.freeze
7
+
8
+ workdir = Dir.pwd
9
+ libdir = File.join(workdir, 'libpg_query-' + LIB_PG_QUERY_TAG)
10
+ gemdir = File.join(__dir__, '../..')
11
+ libfile = libdir + '/libpg_query.a'
12
+
13
+ unless File.exist?("#{workdir}/libpg_query.tar.gz")
14
+ File.open("#{workdir}/libpg_query.tar.gz", 'wb') do |target_file|
15
+ open('https://codeload.github.com/lfittl/libpg_query/tar.gz/' + LIB_PG_QUERY_TAG, 'rb') do |read_file|
16
+ target_file.write(read_file.read)
17
+ end
18
+ end
19
+ end
20
+
21
+ unless Dir.exist?(libdir)
22
+ system("tar -xzf #{workdir}/libpg_query.tar.gz") || raise('ERROR')
23
+ end
24
+
25
+ unless Dir.exist?(libfile)
26
+ # Build libpg_query (and parts of PostgreSQL)
27
+ system("cd #{libdir}; #{ENV['MAKE'] || (RUBY_PLATFORM =~ /bsd/ ? 'gmake' : 'make')} build")
28
+ end
29
+
30
+ # Copy test files (this intentionally overwrites existing files!)
31
+ system("cp #{libdir}/testdata/* #{gemdir}/spec/files/")
32
+
33
+ $objs = ['pg_query_ruby.o']
34
+
35
+ $LOCAL_LIBS << '-lpg_query'
36
+ $LIBPATH << libdir
37
+ $CFLAGS << " -I #{libdir} -O3 -Wall -fno-strict-aliasing -fwrapv -g"
38
+
39
+ SYMFILE = File.join(__dir__, 'pg_query_ruby.sym')
40
+ if RUBY_PLATFORM =~ /darwin/
41
+ $DLDFLAGS << " -Wl,-exported_symbols_list #{SYMFILE}" unless defined?(::Rubinius)
42
+ else
43
+ $DLDFLAGS << " -Wl,--retain-symbols-file=#{SYMFILE}"
44
+ end
45
+
46
+ create_makefile 'pg_query/pg_query'
@@ -0,0 +1,129 @@
1
+ #include "pg_query_ruby.h"
2
+
3
+ void raise_ruby_parse_error(PgQueryParseResult result);
4
+ void raise_ruby_normalize_error(PgQueryNormalizeResult result);
5
+ void raise_ruby_fingerprint_error(PgQueryFingerprintResult result);
6
+
7
+ VALUE pg_query_ruby_parse(VALUE self, VALUE input);
8
+ VALUE pg_query_ruby_normalize(VALUE self, VALUE input);
9
+ VALUE pg_query_ruby_fingerprint(VALUE self, VALUE input);
10
+
11
+ void Init_pg_query(void)
12
+ {
13
+ VALUE cPgQuery;
14
+
15
+ cPgQuery = rb_const_get(rb_cObject, rb_intern("PgQuery"));
16
+
17
+ rb_define_singleton_method(cPgQuery, "_raw_parse", pg_query_ruby_parse, 1);
18
+ rb_define_singleton_method(cPgQuery, "normalize", pg_query_ruby_normalize, 1);
19
+ rb_define_singleton_method(cPgQuery, "fingerprint", pg_query_ruby_fingerprint, 1);
20
+ }
21
+
22
+ void raise_ruby_parse_error(PgQueryParseResult result)
23
+ {
24
+ VALUE cPgQuery, cParseError;
25
+ VALUE args[4];
26
+
27
+ cPgQuery = rb_const_get(rb_cObject, rb_intern("PgQuery"));
28
+ cParseError = rb_const_get_at(cPgQuery, rb_intern("ParseError"));
29
+
30
+ args[0] = rb_str_new2(result.error->message);
31
+ args[1] = rb_str_new2(result.error->filename);
32
+ args[2] = INT2NUM(result.error->lineno);
33
+ args[3] = INT2NUM(result.error->cursorpos);
34
+
35
+ pg_query_free_parse_result(result);
36
+
37
+ rb_exc_raise(rb_class_new_instance(4, args, cParseError));
38
+ }
39
+
40
+ void raise_ruby_normalize_error(PgQueryNormalizeResult result)
41
+ {
42
+ VALUE cPgQuery, cParseError;
43
+ VALUE args[4];
44
+
45
+ cPgQuery = rb_const_get(rb_cObject, rb_intern("PgQuery"));
46
+ cParseError = rb_const_get_at(cPgQuery, rb_intern("ParseError"));
47
+
48
+ args[0] = rb_str_new2(result.error->message);
49
+ args[1] = rb_str_new2(result.error->filename);
50
+ args[2] = INT2NUM(result.error->lineno);
51
+ args[3] = INT2NUM(result.error->cursorpos);
52
+
53
+ pg_query_free_normalize_result(result);
54
+
55
+ rb_exc_raise(rb_class_new_instance(4, args, cParseError));
56
+ }
57
+
58
+ void raise_ruby_fingerprint_error(PgQueryFingerprintResult result)
59
+ {
60
+ VALUE cPgQuery, cParseError;
61
+ VALUE args[4];
62
+
63
+ cPgQuery = rb_const_get(rb_cObject, rb_intern("PgQuery"));
64
+ cParseError = rb_const_get_at(cPgQuery, rb_intern("ParseError"));
65
+
66
+ args[0] = rb_str_new2(result.error->message);
67
+ args[1] = rb_str_new2(result.error->filename);
68
+ args[2] = INT2NUM(result.error->lineno);
69
+ args[3] = INT2NUM(result.error->cursorpos);
70
+
71
+ pg_query_free_fingerprint_result(result);
72
+
73
+ rb_exc_raise(rb_class_new_instance(4, args, cParseError));
74
+ }
75
+
76
+ VALUE pg_query_ruby_parse(VALUE self, VALUE input)
77
+ {
78
+ Check_Type(input, T_STRING);
79
+
80
+ VALUE output;
81
+ PgQueryParseResult result = pg_query_parse(StringValueCStr(input));
82
+
83
+ if (result.error) raise_ruby_parse_error(result);
84
+
85
+ output = rb_ary_new();
86
+
87
+ rb_ary_push(output, rb_str_new2(result.parse_tree));
88
+ rb_ary_push(output, rb_str_new2(result.stderr_buffer));
89
+
90
+ pg_query_free_parse_result(result);
91
+
92
+ return output;
93
+ }
94
+
95
+ VALUE pg_query_ruby_normalize(VALUE self, VALUE input)
96
+ {
97
+ Check_Type(input, T_STRING);
98
+
99
+ VALUE output;
100
+ PgQueryNormalizeResult result = pg_query_normalize(StringValueCStr(input));
101
+
102
+ if (result.error) raise_ruby_normalize_error(result);
103
+
104
+ output = rb_str_new2(result.normalized_query);
105
+
106
+ pg_query_free_normalize_result(result);
107
+
108
+ return output;
109
+ }
110
+
111
+ VALUE pg_query_ruby_fingerprint(VALUE self, VALUE input)
112
+ {
113
+ Check_Type(input, T_STRING);
114
+
115
+ VALUE output;
116
+ PgQueryFingerprintResult result = pg_query_fingerprint(StringValueCStr(input));
117
+
118
+ if (result.error) raise_ruby_fingerprint_error(result);
119
+
120
+ if (result.hexdigest) {
121
+ output = rb_str_new2(result.hexdigest);
122
+ } else {
123
+ output = Qnil;
124
+ }
125
+
126
+ pg_query_free_fingerprint_result(result);
127
+
128
+ return output;
129
+ }
@@ -0,0 +1,10 @@
1
+ #ifndef PG_QUERY_RUBY_H
2
+ #define PG_QUERY_RUBY_H
3
+
4
+ #include "pg_query.h"
5
+
6
+ #include <ruby.h>
7
+
8
+ void Init_pg_query(void);
9
+
10
+ #endif
@@ -0,0 +1 @@
1
+ _Init_pg_query
@@ -0,0 +1,16 @@
1
+ require 'pg_query/version'
2
+ require 'pg_query/parse_error'
3
+
4
+ require 'pg_query/pg_query'
5
+ require 'pg_query/parse'
6
+ require 'pg_query/treewalker'
7
+ require 'pg_query/node_types'
8
+ require 'pg_query/deep_dup'
9
+
10
+ require 'pg_query/legacy_parsetree'
11
+
12
+ require 'pg_query/filter_columns'
13
+ require 'pg_query/fingerprint'
14
+ require 'pg_query/param_refs'
15
+ require 'pg_query/deparse'
16
+ require 'pg_query/truncate'
@@ -0,0 +1,16 @@
1
+ class PgQuery
2
+ def deep_dup(obj)
3
+ case obj
4
+ when Hash
5
+ obj.each_with_object(obj.dup) do |(key, value), hash|
6
+ hash[deep_dup(key)] = deep_dup(value)
7
+ end
8
+ when Array
9
+ obj.map { |it| deep_dup(it) }
10
+ when NilClass, FalseClass, TrueClass, Symbol, Numeric
11
+ obj # Can't be duplicated
12
+ else
13
+ obj.dup
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,1658 @@
1
+ require_relative 'deparse/alter_table'
2
+ require_relative 'deparse/rename'
3
+ require_relative 'deparse/interval'
4
+ require_relative 'deparse/keywords'
5
+
6
+ class PgQuery
7
+ # 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('; ')
12
+ end
13
+
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?(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) } - ['pg_catalog']).join('.')
421
+ if name == 'overlay'
422
+ output << format('%s(%s placing %s from %s for %s)', name, args[0], args[1], args[2], args[3])
423
+ else
424
+ distinct = node['agg_distinct'] ? 'DISTINCT ' : ''
425
+ output << format('%s(%s%s)', name, distinct, args.join(', '))
426
+ output << format('FILTER (WHERE %s)', deparse_item(node['agg_filter'])) if node['agg_filter']
427
+ output << format('OVER %s', deparse_item(node['over'])) if node['over']
428
+ end
429
+
430
+ output.join(' ')
431
+ end
432
+
433
+ def deparse_windowdef(node)
434
+ return deparse_identifier(node['name']) if node['name']
435
+
436
+ output = []
437
+
438
+ if node['partitionClause']
439
+ output << 'PARTITION BY'
440
+ output << node['partitionClause'].map do |item|
441
+ deparse_item(item)
442
+ end.join(', ')
443
+ end
444
+
445
+ if node['orderClause']
446
+ output << 'ORDER BY'
447
+ output << node['orderClause'].map do |item|
448
+ deparse_item(item)
449
+ end.join(', ')
450
+ end
451
+
452
+ format('(%s)', output.join(' '))
453
+ end
454
+
455
+ def deparse_functionparameter(node)
456
+ deparse_item(node['argType'])
457
+ end
458
+
459
+ def deparse_grant_role(node)
460
+ output = []
461
+ output << ['GRANT'] if node['is_grant']
462
+ output << ['REVOKE'] unless node['is_grant']
463
+ output << node['granted_roles'].map(&method(:deparse_item)).join(', ')
464
+ output << ['TO'] if node['is_grant']
465
+ output << ['FROM'] unless node['is_grant']
466
+ output << node['grantee_roles'].map(&method(:deparse_item)).join(', ')
467
+ output << 'WITH ADMIN OPTION' if node['admin_opt']
468
+ output.join(' ')
469
+ end
470
+
471
+ def deparse_grant(node) # rubocop:disable Metrics/CyclomaticComplexity
472
+ objtype, allow_all = deparse_grant_objtype(node)
473
+ output = []
474
+ output << ['GRANT'] if node['is_grant']
475
+ output << ['REVOKE'] unless node['is_grant']
476
+ output << if node.key?('privileges')
477
+ node['privileges'].map(&method(:deparse_item)).join(', ')
478
+ else
479
+ 'ALL'
480
+ end
481
+ output << 'ON'
482
+ objects = node['objects']
483
+ objects = objects[0] if %w[DOMAIN TYPE].include?(objtype)
484
+ objects = objects.map do |object|
485
+ deparsed = deparse_item(object)
486
+ if object.key?(RANGE_VAR) || object.key?(OBJECT_WITH_ARGS) || !allow_all
487
+ objtype == 'TABLE' ? deparsed : "#{objtype} #{deparsed}"
488
+ else
489
+ "ALL #{objtype}S IN SCHEMA #{deparsed}"
490
+ end
491
+ end
492
+ output << objects.join(', ')
493
+ output << ['TO'] if node['is_grant']
494
+ output << ['FROM'] unless node['is_grant']
495
+ output << node['grantees'].map(&method(:deparse_item)).join(', ')
496
+ output << 'WITH GRANT OPTION' if node['grant_option']
497
+ output.join(' ')
498
+ end
499
+
500
+ def deparse_grant_objtype(node)
501
+ {
502
+ 1 => ['TABLE', true],
503
+ 2 => ['SEQUENCE', true],
504
+ 3 => ['DATABASE', false],
505
+ 4 => ['DOMAIN', false],
506
+ 5 => ['FOREIGN DATA WRAPPER', false],
507
+ 6 => ['FOREIGN SERVER', false],
508
+ 7 => ['FUNCTION', true],
509
+ 8 => ['LANGUAGE', false],
510
+ 9 => ['LARGE OBJECT', false],
511
+ 10 => ['SCHEMA', false],
512
+ 11 => ['TABLESPACE', false],
513
+ 12 => ['TYPE', false]
514
+ }.fetch(node['objtype'])
515
+ end
516
+
517
+ def deparse_access_priv(node)
518
+ output = [node['priv_name']]
519
+ output << "(#{node['cols'].map(&method(:deparse_item)).join(', ')})" if node.key?('cols')
520
+ output.join(' ')
521
+ end
522
+
523
+ def deparse_role_spec(node)
524
+ return 'CURRENT_USER' if node['roletype'] == 1
525
+ return 'SESSION_USER' if node['roletype'] == 2
526
+ return 'PUBLIC' if node['roletype'] == 3
527
+ deparse_identifier(node['rolename'], escape_always: true)
528
+ end
529
+
530
+ def deparse_aexpr_in(node)
531
+ rexpr = Array(node['rexpr']).map { |arg| deparse_item(arg) }
532
+ operator = node['name'].map { |n| deparse_item(n, :operator) } == ['='] ? 'IN' : 'NOT IN'
533
+ format('%s %s (%s)', deparse_item(node['lexpr']), operator, rexpr.join(', '))
534
+ end
535
+
536
+ def deparse_aexpr_like(node)
537
+ value = deparse_item(node['rexpr'])
538
+ operator = node['name'].map { |n| deparse_item(n, :operator) } == ['~~'] ? 'LIKE' : 'NOT LIKE'
539
+ format('%s %s %s', deparse_item(node['lexpr']), operator, value)
540
+ end
541
+
542
+ def deparse_aexpr_ilike(node)
543
+ value = deparse_item(node['rexpr'])
544
+ operator = node['name'][0]['String']['str'] == '~~*' ? 'ILIKE' : 'NOT ILIKE'
545
+ format('%s %s %s', deparse_item(node['lexpr']), operator, value)
546
+ end
547
+
548
+ def deparse_bool_expr_not(node)
549
+ format('NOT %s', deparse_item(node['args'][0]))
550
+ end
551
+
552
+ BOOLEAN_TEST_TYPE_TO_STRING = {
553
+ BOOLEAN_TEST_TRUE => ' IS TRUE',
554
+ BOOLEAN_TEST_NOT_TRUE => ' IS NOT TRUE',
555
+ BOOLEAN_TEST_FALSE => ' IS FALSE',
556
+ BOOLEAN_TEST_NOT_FALSE => ' IS NOT FALSE',
557
+ BOOLEAN_TEST_UNKNOWN => ' IS UNKNOWN',
558
+ BOOLEAN_TEST_NOT_UNKNOWN => ' IS NOT UNKNOWN'
559
+ }.freeze
560
+ def deparse_boolean_test(node)
561
+ deparse_item(node['arg']) + BOOLEAN_TEST_TYPE_TO_STRING[node['booltesttype']]
562
+ end
563
+
564
+ def deparse_range_function(node)
565
+ output = []
566
+ output << 'LATERAL' if node['lateral']
567
+ output << deparse_item(node['functions'][0][0]) # FIXME: Needs more test cases
568
+ output << deparse_item(node['alias']) if node['alias']
569
+ output << "#{node['alias'] ? '' : 'AS '}(#{deparse_item_list(node['coldeflist']).join(', ')})" if node['coldeflist']
570
+ output.join(' ')
571
+ end
572
+
573
+ def deparse_aexpr(node, context = false)
574
+ output = []
575
+ output << deparse_item(node['lexpr'], context || true)
576
+ output << deparse_item(node['rexpr'], context || true)
577
+ output = output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
578
+ if context
579
+ # This is a nested expression, add parentheses.
580
+ output = '(' + output + ')'
581
+ end
582
+ output
583
+ end
584
+
585
+ def deparse_bool_expr_and(node)
586
+ # Only put parantheses around OR nodes that are inside this one
587
+ node['args'].map do |arg|
588
+ if [BOOL_EXPR_OR].include?(arg.values[0]['boolop'])
589
+ format('(%s)', deparse_item(arg))
590
+ else
591
+ deparse_item(arg)
592
+ end
593
+ end.join(' AND ')
594
+ end
595
+
596
+ def deparse_bool_expr_or(node)
597
+ # Put parantheses around AND + OR nodes that are inside
598
+ node['args'].map do |arg|
599
+ if [BOOL_EXPR_AND, BOOL_EXPR_OR].include?(arg.values[0]['boolop'])
600
+ format('(%s)', deparse_item(arg))
601
+ else
602
+ deparse_item(arg)
603
+ end
604
+ end.join(' OR ')
605
+ end
606
+
607
+ def deparse_aexpr_any(node)
608
+ output = []
609
+ output << deparse_item(node['lexpr'])
610
+ output << format('ANY(%s)', deparse_item(node['rexpr']))
611
+ output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
612
+ end
613
+
614
+ def deparse_aexpr_all(node)
615
+ output = []
616
+ output << deparse_item(node['lexpr'])
617
+ output << format('ALL(%s)', deparse_item(node['rexpr']))
618
+ output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
619
+ end
620
+
621
+ def deparse_aexpr_between(node)
622
+ between = case node['kind']
623
+ when AEXPR_BETWEEN
624
+ ' BETWEEN '
625
+ when AEXPR_NOT_BETWEEN
626
+ ' NOT BETWEEN '
627
+ when AEXPR_BETWEEN_SYM
628
+ ' BETWEEN SYMMETRIC '
629
+ when AEXPR_NOT_BETWEEN_SYM
630
+ ' NOT BETWEEN SYMMETRIC '
631
+ end
632
+ name = deparse_item(node['lexpr'])
633
+ output = node['rexpr'].map { |n| deparse_item(n) }
634
+ name << between << output.join(' AND ')
635
+ end
636
+
637
+ def deparse_aexpr_nullif(node)
638
+ lexpr = deparse_item(node['lexpr'])
639
+ rexpr = deparse_item(node['rexpr'])
640
+ format('NULLIF(%s, %s)', lexpr, rexpr)
641
+ end
642
+
643
+ def deparse_joinexpr(node) # rubocop:disable Metrics/CyclomaticComplexity
644
+ output = []
645
+ output << deparse_item(node['larg'])
646
+ case node['jointype']
647
+ when 0
648
+ if node['isNatural']
649
+ output << 'NATURAL'
650
+ elsif node['quals'].nil? && node['usingClause'].nil?
651
+ output << 'CROSS'
652
+ end
653
+ when 1
654
+ output << 'LEFT'
655
+ when 2
656
+ output << 'FULL'
657
+ when 3
658
+ output << 'RIGHT'
659
+ end
660
+ output << 'JOIN'
661
+ output << deparse_item(node['rarg'])
662
+
663
+ if node['quals']
664
+ output << 'ON'
665
+ output << deparse_item(node['quals'])
666
+ end
667
+
668
+ output << format('USING (%s)', node['usingClause'].map { |n| deparse_item(n) }.join(', ')) if node['usingClause']
669
+
670
+ output.join(' ')
671
+ end
672
+
673
+ def deparse_lock(node)
674
+ output = []
675
+ output << 'LOCK TABLE'
676
+ tables = node['relations'].map { |table| deparse_item(table) }
677
+ output << tables.join(', ')
678
+ output.join(' ')
679
+ end
680
+
681
+ LOCK_CLAUSE_STRENGTH = {
682
+ LCS_FORKEYSHARE => 'FOR KEY SHARE',
683
+ LCS_FORSHARE => 'FOR SHARE',
684
+ LCS_FORNOKEYUPDATE => 'FOR NO KEY UPDATE',
685
+ LCS_FORUPDATE => 'FOR UPDATE'
686
+ }.freeze
687
+ def deparse_lockingclause(node)
688
+ output = []
689
+ output << LOCK_CLAUSE_STRENGTH[node['strength']]
690
+ if node['lockedRels']
691
+ output << 'OF'
692
+ output << node['lockedRels'].map do |item|
693
+ deparse_item(item)
694
+ end.join(', ')
695
+ end
696
+ output.join(' ')
697
+ end
698
+
699
+ def deparse_sortby(node)
700
+ output = []
701
+ output << deparse_item(node['node'])
702
+ output << 'ASC' if node['sortby_dir'] == 1
703
+ output << 'DESC' if node['sortby_dir'] == 2
704
+ output << 'NULLS FIRST' if node['sortby_nulls'] == 1
705
+ output << 'NULLS LAST' if node['sortby_nulls'] == 2
706
+ output.join(' ')
707
+ end
708
+
709
+ def deparse_collate(node)
710
+ output = []
711
+ output << deparse_item(node['arg'])
712
+ output << 'COLLATE'
713
+ output << deparse_item_list(node['collname'])
714
+ output.join(' ')
715
+ end
716
+
717
+ def deparse_with_clause(node)
718
+ output = ['WITH']
719
+ output << 'RECURSIVE' if node['recursive']
720
+ output << node['ctes'].map do |cte|
721
+ deparse_item(cte)
722
+ end.join(', ')
723
+ output.join(' ')
724
+ end
725
+
726
+ def deparse_viewstmt(node)
727
+ output = []
728
+ output << 'CREATE'
729
+ output << 'OR REPLACE' if node['replace']
730
+
731
+ persistence = relpersistence(node['view'])
732
+ output << persistence if persistence
733
+
734
+ output << 'VIEW'
735
+ output << node['view'][RANGE_VAR]['relname']
736
+ output << format('(%s)', deparse_item_list(node['aliases']).join(', ')) if node['aliases']
737
+
738
+ output << 'AS'
739
+ output << deparse_item(node['query'])
740
+
741
+ case node['withCheckOption']
742
+ when 1
743
+ output << 'WITH CHECK OPTION'
744
+ when 2
745
+ output << 'WITH CASCADED CHECK OPTION'
746
+ end
747
+ output.join(' ')
748
+ end
749
+
750
+ def deparse_variable_set_stmt(node)
751
+ output = []
752
+ output << 'SET'
753
+ output << 'LOCAL' if node['is_local']
754
+ output << node['name']
755
+ output << 'TO'
756
+ output << node['args'].map { |arg| deparse_item(arg) }.join(', ')
757
+ output.join(' ')
758
+ end
759
+
760
+ def deparse_vacuum_stmt(node)
761
+ output = []
762
+ output << 'VACUUM'
763
+ output.concat(deparse_vacuum_options(node))
764
+ output << deparse_item(node['relation']) if node.key?('relation')
765
+ if node.key?('va_cols')
766
+ output << "(#{node['va_cols'].map(&method(:deparse_item)).join(', ')})"
767
+ end
768
+ output.join(' ')
769
+ end
770
+
771
+ def deparse_vacuum_options(node)
772
+ output = []
773
+ output << 'FULL' if node['options'][4] == 1
774
+ output << 'FREEZE' if node['options'][3] == 1
775
+ output << 'VERBOSE' if node['options'][2] == 1
776
+ output << 'ANALYZE' if node['options'][1] == 1
777
+ output
778
+ end
779
+
780
+ def deparse_do_stmt(node)
781
+ output = []
782
+ output << 'DO'
783
+ statement, *rest = node['args']
784
+ output << "$$#{statement['DefElem']['arg']['String']['str']}$$"
785
+ output += rest.map { |item| deparse_item(item) }
786
+ output.join(' ')
787
+ end
788
+
789
+ def deparse_cte(node)
790
+ output = []
791
+ output << node['ctename']
792
+ output << format('(%s)', node['aliascolnames'].map { |n| deparse_item(n) }.join(', ')) if node['aliascolnames']
793
+ output << format('AS (%s)', deparse_item(node['ctequery']))
794
+ output.join(' ')
795
+ end
796
+
797
+ def deparse_case(node)
798
+ output = ['CASE']
799
+ output << deparse_item(node['arg']) if node['arg']
800
+ output += node['args'].map { |arg| deparse_item(arg) }
801
+ if node['defresult']
802
+ output << 'ELSE'
803
+ output << deparse_item(node['defresult'])
804
+ end
805
+ output << 'END'
806
+ output.join(' ')
807
+ end
808
+
809
+ def deparse_columndef(node)
810
+ output = [node['colname']]
811
+ output << deparse_item(node['typeName'])
812
+ if node['raw_default']
813
+ output << 'USING'
814
+ output << deparse_item(node['raw_default'])
815
+ end
816
+ if node['constraints']
817
+ output += node['constraints'].map do |item|
818
+ deparse_item(item)
819
+ end
820
+ end
821
+ if node['collClause']
822
+ output << 'COLLATE'
823
+ output += node['collClause']['CollateClause']['collname'].map(&method(:deparse_item))
824
+ end
825
+ output.compact.join(' ')
826
+ end
827
+
828
+ def deparse_composite_type(node)
829
+ output = ['CREATE TYPE']
830
+ output << deparse_rangevar(node['typevar'][RANGE_VAR].merge('inh' => true))
831
+ output << 'AS'
832
+ coldeflist = node['coldeflist'].map(&method(:deparse_item))
833
+ output << "(#{coldeflist.join(', ')})"
834
+ output.join(' ')
835
+ end
836
+
837
+ def deparse_constraint(node) # rubocop:disable Metrics/CyclomaticComplexity
838
+ output = []
839
+ if node['conname']
840
+ output << 'CONSTRAINT'
841
+ output << node['conname']
842
+ end
843
+ case node['contype']
844
+ when CONSTR_TYPE_NULL
845
+ output << 'NULL'
846
+ when CONSTR_TYPE_NOTNULL
847
+ output << 'NOT NULL'
848
+ when CONSTR_TYPE_DEFAULT
849
+ output << 'DEFAULT'
850
+ when CONSTR_TYPE_CHECK
851
+ output << 'CHECK'
852
+ when CONSTR_TYPE_PRIMARY
853
+ output << 'PRIMARY KEY'
854
+ when CONSTR_TYPE_UNIQUE
855
+ output << 'UNIQUE'
856
+ when CONSTR_TYPE_EXCLUSION
857
+ output << 'EXCLUSION'
858
+ when CONSTR_TYPE_FOREIGN
859
+ output << 'FOREIGN KEY'
860
+ end
861
+
862
+ if node['raw_expr']
863
+ expression = deparse_item(node['raw_expr'])
864
+ # Unless it's simple, put parentheses around it
865
+ expression = '(' + expression + ')' if node['raw_expr'][BOOL_EXPR]
866
+ expression = '(' + expression + ')' if node['raw_expr'][A_EXPR] && node['raw_expr'][A_EXPR]['kind'] == AEXPR_OP
867
+ output << expression
868
+ end
869
+ output << '(' + deparse_item_list(node['keys']).join(', ') + ')' if node['keys']
870
+ output << '(' + deparse_item_list(node['fk_attrs']).join(', ') + ')' if node['fk_attrs']
871
+ output << 'REFERENCES ' + deparse_item(node['pktable']) + ' (' + deparse_item_list(node['pk_attrs']).join(', ') + ')' if node['pktable']
872
+ output << 'NOT VALID' if node['skip_validation']
873
+ output << "USING INDEX #{node['indexname']}" if node['indexname']
874
+ output.join(' ')
875
+ end
876
+
877
+ def deparse_copy(node)
878
+ output = ['COPY']
879
+ if node.key?('relation')
880
+ output << deparse_item(node['relation'])
881
+ elsif node.key?('query')
882
+ output << "(#{deparse_item(node['query'])})"
883
+ end
884
+ columns = node.fetch('attlist', []).map { |column| deparse_item(column) }
885
+ output << "(#{columns.join(', ')})" unless columns.empty?
886
+ output << (node['is_from'] ? 'FROM' : 'TO')
887
+ output << 'PROGRAM' if node['is_program']
888
+ output << deparse_copy_output(node)
889
+ output.join(' ')
890
+ end
891
+
892
+ def deparse_copy_output(node)
893
+ return "'#{node['filename']}'" if node.key?('filename')
894
+ return 'STDIN' if node['is_from']
895
+ 'STDOUT'
896
+ end
897
+
898
+ def deparse_create_enum(node)
899
+ output = ['CREATE TYPE']
900
+ output << deparse_item(node['typeName'][0])
901
+ output << 'AS ENUM'
902
+ vals = node['vals'].map { |val| deparse_item(val, A_CONST) }
903
+ output << "(#{vals.join(', ')})"
904
+ output.join(' ')
905
+ end
906
+
907
+ def deparse_create_cast(node)
908
+ output = []
909
+ output << 'CREATE'
910
+ output << 'CAST'
911
+ output << format('(%s AS %s)', deparse_item(node['sourcetype']), deparse_item(node['targettype']))
912
+ output << if node['func']
913
+ function = node['func']['ObjectWithArgs']
914
+ name = deparse_item_list(function['objname']).join('.')
915
+ arguments = deparse_item_list(function['objargs']).join(', ')
916
+ format('WITH FUNCTION %s(%s)', name, arguments)
917
+ elsif node['inout']
918
+ 'WITH INOUT'
919
+ else
920
+ 'WITHOUT FUNCTION'
921
+ end
922
+ output << 'AS IMPLICIT' if (node['context']).zero?
923
+ output << 'AS ASSIGNMENT' if node['context'] == 1
924
+ output.join(' ')
925
+ end
926
+
927
+ def deparse_create_domain(node)
928
+ output = []
929
+ output << 'CREATE'
930
+ output << 'DOMAIN'
931
+ output << deparse_item_list(node['domainname']).join('.')
932
+ output << 'AS'
933
+ output << deparse_item(node['typeName']) if node['typeName']
934
+ output << deparse_item(node['collClause']) if node['collClause']
935
+ output << deparse_item_list(node['constraints'])
936
+ output.join(' ')
937
+ end
938
+
939
+ def deparse_create_function(node)
940
+ output = []
941
+ output << 'CREATE'
942
+ output << 'OR REPLACE' if node['replace']
943
+ output << 'FUNCTION'
944
+
945
+ arguments = deparse_item_list(node.fetch('parameters', [])).join(', ')
946
+
947
+ output << deparse_item_list(node['funcname']).join('.') + '(' + arguments + ')'
948
+
949
+ output << 'RETURNS'
950
+ output << deparse_item(node['returnType'])
951
+ output += node['options'].map { |item| deparse_item(item) }
952
+
953
+ output.join(' ')
954
+ end
955
+
956
+ def deparse_create_range(node)
957
+ output = ['CREATE TYPE']
958
+ output << deparse_item(node['typeName'][0])
959
+ output << 'AS RANGE'
960
+ params = node['params'].map do |param|
961
+ param_out = [param['DefElem']['defname']]
962
+ if param['DefElem'].key?('arg')
963
+ param_out << deparse_item(param['DefElem']['arg'])
964
+ end
965
+ param_out.join('=')
966
+ end
967
+ output << "(#{params.join(', ')})"
968
+ output.join(' ')
969
+ end
970
+
971
+ def deparse_create_schema(node)
972
+ output = ['CREATE SCHEMA']
973
+ output << 'IF NOT EXISTS' if node['if_not_exists']
974
+ output << deparse_identifier(node['schemaname']) if node.key?('schemaname')
975
+ output << format('AUTHORIZATION %s', deparse_item(node['authrole'])) if node.key?('authrole')
976
+ output << deparse_item_list(node['schemaElts']) if node.key?('schemaElts')
977
+ output.join(' ')
978
+ end
979
+
980
+ def deparse_create_table(node)
981
+ output = []
982
+ output << 'CREATE'
983
+
984
+ persistence = relpersistence(node['relation'])
985
+ output << persistence if persistence
986
+
987
+ output << 'TABLE'
988
+
989
+ output << 'IF NOT EXISTS' if node['if_not_exists']
990
+
991
+ output << deparse_item(node['relation'])
992
+
993
+ output << '(' + node['tableElts'].map do |item|
994
+ deparse_item(item)
995
+ end.join(', ') + ')'
996
+
997
+ if node['inhRelations']
998
+ output << 'INHERITS'
999
+ output << '(' + node['inhRelations'].map do |relation|
1000
+ deparse_item(relation)
1001
+ end.join(', ') + ')'
1002
+ end
1003
+
1004
+ output.join(' ')
1005
+ end
1006
+
1007
+ def deparse_create_table_as(node)
1008
+ output = []
1009
+ output << 'CREATE'
1010
+
1011
+ into = node['into']['IntoClause']
1012
+ persistence = relpersistence(into['rel'])
1013
+ output << persistence if persistence
1014
+
1015
+ output << 'TABLE'
1016
+
1017
+ output << deparse_item(node['into'])
1018
+
1019
+ if into['onCommit'] > 0
1020
+ output << 'ON COMMIT'
1021
+ output << 'DELETE ROWS' if into['onCommit'] == 2
1022
+ output << 'DROP' if into['onCommit'] == 3
1023
+ end
1024
+
1025
+ output << 'AS'
1026
+ output << deparse_item(node['query'])
1027
+ output.join(' ')
1028
+ end
1029
+
1030
+ def deparse_execute(node)
1031
+ output = ['EXECUTE']
1032
+ output << deparse_identifier(node['name'])
1033
+ output << "(#{deparse_item_list(node['params']).join(', ')})" if node['params']
1034
+ output.join(' ')
1035
+ end
1036
+
1037
+ def deparse_into_clause(node)
1038
+ deparse_item(node['rel'])
1039
+ end
1040
+
1041
+ def deparse_when(node)
1042
+ output = ['WHEN']
1043
+ output << deparse_item(node['expr'])
1044
+ output << 'THEN'
1045
+ output << deparse_item(node['result'])
1046
+ output.join(' ')
1047
+ end
1048
+
1049
+ def deparse_sublink(node)
1050
+ if node['subLinkType'] == SUBLINK_TYPE_ANY
1051
+ format('%s IN (%s)', deparse_item(node['testexpr']), deparse_item(node['subselect']))
1052
+ elsif node['subLinkType'] == SUBLINK_TYPE_ALL
1053
+ format('%s %s ALL (%s)', deparse_item(node['testexpr']), deparse_item(node['operName'][0], :operator), deparse_item(node['subselect']))
1054
+ elsif node['subLinkType'] == SUBLINK_TYPE_EXISTS
1055
+ format('EXISTS(%s)', deparse_item(node['subselect']))
1056
+ elsif node['subLinkType'] == SUBLINK_TYPE_ARRAY
1057
+ format('ARRAY(%s)', deparse_item(node['subselect']))
1058
+ else
1059
+ format('(%s)', deparse_item(node['subselect']))
1060
+ end
1061
+ end
1062
+
1063
+ def deparse_rangesubselect(node)
1064
+ output = ''
1065
+ output = 'LATERAL ' if node['lateral']
1066
+ output = output + '(' + deparse_item(node['subquery']) + ')'
1067
+ if node['alias']
1068
+ output + ' ' + deparse_item(node['alias'])
1069
+ else
1070
+ output
1071
+ end
1072
+ end
1073
+
1074
+ def deparse_row(node)
1075
+ 'ROW(' + node['args'].map { |arg| deparse_item(arg) }.join(', ') + ')'
1076
+ end
1077
+
1078
+ def deparse_select(node) # rubocop:disable Metrics/CyclomaticComplexity
1079
+ output = []
1080
+
1081
+ output << deparse_item(node['withClause']) if node['withClause']
1082
+
1083
+ if node['op'] == 1
1084
+ output << '(' if node['larg']['SelectStmt']['sortClause']
1085
+ output << deparse_item(node['larg'])
1086
+ output << ')' if node['larg']['SelectStmt']['sortClause']
1087
+ output << 'UNION'
1088
+ output << 'ALL' if node['all']
1089
+ output << '(' if node['rarg']['SelectStmt']['sortClause']
1090
+ output << deparse_item(node['rarg'])
1091
+ output << ')' if node['rarg']['SelectStmt']['sortClause']
1092
+ output.join(' ')
1093
+ end
1094
+
1095
+ if node['op'] == 3
1096
+ output << deparse_item(node['larg'])
1097
+ output << 'EXCEPT'
1098
+ output << deparse_item(node['rarg'])
1099
+ output.join(' ')
1100
+ end
1101
+
1102
+ if node['op'] == 2
1103
+ output << deparse_item(node['larg'])
1104
+ output << 'INTERSECT'
1105
+ output << deparse_item(node['rarg'])
1106
+ output.join(' ')
1107
+ end
1108
+
1109
+ output << 'SELECT' if node[FROM_CLAUSE_FIELD] || node[TARGET_LIST_FIELD]
1110
+
1111
+ if node[TARGET_LIST_FIELD]
1112
+ if node['distinctClause']
1113
+ output << 'DISTINCT'
1114
+ unless node['distinctClause'].compact.empty?
1115
+ columns = node['distinctClause'].map { |item| deparse_item(item, :select) }
1116
+ output << "ON (#{columns.join(', ')})"
1117
+ end
1118
+ end
1119
+ output << node[TARGET_LIST_FIELD].map do |item|
1120
+ deparse_item(item, :select)
1121
+ end.join(', ')
1122
+
1123
+ if node['intoClause']
1124
+ output << 'INTO'
1125
+ output << deparse_item(node['intoClause'])
1126
+ end
1127
+ end
1128
+
1129
+ if node[FROM_CLAUSE_FIELD]
1130
+ output << 'FROM'
1131
+ output << node[FROM_CLAUSE_FIELD].map do |item|
1132
+ deparse_item(item)
1133
+ end.join(', ')
1134
+ end
1135
+
1136
+ if node['whereClause']
1137
+ output << 'WHERE'
1138
+ output << deparse_item(node['whereClause'])
1139
+ end
1140
+
1141
+ if node['valuesLists']
1142
+ output << 'VALUES'
1143
+ output << node['valuesLists'].map do |value_list|
1144
+ '(' + value_list.map { |v| deparse_item(v) }.join(', ') + ')'
1145
+ end.join(', ')
1146
+ end
1147
+
1148
+ if node['groupClause']
1149
+ output << 'GROUP BY'
1150
+ output << node['groupClause'].map do |item|
1151
+ deparse_item(item)
1152
+ end.join(', ')
1153
+ end
1154
+
1155
+ if node['havingClause']
1156
+ output << 'HAVING'
1157
+ output << deparse_item(node['havingClause'])
1158
+ end
1159
+
1160
+ if node['sortClause']
1161
+ output << 'ORDER BY'
1162
+ output << node['sortClause'].map do |item|
1163
+ deparse_item(item)
1164
+ end.join(', ')
1165
+ end
1166
+
1167
+ if node['limitCount']
1168
+ output << 'LIMIT'
1169
+ output << deparse_item(node['limitCount'])
1170
+ end
1171
+
1172
+ if node['limitOffset']
1173
+ output << 'OFFSET'
1174
+ output << deparse_item(node['limitOffset'])
1175
+ end
1176
+
1177
+ if node['lockingClause']
1178
+ node['lockingClause'].map do |item|
1179
+ output << deparse_item(item)
1180
+ end
1181
+ end
1182
+
1183
+ output.join(' ')
1184
+ end
1185
+
1186
+ def deparse_sql_value_function(node)
1187
+ output = []
1188
+ lookup = [
1189
+ 'current_date',
1190
+ 'current_time',
1191
+ 'current_time', # with precision
1192
+ 'current_timestamp',
1193
+ 'current_timestamp', # with precision
1194
+ 'localtime',
1195
+ 'localtime', # with precision
1196
+ 'localtimestamp',
1197
+ 'localtimestamp', # with precision
1198
+ 'current_role',
1199
+ 'current_user',
1200
+ 'session_user',
1201
+ 'user',
1202
+ 'current_catalog',
1203
+ 'current_schema'
1204
+ ]
1205
+ output << lookup[node['op']]
1206
+ output << "(#{node['typmod']})" unless node.fetch('typmod', -1) == -1
1207
+ output.join('')
1208
+ end
1209
+
1210
+ def deparse_insert_into(node)
1211
+ output = []
1212
+ output << deparse_item(node['withClause']) if node['withClause']
1213
+
1214
+ output << 'INSERT INTO'
1215
+ output << deparse_item(node['relation'])
1216
+
1217
+ if node['cols']
1218
+ output << '(' + node['cols'].map do |column|
1219
+ deparse_item(column, :select)
1220
+ end.join(', ') + ')'
1221
+ end
1222
+
1223
+ output << deparse_item(node['selectStmt'])
1224
+
1225
+ if node['onConflictClause']
1226
+ output << deparse_insert_onconflict(node['onConflictClause']['OnConflictClause'])
1227
+ end
1228
+
1229
+ if node['returningList']
1230
+ output << 'RETURNING'
1231
+ output << node['returningList'].map do |column|
1232
+ deparse_item(column, :select)
1233
+ end.join(', ')
1234
+ end
1235
+
1236
+ output.join(' ')
1237
+ end
1238
+
1239
+ def deparse_insert_onconflict(node) # rubocop:disable Metrics/CyclomaticComplexity
1240
+ output = []
1241
+ output << 'ON CONFLICT'
1242
+
1243
+ infer = node['infer']['InferClause']
1244
+ if infer['indexElems']
1245
+ output << '(' + infer['indexElems'].map do |column|
1246
+ '"' + column['IndexElem']['name'] + '"'
1247
+ end.join(', ') + ')'
1248
+ end
1249
+
1250
+ if infer['conname']
1251
+ output << 'ON CONSTRAINT'
1252
+ output << deparse_identifier(infer['conname'], escape_always: true)
1253
+ end
1254
+
1255
+ output << 'DO UPDATE' if node['action'] == 2
1256
+
1257
+ if node[TARGET_LIST_FIELD]
1258
+ output << 'SET ' + node[TARGET_LIST_FIELD].map do |column|
1259
+ deparse_item(column, :excluded)
1260
+ end.join(', ')
1261
+ end
1262
+
1263
+ if infer['whereClause']
1264
+ output << 'WHERE'
1265
+ output << deparse_item(infer['whereClause'])
1266
+ end
1267
+
1268
+ output << 'DO NOTHING' if node['action'] == 1
1269
+
1270
+ output.join(' ')
1271
+ end
1272
+
1273
+ def deparse_update(node)
1274
+ output = []
1275
+ output << deparse_item(node['withClause']) if node['withClause']
1276
+
1277
+ output << 'UPDATE'
1278
+ output << deparse_item(node['relation'])
1279
+
1280
+ if node[TARGET_LIST_FIELD]
1281
+ output << 'SET'
1282
+ columns = node[TARGET_LIST_FIELD].map do |item|
1283
+ deparse_item(item, :update)
1284
+ end
1285
+ output << columns.join(', ')
1286
+ end
1287
+
1288
+ if node[FROM_CLAUSE_FIELD]
1289
+ output << 'FROM'
1290
+ output << node[FROM_CLAUSE_FIELD].map do |item|
1291
+ deparse_item(item)
1292
+ end.join(', ')
1293
+ end
1294
+
1295
+ if node['whereClause']
1296
+ output << 'WHERE'
1297
+ output << deparse_item(node['whereClause'])
1298
+ end
1299
+
1300
+ if node['returningList']
1301
+ output << 'RETURNING'
1302
+ output << node['returningList'].map do |item|
1303
+ # RETURNING is formatted like a SELECT
1304
+ deparse_item(item, :select)
1305
+ end.join(', ')
1306
+ end
1307
+
1308
+ output.join(' ')
1309
+ end
1310
+
1311
+ def deparse_typecast(node) # rubocop:disable Metrics/CyclomaticComplexity
1312
+ if deparse_item(node['typeName']) == 'boolean' || deparse_item(node['typeName']) == 'bool'
1313
+ return 'true' if deparse_item(node['arg']) == "'t'" || deparse_item(node['arg']) == 'true'
1314
+ return 'false' if deparse_item(node['arg']) == "'f'" || deparse_item(node['arg']) == 'false'
1315
+ end
1316
+
1317
+ context = true if node['arg']['A_Expr']
1318
+ deparse_item(node['arg'], context) + '::' + deparse_typename(node['typeName'][TYPE_NAME])
1319
+ end
1320
+
1321
+ def deparse_typename(node)
1322
+ names = node['names'].map { |n| deparse_item(n, TYPE_NAME) }
1323
+
1324
+ # Intervals are tricky and should be handled in a separate method because
1325
+ # they require performing some bitmask operations.
1326
+ return deparse_interval_type(node) if names == %w[pg_catalog interval]
1327
+
1328
+ output = []
1329
+ output << 'SETOF' if node['setof']
1330
+
1331
+ if node['typmods']
1332
+ arguments = node['typmods'].map do |item|
1333
+ deparse_item(item)
1334
+ end.join(', ')
1335
+ end
1336
+ output << deparse_typename_cast(names, arguments)
1337
+ output.last << '[]' if node['arrayBounds']
1338
+
1339
+ output.join(' ')
1340
+ end
1341
+
1342
+ def deparse_typename_cast(names, arguments) # rubocop:disable Metrics/CyclomaticComplexity
1343
+ catalog, type = names
1344
+ # Just pass along any custom types.
1345
+ # (The pg_catalog types are built-in Postgres system types and are
1346
+ # handled in the case statement below)
1347
+ return names.join('.') + (arguments.nil? ? '' : "(#{arguments})") if catalog != 'pg_catalog'
1348
+
1349
+ case type
1350
+ when 'bpchar'
1351
+ # char(2) or char(9)
1352
+ "char(#{arguments})"
1353
+ when 'varchar'
1354
+ arguments.nil? ? 'varchar' : "varchar(#{arguments})"
1355
+ when 'numeric'
1356
+ # numeric(3, 5)
1357
+ arguments.nil? ? 'numeric' : "numeric(#{arguments})"
1358
+ when 'bool'
1359
+ 'boolean'
1360
+ when 'int2'
1361
+ 'smallint'
1362
+ when 'int4'
1363
+ 'int'
1364
+ when 'int8'
1365
+ 'bigint'
1366
+ when 'real', 'float4'
1367
+ 'real'
1368
+ when 'float8'
1369
+ 'double precision'
1370
+ when 'time'
1371
+ 'time'
1372
+ when 'timetz'
1373
+ 'time with time zone'
1374
+ when 'timestamp'
1375
+ 'timestamp'
1376
+ when 'timestamptz'
1377
+ 'timestamp with time zone'
1378
+ else
1379
+ raise format("Can't deparse type: %s", type)
1380
+ end
1381
+ end
1382
+
1383
+ # Deparses interval type expressions like `interval year to month` or
1384
+ # `interval hour to second(5)`
1385
+ def deparse_interval_type(node)
1386
+ type = ['interval']
1387
+
1388
+ if node['typmods']
1389
+ typmods = node['typmods'].map { |typmod| deparse_item(typmod) }
1390
+ type << Interval.from_int(typmods.first.to_i).map do |part|
1391
+ # only the `second` type can take an argument.
1392
+ if part == 'second' && typmods.size == 2
1393
+ "second(#{typmods.last})"
1394
+ else
1395
+ part
1396
+ end.downcase
1397
+ end.join(' to ')
1398
+ end
1399
+
1400
+ type.join(' ')
1401
+ end
1402
+
1403
+ def deparse_nulltest(node)
1404
+ output = [deparse_item(node['arg'])]
1405
+ case node['nulltesttype']
1406
+ when 0
1407
+ output << 'IS NULL'
1408
+ when 1
1409
+ output << 'IS NOT NULL'
1410
+ end
1411
+ output.join(' ')
1412
+ end
1413
+
1414
+ TRANSACTION_CMDS = {
1415
+ TRANS_STMT_BEGIN => 'BEGIN',
1416
+ TRANS_STMT_COMMIT => 'COMMIT',
1417
+ TRANS_STMT_ROLLBACK => 'ROLLBACK',
1418
+ TRANS_STMT_SAVEPOINT => 'SAVEPOINT',
1419
+ TRANS_STMT_RELEASE => 'RELEASE',
1420
+ TRANS_STMT_ROLLBACK_TO => 'ROLLBACK TO SAVEPOINT'
1421
+ }.freeze
1422
+ def deparse_transaction(node)
1423
+ output = []
1424
+ output << TRANSACTION_CMDS[node['kind']] || raise(format("Can't deparse TRANSACTION %s", node.inspect))
1425
+
1426
+ if node['options']
1427
+ output += node['options'].map { |item| deparse_item(item) }
1428
+ end
1429
+
1430
+ output.join(' ')
1431
+ end
1432
+
1433
+ def deparse_coalesce(node)
1434
+ format('COALESCE(%s)', node['args'].map { |a| deparse_item(a) }.join(', '))
1435
+ end
1436
+
1437
+ def deparse_defelem(node)
1438
+ case node['defname']
1439
+ when 'as'
1440
+ "AS $$#{deparse_item_list(node['arg'], :defname_as).join("\n")}$$"
1441
+ when 'language'
1442
+ "language #{deparse_item(node['arg'])}"
1443
+ when 'volatility'
1444
+ node['arg']['String']['str'].upcase # volatility does not need to be quoted
1445
+ when 'strict'
1446
+ deparse_item(node['arg']) == '1' ? 'RETURNS NULL ON NULL INPUT' : 'CALLED ON NULL INPUT'
1447
+ else
1448
+ deparse_item(node['arg'])
1449
+ end
1450
+ end
1451
+
1452
+ def deparse_define_stmt(node)
1453
+ dispatch = {
1454
+ 1 => :deparse_create_aggregate,
1455
+ 25 => :deparse_create_operator,
1456
+ 45 => :deparse_create_type
1457
+ }
1458
+ method(dispatch.fetch(node['kind'])).call(node)
1459
+ end
1460
+
1461
+ def deparse_create_aggregate(node)
1462
+ output = ['CREATE AGGREGATE']
1463
+ output << node['defnames'].map(&method(:deparse_item))
1464
+ args = node['args'][0] || [{ A_STAR => nil }]
1465
+ output << "(#{args.map(&method(:deparse_item)).join(', ')})"
1466
+ definitions = node['definition'].map do |definition|
1467
+ definition_output = [definition['DefElem']['defname']]
1468
+ definition_output << definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item)).join(', ') if definition['DefElem'].key?('arg')
1469
+ definition_output.join('=')
1470
+ end
1471
+ output << "(#{definitions.join(', ')})"
1472
+ output.join(' ')
1473
+ end
1474
+
1475
+ def deparse_create_operator(node)
1476
+ output = ['CREATE OPERATOR']
1477
+ output << node['defnames'][0]['String']['str']
1478
+ definitions = node['definition'].map do |definition|
1479
+ definition_output = [definition['DefElem']['defname']]
1480
+ definition_output << definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item)).join(', ') if definition['DefElem'].key?('arg')
1481
+ definition_output.join('=')
1482
+ end
1483
+ output << "(#{definitions.join(', ')})"
1484
+ output.join(' ')
1485
+ end
1486
+
1487
+ def deparse_create_type(node)
1488
+ output = ['CREATE TYPE']
1489
+ output << node['defnames'].map(&method(:deparse_item))
1490
+ if node.key?('definition')
1491
+ definitions = node['definition'].map do |definition|
1492
+ definition_output = [definition['DefElem']['defname']]
1493
+ if definition['DefElem'].key?('arg')
1494
+ definition_output += definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item))
1495
+ end
1496
+ definition_output.join('=')
1497
+ end
1498
+ output << "(#{definitions.join(', ')})"
1499
+ end
1500
+ output.join(' ')
1501
+ end
1502
+
1503
+ def deparse_delete_from(node)
1504
+ output = []
1505
+ output << deparse_item(node['withClause']) if node['withClause']
1506
+
1507
+ output << 'DELETE FROM'
1508
+ output << deparse_item(node['relation'])
1509
+
1510
+ if node['usingClause']
1511
+ output << 'USING'
1512
+ output << node['usingClause'].map do |item|
1513
+ deparse_item(item)
1514
+ end.join(', ')
1515
+ end
1516
+
1517
+ if node['whereClause']
1518
+ output << 'WHERE'
1519
+ output << deparse_item(node['whereClause'])
1520
+ end
1521
+
1522
+ if node['returningList']
1523
+ output << 'RETURNING'
1524
+ output << node['returningList'].map do |item|
1525
+ # RETURNING is formatted like a SELECT
1526
+ deparse_item(item, :select)
1527
+ end.join(', ')
1528
+ end
1529
+
1530
+ output.join(' ')
1531
+ end
1532
+
1533
+ def deparse_discard(node)
1534
+ output = ['DISCARD']
1535
+ output << 'ALL' if (node['target']).zero?
1536
+ output << 'PLANS' if node['target'] == 1
1537
+ output << 'SEQUENCES' if node['target'] == 2
1538
+ output << 'TEMP' if node['target'] == 3
1539
+ output.join(' ')
1540
+ end
1541
+
1542
+ def deparse_drop(node) # rubocop:disable Metrics/CyclomaticComplexity
1543
+ output = ['DROP']
1544
+
1545
+ output << 'ACCESS METHOD' if node['removeType'] == OBJECT_TYPE_ACCESS_METHOD
1546
+ output << 'AGGREGATE' if node['removeType'] == OBJECT_TYPE_AGGREGATE
1547
+ output << 'CAST' if node['removeType'] == OBJECT_TYPE_CAST
1548
+ output << 'COLLATION' if node['removeType'] == OBJECT_TYPE_COLLATION
1549
+ output << 'DOMAIN' if node['removeType'] == OBJECT_TYPE_DOMAIN
1550
+ output << 'CONVERSION' if node['removeType'] == OBJECT_TYPE_CONVERSION
1551
+ output << 'EVENT TRIGGER' if node['removeType'] == OBJECT_TYPE_EVENT_TRIGGER
1552
+ output << 'EXTENSION' if node['removeType'] == OBJECT_TYPE_EXTENSION
1553
+ output << 'FOREIGN DATA WRAPPER' if node['removeType'] == OBJECT_TYPE_FDW
1554
+ output << 'FOREIGN TABLE' if node['removeType'] == OBJECT_TYPE_FOREIGN_TABLE
1555
+ output << 'FUNCTION' if node['removeType'] == OBJECT_TYPE_FUNCTION
1556
+ output << 'INDEX' if node['removeType'] == OBJECT_TYPE_INDEX
1557
+ output << 'MATERIALIZED VIEW' if node['removeType'] == OBJECT_TYPE_MATVIEW
1558
+ output << 'OPERATOR CLASS' if node['removeType'] == OBJECT_TYPE_OPCLASS
1559
+ output << 'OPERATOR FAMILY' if node['removeType'] == OBJECT_TYPE_OPFAMILY
1560
+ output << 'POLICY' if node['removeType'] == OBJECT_TYPE_POLICY
1561
+ output << 'PUBLICATION' if node['removeType'] == OBJECT_TYPE_PUBLICATION
1562
+ output << 'RULE' if node['removeType'] == OBJECT_TYPE_RULE
1563
+ output << 'SCHEMA' if node['removeType'] == OBJECT_TYPE_SCHEMA
1564
+ output << 'SERVER' if node['removeType'] == OBJECT_TYPE_FOREIGN_SERVER
1565
+ output << 'SEQUENCE' if node['removeType'] == OBJECT_TYPE_SEQUENCE
1566
+ output << 'STATISTICS' if node['removeType'] == OBJECT_TYPE_STATISTIC_EXT
1567
+ output << 'TABLE' if node['removeType'] == OBJECT_TYPE_TABLE
1568
+ output << 'TRANSFORM' if node['removeType'] == OBJECT_TYPE_TRANSFORM
1569
+ output << 'TRIGGER' if node['removeType'] == OBJECT_TYPE_TRIGGER
1570
+ output << 'TEXT SEARCH CONFIGURATION' if node['removeType'] == OBJECT_TYPE_TSCONFIGURATION
1571
+ output << 'TEXT SEARCH DICTIONARY' if node['removeType'] == OBJECT_TYPE_TSDICTIONARY
1572
+ output << 'TEXT SEARCH PARSER' if node['removeType'] == OBJECT_TYPE_TSPARSER
1573
+ output << 'TEXT SEARCH TEMPLATE' if node['removeType'] == OBJECT_TYPE_TSTEMPLATE
1574
+ output << 'TYPE' if node['removeType'] == OBJECT_TYPE_TYPE
1575
+ output << 'VIEW' if node['removeType'] == OBJECT_TYPE_VIEW
1576
+
1577
+ output << 'CONCURRENTLY' if node['concurrent']
1578
+ output << 'IF EXISTS' if node['missing_ok']
1579
+
1580
+ objects = node['objects']
1581
+ objects = [objects] unless objects[0].is_a?(Array)
1582
+ case node['removeType']
1583
+ when OBJECT_TYPE_CAST
1584
+ object = objects[0]
1585
+ output << format('(%s)', deparse_item_list(object).join(' AS '))
1586
+ when OBJECT_TYPE_FUNCTION, OBJECT_TYPE_AGGREGATE, OBJECT_TYPE_SCHEMA, OBJECT_TYPE_EXTENSION
1587
+ output << objects.map { |list| list.map { |object_line| deparse_item(object_line) } }.join(', ')
1588
+ when OBJECT_TYPE_OPFAMILY, OBJECT_TYPE_OPCLASS
1589
+ object = objects[0]
1590
+ output << deparse_item(object[1]) if object.length == 2
1591
+ output << deparse_item_list(object[1..-1]).join('.') if object.length == 3
1592
+ output << 'USING'
1593
+ output << deparse_item(object[0])
1594
+ when OBJECT_TYPE_TRIGGER, OBJECT_TYPE_RULE, OBJECT_TYPE_POLICY
1595
+ object = objects[0]
1596
+ output << deparse_item(object[-1])
1597
+ output << 'ON'
1598
+ output << deparse_item(object[0]) if object.length == 2
1599
+ output << deparse_item_list(object[0..1]).join('.') if object.length == 3
1600
+ when OBJECT_TYPE_TRANSFORM
1601
+ object = objects[0]
1602
+ output << 'FOR'
1603
+ output << deparse_item(object[0])
1604
+ output << 'LANGUAGE'
1605
+ output << deparse_item(object[1])
1606
+ else
1607
+ output << objects.map { |list| list.map { |object_line| deparse_item(object_line) }.join('.') }.join(', ')
1608
+ end
1609
+
1610
+ output << 'CASCADE' if node['behavior'] == 1
1611
+
1612
+ output.join(' ')
1613
+ end
1614
+
1615
+ def deparse_drop_role(node)
1616
+ output = ['DROP ROLE']
1617
+ output << 'IF EXISTS' if node['missing_ok']
1618
+ output << node['roles'].map { |role| deparse_identifier(role['RoleSpec']['rolename']) }.join(', ')
1619
+ output.join(' ')
1620
+ end
1621
+
1622
+ def deparse_drop_subscription(node)
1623
+ output = ['DROP SUBSCRIPTION']
1624
+ output << 'IF EXISTS' if node['missing_ok']
1625
+ output << deparse_identifier(node['subname'])
1626
+ output.join(' ')
1627
+ end
1628
+
1629
+ def deparse_drop_tablespace(node)
1630
+ output = ['DROP TABLESPACE']
1631
+ output << 'IF EXISTS' if node['missing_ok']
1632
+ output << node['tablespacename']
1633
+ output.join(' ')
1634
+ end
1635
+
1636
+ def deparse_explain(node)
1637
+ output = ['EXPLAIN']
1638
+ options = node.fetch('options', []).map { |option| option['DefElem']['defname'].upcase }
1639
+ if options.size == 1
1640
+ output.concat(options)
1641
+ elsif options.size > 1
1642
+ output << "(#{options.join(', ')})"
1643
+ end
1644
+ output << deparse_item(node['query'])
1645
+ output.join(' ')
1646
+ end
1647
+
1648
+ # The PG parser adds several pieces of view data onto the RANGEVAR
1649
+ # that need to be printed before deparse_rangevar is called.
1650
+ def relpersistence(rangevar)
1651
+ if rangevar[RANGE_VAR]['relpersistence'] == 't'
1652
+ 'TEMPORARY'
1653
+ elsif rangevar[RANGE_VAR]['relpersistence'] == 'u'
1654
+ 'UNLOGGED'
1655
+ end
1656
+ end
1657
+ end
1658
+ end