pg_query_pg_ddm 0.2

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.
@@ -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