gitlab-pg_query 1.3.0

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 = 'gitlab-10-1.0.3'.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://gitlab.com/gitlab-org/libpg_query/-/archive/#{LIB_PG_QUERY_TAG}/libpg_query-#{LIB_PG_QUERY_TAG}.tar.gz", '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,1588 @@
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)
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
+ else
220
+ deparse_identifier(node['str'], escape_always: true)
221
+ end
222
+ when INTEGER
223
+ node['ival'].to_s
224
+ when FLOAT
225
+ node['str']
226
+ when NULL
227
+ 'NULL'
228
+ else
229
+ raise format("Can't deparse: %s: %s", type, node.inspect)
230
+ end
231
+ end
232
+
233
+ def deparse_item_list(nodes, context = nil)
234
+ nodes.map { |n| deparse_item(n, context) }
235
+ end
236
+
237
+ def deparse_identifier(ident, escape_always: false)
238
+ return if ident.nil?
239
+ if escape_always || !ident[/^\w+$/] || KEYWORDS.include?(ident.upcase)
240
+ format('"%s"', ident.gsub('"', '""'))
241
+ else
242
+ ident
243
+ end
244
+ end
245
+
246
+ def deparse_rangevar(node)
247
+ output = []
248
+ output << 'ONLY' unless node['inh']
249
+ schema = node['schemaname'] ? '"' + node['schemaname'] + '".' : ''
250
+ output << schema + '"' + node['relname'] + '"'
251
+ output << deparse_item(node['alias']) if node['alias']
252
+ output.join(' ')
253
+ end
254
+
255
+ def deparse_raw_stmt(node)
256
+ deparse_item(node[STMT_FIELD])
257
+ end
258
+
259
+ def deparse_renamestmt_decision(node, type)
260
+ if node[type]
261
+ if node[type].is_a?(String)
262
+ deparse_identifier(node[type])
263
+ elsif node[type].is_a?(Array)
264
+ deparse_item_list(node[type])
265
+ elsif node[type].is_a?(Object)
266
+ deparse_item(node[type])
267
+ end
268
+ else
269
+ type
270
+ end
271
+ end
272
+
273
+ def deparse_renamestmt(node)
274
+ output = []
275
+ output << 'ALTER'
276
+ command, type, options, type2, options2 = Rename.commands(node)
277
+
278
+ output << command if command
279
+ output << deparse_renamestmt_decision(node, type)
280
+ output << options if options
281
+ output << deparse_renamestmt_decision(node, type2) if type2
282
+ output << deparse_renamestmt_decision(node, options2) if options2
283
+ output << 'TO'
284
+ output << deparse_identifier(node['newname'], escape_always: true)
285
+ output.join(' ')
286
+ end
287
+
288
+ def deparse_columnref(node)
289
+ node['fields'].map do |field|
290
+ field.is_a?(String) ? '"' + field + '"' : deparse_item(field)
291
+ end.join('.')
292
+ end
293
+
294
+ def deparse_a_arrayexp(node)
295
+ 'ARRAY[' + (node['elements'] || []).map do |element|
296
+ deparse_item(element)
297
+ end.join(', ') + ']'
298
+ end
299
+
300
+ def deparse_a_const(node)
301
+ deparse_item(node['val'], A_CONST)
302
+ end
303
+
304
+ def deparse_a_star(_node)
305
+ '*'
306
+ end
307
+
308
+ def deparse_a_indirection(node)
309
+ output = []
310
+ arg = deparse_item(node['arg'])
311
+ output << if node['arg'].key?(FUNC_CALL) || node['arg'].key?(SUB_LINK)
312
+ "(#{arg})."
313
+ else
314
+ arg
315
+ end
316
+ node['indirection'].each do |subnode|
317
+ output << deparse_item(subnode)
318
+ end
319
+ output.join
320
+ end
321
+
322
+ def deparse_a_indices(node)
323
+ format('[%s]', deparse_item(node['uidx']))
324
+ end
325
+
326
+ def deparse_alias(node)
327
+ name = node['aliasname']
328
+ if node['colnames']
329
+ name + '(' + deparse_item_list(node['colnames']).join(', ') + ')'
330
+ else
331
+ deparse_identifier(name)
332
+ end
333
+ end
334
+
335
+ def deparse_alter_table(node)
336
+ output = []
337
+ output << 'ALTER'
338
+
339
+ output << 'TABLE' if node['relkind'] == OBJECT_TYPE_TABLE
340
+ output << 'VIEW' if node['relkind'] == OBJECT_TYPE_VIEW
341
+
342
+ output << deparse_item(node['relation'])
343
+
344
+ output << node['cmds'].map do |item|
345
+ deparse_item(item)
346
+ end.join(', ')
347
+
348
+ output.join(' ')
349
+ end
350
+
351
+ def deparse_alter_table_cmd(node)
352
+ command, options = AlterTable.commands(node)
353
+
354
+ output = []
355
+ output << command if command
356
+ output << 'IF EXISTS' if node['missing_ok']
357
+ output << node['name']
358
+ output << options if options
359
+ output << deparse_item(node['def']) if node['def']
360
+ output << 'CASCADE' if node['behavior'] == 1
361
+
362
+ output.compact.join(' ')
363
+ end
364
+
365
+ def deparse_object_with_args(node)
366
+ output = []
367
+ output << deparse_item_list(node['objname']).join('.')
368
+ unless node['args_unspecified']
369
+ args = node.fetch('objargs', []).map(&method(:deparse_item)).join(', ')
370
+ output << "(#{args})"
371
+ end
372
+ output.join('')
373
+ end
374
+
375
+ def deparse_paramref(node)
376
+ if node['number'].nil?
377
+ '?'
378
+ else
379
+ format('$%d', node['number'])
380
+ end
381
+ end
382
+
383
+ def deparse_prepare(node)
384
+ output = ['PREPARE']
385
+ output << deparse_identifier(node['name'])
386
+ output << "(#{deparse_item_list(node['argtypes']).join(', ')})" if node['argtypes']
387
+ output << 'AS'
388
+ output << deparse_item(node['query'])
389
+ output.join(' ')
390
+ end
391
+
392
+ def deparse_restarget(node, context)
393
+ if context == :select
394
+ [deparse_item(node['val']), deparse_identifier(node['name'])].compact.join(' AS ')
395
+ elsif context == :update
396
+ [node['name'], deparse_item(node['val'])].compact.join(' = ')
397
+ elsif node['val'].nil?
398
+ node['name']
399
+ else
400
+ raise format("Can't deparse %s in context %s", node.inspect, context)
401
+ end
402
+ end
403
+
404
+ def deparse_funccall(node)
405
+ output = []
406
+
407
+ # SUM(a, b)
408
+ args = Array(node['args']).map { |arg| deparse_item(arg) }
409
+ # COUNT(*)
410
+ args << '*' if node['agg_star']
411
+
412
+ name = (node['funcname'].map { |n| deparse_item(n, FUNC_CALL) } - ['pg_catalog']).join('.')
413
+ distinct = node['agg_distinct'] ? 'DISTINCT ' : ''
414
+ output << format('%s(%s%s)', name, distinct, args.join(', '))
415
+ output << format('OVER %s', deparse_item(node['over'])) if node['over']
416
+
417
+ output.join(' ')
418
+ end
419
+
420
+ def deparse_windowdef(node)
421
+ return deparse_identifier(node['name']) if node['name']
422
+
423
+ output = []
424
+
425
+ if node['partitionClause']
426
+ output << 'PARTITION BY'
427
+ output << node['partitionClause'].map do |item|
428
+ deparse_item(item)
429
+ end.join(', ')
430
+ end
431
+
432
+ if node['orderClause']
433
+ output << 'ORDER BY'
434
+ output << node['orderClause'].map do |item|
435
+ deparse_item(item)
436
+ end.join(', ')
437
+ end
438
+
439
+ format('(%s)', output.join(' '))
440
+ end
441
+
442
+ def deparse_functionparameter(node)
443
+ deparse_item(node['argType'])
444
+ end
445
+
446
+ def deparse_grant_role(node)
447
+ output = []
448
+ output << ['GRANT'] if node['is_grant']
449
+ output << ['REVOKE'] unless node['is_grant']
450
+ output << node['granted_roles'].map(&method(:deparse_item)).join(', ')
451
+ output << ['TO'] if node['is_grant']
452
+ output << ['FROM'] unless node['is_grant']
453
+ output << node['grantee_roles'].map(&method(:deparse_item)).join(', ')
454
+ output << 'WITH ADMIN OPTION' if node['admin_opt']
455
+ output.join(' ')
456
+ end
457
+
458
+ def deparse_grant(node) # rubocop:disable Metrics/CyclomaticComplexity
459
+ objtype, allow_all = deparse_grant_objtype(node)
460
+ output = []
461
+ output << ['GRANT'] if node['is_grant']
462
+ output << ['REVOKE'] unless node['is_grant']
463
+ output << if node.key?('privileges')
464
+ node['privileges'].map(&method(:deparse_item)).join(', ')
465
+ else
466
+ 'ALL'
467
+ end
468
+ output << 'ON'
469
+ objects = node['objects']
470
+ objects = objects[0] if %w[DOMAIN TYPE].include?(objtype)
471
+ objects = objects.map do |object|
472
+ deparsed = deparse_item(object)
473
+ if object.key?(RANGE_VAR) || object.key?(OBJECT_WITH_ARGS) || !allow_all
474
+ objtype == 'TABLE' ? deparsed : "#{objtype} #{deparsed}"
475
+ else
476
+ "ALL #{objtype}S IN SCHEMA #{deparsed}"
477
+ end
478
+ end
479
+ output << objects.join(', ')
480
+ output << ['TO'] if node['is_grant']
481
+ output << ['FROM'] unless node['is_grant']
482
+ output << node['grantees'].map(&method(:deparse_item)).join(', ')
483
+ output << 'WITH GRANT OPTION' if node['grant_option']
484
+ output.join(' ')
485
+ end
486
+
487
+ def deparse_grant_objtype(node)
488
+ {
489
+ 1 => ['TABLE', true],
490
+ 2 => ['SEQUENCE', true],
491
+ 3 => ['DATABASE', false],
492
+ 4 => ['DOMAIN', false],
493
+ 5 => ['FOREIGN DATA WRAPPER', false],
494
+ 6 => ['FOREIGN SERVER', false],
495
+ 7 => ['FUNCTION', true],
496
+ 8 => ['LANGUAGE', false],
497
+ 9 => ['LARGE OBJECT', false],
498
+ 10 => ['SCHEMA', false],
499
+ 11 => ['TABLESPACE', false],
500
+ 12 => ['TYPE', false]
501
+ }.fetch(node['objtype'])
502
+ end
503
+
504
+ def deparse_access_priv(node)
505
+ output = [node['priv_name']]
506
+ output << "(#{node['cols'].map(&method(:deparse_item)).join(', ')})" if node.key?('cols')
507
+ output.join(' ')
508
+ end
509
+
510
+ def deparse_role_spec(node)
511
+ return 'CURRENT_USER' if node['roletype'] == 1
512
+ return 'SESSION_USER' if node['roletype'] == 2
513
+ return 'PUBLIC' if node['roletype'] == 3
514
+ deparse_identifier(node['rolename'], escape_always: true)
515
+ end
516
+
517
+ def deparse_aexpr_in(node)
518
+ rexpr = Array(node['rexpr']).map { |arg| deparse_item(arg) }
519
+ operator = node['name'].map { |n| deparse_item(n, :operator) } == ['='] ? 'IN' : 'NOT IN'
520
+ format('%s %s (%s)', deparse_item(node['lexpr']), operator, rexpr.join(', '))
521
+ end
522
+
523
+ def deparse_aexpr_like(node)
524
+ value = deparse_item(node['rexpr'])
525
+ operator = node['name'].map { |n| deparse_item(n, :operator) } == ['~~'] ? 'LIKE' : 'NOT LIKE'
526
+ format('%s %s %s', deparse_item(node['lexpr']), operator, value)
527
+ end
528
+
529
+ def deparse_aexpr_ilike(node)
530
+ value = deparse_item(node['rexpr'])
531
+ operator = node['name'][0]['String']['str'] == '~~*' ? 'ILIKE' : 'NOT ILIKE'
532
+ format('%s %s %s', deparse_item(node['lexpr']), operator, value)
533
+ end
534
+
535
+ def deparse_bool_expr_not(node)
536
+ format('NOT %s', deparse_item(node['args'][0]))
537
+ end
538
+
539
+ BOOLEAN_TEST_TYPE_TO_STRING = {
540
+ BOOLEAN_TEST_TRUE => ' IS TRUE',
541
+ BOOLEAN_TEST_NOT_TRUE => ' IS NOT TRUE',
542
+ BOOLEAN_TEST_FALSE => ' IS FALSE',
543
+ BOOLEAN_TEST_NOT_FALSE => ' IS NOT FALSE',
544
+ BOOLEAN_TEST_UNKNOWN => ' IS UNKNOWN',
545
+ BOOLEAN_TEST_NOT_UNKNOWN => ' IS NOT UNKNOWN'
546
+ }.freeze
547
+ def deparse_boolean_test(node)
548
+ deparse_item(node['arg']) + BOOLEAN_TEST_TYPE_TO_STRING[node['booltesttype']]
549
+ end
550
+
551
+ def deparse_range_function(node)
552
+ output = []
553
+ output << 'LATERAL' if node['lateral']
554
+ output << deparse_item(node['functions'][0][0]) # FIXME: Needs more test cases
555
+ output << deparse_item(node['alias']) if node['alias']
556
+ output << "#{node['alias'] ? '' : 'AS '}(#{deparse_item_list(node['coldeflist']).join(', ')})" if node['coldeflist']
557
+ output.join(' ')
558
+ end
559
+
560
+ def deparse_aexpr(node, context = false)
561
+ output = []
562
+ output << deparse_item(node['lexpr'], context || true)
563
+ output << deparse_item(node['rexpr'], context || true)
564
+ output = output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
565
+ if context
566
+ # This is a nested expression, add parentheses.
567
+ output = '(' + output + ')'
568
+ end
569
+ output
570
+ end
571
+
572
+ def deparse_bool_expr_and(node)
573
+ # Only put parantheses around OR nodes that are inside this one
574
+ node['args'].map do |arg|
575
+ if [BOOL_EXPR_OR].include?(arg.values[0]['boolop'])
576
+ format('(%s)', deparse_item(arg))
577
+ else
578
+ deparse_item(arg)
579
+ end
580
+ end.join(' AND ')
581
+ end
582
+
583
+ def deparse_bool_expr_or(node)
584
+ # Put parantheses around AND + OR nodes that are inside
585
+ node['args'].map do |arg|
586
+ if [BOOL_EXPR_AND, BOOL_EXPR_OR].include?(arg.values[0]['boolop'])
587
+ format('(%s)', deparse_item(arg))
588
+ else
589
+ deparse_item(arg)
590
+ end
591
+ end.join(' OR ')
592
+ end
593
+
594
+ def deparse_aexpr_any(node)
595
+ output = []
596
+ output << deparse_item(node['lexpr'])
597
+ output << format('ANY(%s)', deparse_item(node['rexpr']))
598
+ output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
599
+ end
600
+
601
+ def deparse_aexpr_all(node)
602
+ output = []
603
+ output << deparse_item(node['lexpr'])
604
+ output << format('ALL(%s)', deparse_item(node['rexpr']))
605
+ output.join(' ' + deparse_item(node['name'][0], :operator) + ' ')
606
+ end
607
+
608
+ def deparse_aexpr_between(node)
609
+ between = case node['kind']
610
+ when AEXPR_BETWEEN
611
+ ' BETWEEN '
612
+ when AEXPR_NOT_BETWEEN
613
+ ' NOT BETWEEN '
614
+ when AEXPR_BETWEEN_SYM
615
+ ' BETWEEN SYMMETRIC '
616
+ when AEXPR_NOT_BETWEEN_SYM
617
+ ' NOT BETWEEN SYMMETRIC '
618
+ end
619
+ name = deparse_item(node['lexpr'])
620
+ output = node['rexpr'].map { |n| deparse_item(n) }
621
+ name << between << output.join(' AND ')
622
+ end
623
+
624
+ def deparse_aexpr_nullif(node)
625
+ lexpr = deparse_item(node['lexpr'])
626
+ rexpr = deparse_item(node['rexpr'])
627
+ format('NULLIF(%s, %s)', lexpr, rexpr)
628
+ end
629
+
630
+ def deparse_joinexpr(node) # rubocop:disable Metrics/CyclomaticComplexity
631
+ output = []
632
+ output << deparse_item(node['larg'])
633
+ case node['jointype']
634
+ when 0
635
+ if node['isNatural']
636
+ output << 'NATURAL'
637
+ elsif node['quals'].nil? && node['usingClause'].nil?
638
+ output << 'CROSS'
639
+ end
640
+ when 1
641
+ output << 'LEFT'
642
+ when 2
643
+ output << 'FULL'
644
+ when 3
645
+ output << 'RIGHT'
646
+ end
647
+ output << 'JOIN'
648
+ output << deparse_item(node['rarg'])
649
+
650
+ if node['quals']
651
+ output << 'ON'
652
+ output << deparse_item(node['quals'])
653
+ end
654
+
655
+ output << format('USING (%s)', node['usingClause'].map { |n| deparse_item(n) }.join(', ')) if node['usingClause']
656
+
657
+ output.join(' ')
658
+ end
659
+
660
+ def deparse_lock(node)
661
+ output = []
662
+ output << 'LOCK TABLE'
663
+ tables = node['relations'].map { |table| deparse_item(table) }
664
+ output << tables.join(', ')
665
+ output.join(' ')
666
+ end
667
+
668
+ LOCK_CLAUSE_STRENGTH = {
669
+ LCS_FORKEYSHARE => 'FOR KEY SHARE',
670
+ LCS_FORSHARE => 'FOR SHARE',
671
+ LCS_FORNOKEYUPDATE => 'FOR NO KEY UPDATE',
672
+ LCS_FORUPDATE => 'FOR UPDATE'
673
+ }.freeze
674
+ def deparse_lockingclause(node)
675
+ output = []
676
+ output << LOCK_CLAUSE_STRENGTH[node['strength']]
677
+ if node['lockedRels']
678
+ output << 'OF'
679
+ output << node['lockedRels'].map do |item|
680
+ deparse_item(item)
681
+ end.join(', ')
682
+ end
683
+ output.join(' ')
684
+ end
685
+
686
+ def deparse_sortby(node)
687
+ output = []
688
+ output << deparse_item(node['node'])
689
+ output << 'ASC' if node['sortby_dir'] == 1
690
+ output << 'DESC' if node['sortby_dir'] == 2
691
+ output << 'NULLS FIRST' if node['sortby_nulls'] == 1
692
+ output << 'NULLS LAST' if node['sortby_nulls'] == 2
693
+ output.join(' ')
694
+ end
695
+
696
+ def deparse_collate(node)
697
+ output = []
698
+ output << deparse_item(node['arg'])
699
+ output << 'COLLATE'
700
+ output << deparse_item_list(node['collname'])
701
+ output.join(' ')
702
+ end
703
+
704
+ def deparse_with_clause(node)
705
+ output = ['WITH']
706
+ output << 'RECURSIVE' if node['recursive']
707
+ output << node['ctes'].map do |cte|
708
+ deparse_item(cte)
709
+ end.join(', ')
710
+ output.join(' ')
711
+ end
712
+
713
+ def deparse_viewstmt(node)
714
+ output = []
715
+ output << 'CREATE'
716
+ output << 'OR REPLACE' if node['replace']
717
+
718
+ persistence = relpersistence(node['view'])
719
+ output << persistence if persistence
720
+
721
+ output << 'VIEW'
722
+ output << node['view'][RANGE_VAR]['relname']
723
+ output << format('(%s)', deparse_item_list(node['aliases']).join(', ')) if node['aliases']
724
+
725
+ output << 'AS'
726
+ output << deparse_item(node['query'])
727
+
728
+ case node['withCheckOption']
729
+ when 1
730
+ output << 'WITH CHECK OPTION'
731
+ when 2
732
+ output << 'WITH CASCADED CHECK OPTION'
733
+ end
734
+ output.join(' ')
735
+ end
736
+
737
+ def deparse_variable_set_stmt(node)
738
+ output = []
739
+ output << 'SET'
740
+ output << 'LOCAL' if node['is_local']
741
+ output << node['name']
742
+ output << 'TO'
743
+ output << node['args'].map { |arg| deparse_item(arg) }.join(', ')
744
+ output.join(' ')
745
+ end
746
+
747
+ def deparse_vacuum_stmt(node)
748
+ output = []
749
+ output << 'VACUUM'
750
+ output.concat(deparse_vacuum_options(node))
751
+ output << deparse_item(node['relation']) if node.key?('relation')
752
+ if node.key?('va_cols')
753
+ output << "(#{node['va_cols'].map(&method(:deparse_item)).join(', ')})"
754
+ end
755
+ output.join(' ')
756
+ end
757
+
758
+ def deparse_vacuum_options(node)
759
+ output = []
760
+ output << 'FULL' if node['options'][4] == 1
761
+ output << 'FREEZE' if node['options'][3] == 1
762
+ output << 'VERBOSE' if node['options'][2] == 1
763
+ output << 'ANALYZE' if node['options'][1] == 1
764
+ output
765
+ end
766
+
767
+ def deparse_do_stmt(node)
768
+ output = []
769
+ output << 'DO'
770
+ statement, *rest = node['args']
771
+ output << "$$#{statement['DefElem']['arg']['String']['str']}$$"
772
+ output += rest.map { |item| deparse_item(item) }
773
+ output.join(' ')
774
+ end
775
+
776
+ def deparse_cte(node)
777
+ output = []
778
+ output << node['ctename']
779
+ output << format('(%s)', node['aliascolnames'].map { |n| deparse_item(n) }.join(', ')) if node['aliascolnames']
780
+ output << format('AS (%s)', deparse_item(node['ctequery']))
781
+ output.join(' ')
782
+ end
783
+
784
+ def deparse_case(node)
785
+ output = ['CASE']
786
+ output << deparse_item(node['arg']) if node['arg']
787
+ output += node['args'].map { |arg| deparse_item(arg) }
788
+ if node['defresult']
789
+ output << 'ELSE'
790
+ output << deparse_item(node['defresult'])
791
+ end
792
+ output << 'END'
793
+ output.join(' ')
794
+ end
795
+
796
+ def deparse_columndef(node)
797
+ output = [node['colname']]
798
+ output << deparse_item(node['typeName'])
799
+ if node['raw_default']
800
+ output << 'USING'
801
+ output << deparse_item(node['raw_default'])
802
+ end
803
+ if node['constraints']
804
+ output += node['constraints'].map do |item|
805
+ deparse_item(item)
806
+ end
807
+ end
808
+ if node['collClause']
809
+ output << 'COLLATE'
810
+ output += node['collClause']['CollateClause']['collname'].map(&method(:deparse_item))
811
+ end
812
+ output.compact.join(' ')
813
+ end
814
+
815
+ def deparse_composite_type(node)
816
+ output = ['CREATE TYPE']
817
+ output << deparse_rangevar(node['typevar'][RANGE_VAR].merge('inh' => true))
818
+ output << 'AS'
819
+ coldeflist = node['coldeflist'].map(&method(:deparse_item))
820
+ output << "(#{coldeflist.join(', ')})"
821
+ output.join(' ')
822
+ end
823
+
824
+ def deparse_constraint(node) # rubocop:disable Metrics/CyclomaticComplexity
825
+ output = []
826
+ if node['conname']
827
+ output << 'CONSTRAINT'
828
+ output << node['conname']
829
+ end
830
+ case node['contype']
831
+ when CONSTR_TYPE_NULL
832
+ output << 'NULL'
833
+ when CONSTR_TYPE_NOTNULL
834
+ output << 'NOT NULL'
835
+ when CONSTR_TYPE_DEFAULT
836
+ output << 'DEFAULT'
837
+ when CONSTR_TYPE_CHECK
838
+ output << 'CHECK'
839
+ when CONSTR_TYPE_PRIMARY
840
+ output << 'PRIMARY KEY'
841
+ when CONSTR_TYPE_UNIQUE
842
+ output << 'UNIQUE'
843
+ when CONSTR_TYPE_EXCLUSION
844
+ output << 'EXCLUSION'
845
+ when CONSTR_TYPE_FOREIGN
846
+ output << 'FOREIGN KEY'
847
+ end
848
+
849
+ if node['raw_expr']
850
+ expression = deparse_item(node['raw_expr'])
851
+ # Unless it's simple, put parentheses around it
852
+ expression = '(' + expression + ')' if node['raw_expr'][BOOL_EXPR]
853
+ expression = '(' + expression + ')' if node['raw_expr'][A_EXPR] && node['raw_expr'][A_EXPR]['kind'] == AEXPR_OP
854
+ output << expression
855
+ end
856
+ output << '(' + deparse_item_list(node['keys']).join(', ') + ')' if node['keys']
857
+ output << '(' + deparse_item_list(node['fk_attrs']).join(', ') + ')' if node['fk_attrs']
858
+ output << 'REFERENCES ' + deparse_item(node['pktable']) + ' (' + deparse_item_list(node['pk_attrs']).join(', ') + ')' if node['pktable']
859
+ output << 'NOT VALID' if node['skip_validation']
860
+ output << "USING INDEX #{node['indexname']}" if node['indexname']
861
+ output.join(' ')
862
+ end
863
+
864
+ def deparse_copy(node)
865
+ output = ['COPY']
866
+ if node.key?('relation')
867
+ output << deparse_item(node['relation'])
868
+ elsif node.key?('query')
869
+ output << "(#{deparse_item(node['query'])})"
870
+ end
871
+ columns = node.fetch('attlist', []).map { |column| deparse_item(column) }
872
+ output << "(#{columns.join(', ')})" unless columns.empty?
873
+ output << (node['is_from'] ? 'FROM' : 'TO')
874
+ output << 'PROGRAM' if node['is_program']
875
+ output << deparse_copy_output(node)
876
+ output.join(' ')
877
+ end
878
+
879
+ def deparse_copy_output(node)
880
+ return "'#{node['filename']}'" if node.key?('filename')
881
+ return 'STDIN' if node['is_from']
882
+ 'STDOUT'
883
+ end
884
+
885
+ def deparse_create_enum(node)
886
+ output = ['CREATE TYPE']
887
+ output << deparse_item(node['typeName'][0])
888
+ output << 'AS ENUM'
889
+ vals = node['vals'].map { |val| deparse_item(val, A_CONST) }
890
+ output << "(#{vals.join(', ')})"
891
+ output.join(' ')
892
+ end
893
+
894
+ def deparse_create_cast(node)
895
+ output = []
896
+ output << 'CREATE'
897
+ output << 'CAST'
898
+ output << format('(%s AS %s)', deparse_item(node['sourcetype']), deparse_item(node['targettype']))
899
+ output << if node['func']
900
+ function = node['func']['ObjectWithArgs']
901
+ name = deparse_item_list(function['objname']).join('.')
902
+ arguments = deparse_item_list(function['objargs']).join(', ')
903
+ format('WITH FUNCTION %s(%s)', name, arguments)
904
+ elsif node['inout']
905
+ 'WITH INOUT'
906
+ else
907
+ 'WITHOUT FUNCTION'
908
+ end
909
+ output << 'AS IMPLICIT' if (node['context']).zero?
910
+ output << 'AS ASSIGNMENT' if node['context'] == 1
911
+ output.join(' ')
912
+ end
913
+
914
+ def deparse_create_domain(node)
915
+ output = []
916
+ output << 'CREATE'
917
+ output << 'DOMAIN'
918
+ output << deparse_item_list(node['domainname']).join('.')
919
+ output << 'AS'
920
+ output << deparse_item(node['typeName']) if node['typeName']
921
+ output << deparse_item(node['collClause']) if node['collClause']
922
+ output << deparse_item_list(node['constraints'])
923
+ output.join(' ')
924
+ end
925
+
926
+ def deparse_create_function(node)
927
+ output = []
928
+ output << 'CREATE'
929
+ output << 'OR REPLACE' if node['replace']
930
+ output << 'FUNCTION'
931
+
932
+ arguments = deparse_item_list(node.fetch('parameters', [])).join(', ')
933
+
934
+ output << deparse_item_list(node['funcname']).join('.') + '(' + arguments + ')'
935
+
936
+ output << 'RETURNS'
937
+ output << deparse_item(node['returnType'])
938
+ output += node['options'].map { |item| deparse_item(item) }
939
+
940
+ output.join(' ')
941
+ end
942
+
943
+ def deparse_create_range(node)
944
+ output = ['CREATE TYPE']
945
+ output << deparse_item(node['typeName'][0])
946
+ output << 'AS RANGE'
947
+ params = node['params'].map do |param|
948
+ param_out = [param['DefElem']['defname']]
949
+ if param['DefElem'].key?('arg')
950
+ param_out << deparse_item(param['DefElem']['arg'])
951
+ end
952
+ param_out.join('=')
953
+ end
954
+ output << "(#{params.join(', ')})"
955
+ output.join(' ')
956
+ end
957
+
958
+ def deparse_create_schema(node)
959
+ output = ['CREATE SCHEMA']
960
+ output << 'IF NOT EXISTS' if node['if_not_exists']
961
+ output << deparse_identifier(node['schemaname']) if node.key?('schemaname')
962
+ output << format('AUTHORIZATION %s', deparse_item(node['authrole'])) if node.key?('authrole')
963
+ output << deparse_item_list(node['schemaElts']) if node.key?('schemaElts')
964
+ output.join(' ')
965
+ end
966
+
967
+ def deparse_create_table(node)
968
+ output = []
969
+ output << 'CREATE'
970
+
971
+ persistence = relpersistence(node['relation'])
972
+ output << persistence if persistence
973
+
974
+ output << 'TABLE'
975
+
976
+ output << 'IF NOT EXISTS' if node['if_not_exists']
977
+
978
+ output << deparse_item(node['relation'])
979
+
980
+ output << '(' + node['tableElts'].map do |item|
981
+ deparse_item(item)
982
+ end.join(', ') + ')'
983
+
984
+ if node['inhRelations']
985
+ output << 'INHERITS'
986
+ output << '(' + node['inhRelations'].map do |relation|
987
+ deparse_item(relation)
988
+ end.join(', ') + ')'
989
+ end
990
+
991
+ output.join(' ')
992
+ end
993
+
994
+ def deparse_create_table_as(node)
995
+ output = []
996
+ output << 'CREATE'
997
+
998
+ into = node['into']['IntoClause']
999
+ persistence = relpersistence(into['rel'])
1000
+ output << persistence if persistence
1001
+
1002
+ output << 'TABLE'
1003
+
1004
+ output << deparse_item(node['into'])
1005
+
1006
+ if into['onCommit'] > 0
1007
+ output << 'ON COMMIT'
1008
+ output << 'DELETE ROWS' if into['onCommit'] == 2
1009
+ output << 'DROP' if into['onCommit'] == 3
1010
+ end
1011
+
1012
+ output << 'AS'
1013
+ output << deparse_item(node['query'])
1014
+ output.join(' ')
1015
+ end
1016
+
1017
+ def deparse_execute(node)
1018
+ output = ['EXECUTE']
1019
+ output << deparse_identifier(node['name'])
1020
+ output << "(#{deparse_item_list(node['params']).join(', ')})" if node['params']
1021
+ output.join(' ')
1022
+ end
1023
+
1024
+ def deparse_into_clause(node)
1025
+ deparse_item(node['rel'])
1026
+ end
1027
+
1028
+ def deparse_when(node)
1029
+ output = ['WHEN']
1030
+ output << deparse_item(node['expr'])
1031
+ output << 'THEN'
1032
+ output << deparse_item(node['result'])
1033
+ output.join(' ')
1034
+ end
1035
+
1036
+ def deparse_sublink(node)
1037
+ if node['subLinkType'] == SUBLINK_TYPE_ANY
1038
+ format('%s IN (%s)', deparse_item(node['testexpr']), deparse_item(node['subselect']))
1039
+ elsif node['subLinkType'] == SUBLINK_TYPE_ALL
1040
+ format('%s %s ALL (%s)', deparse_item(node['testexpr']), deparse_item(node['operName'][0], :operator), deparse_item(node['subselect']))
1041
+ elsif node['subLinkType'] == SUBLINK_TYPE_EXISTS
1042
+ format('EXISTS(%s)', deparse_item(node['subselect']))
1043
+ else
1044
+ format('(%s)', deparse_item(node['subselect']))
1045
+ end
1046
+ end
1047
+
1048
+ def deparse_rangesubselect(node)
1049
+ output = '(' + deparse_item(node['subquery']) + ')'
1050
+ if node['alias']
1051
+ output + ' ' + deparse_item(node['alias'])
1052
+ else
1053
+ output
1054
+ end
1055
+ end
1056
+
1057
+ def deparse_row(node)
1058
+ 'ROW(' + node['args'].map { |arg| deparse_item(arg) }.join(', ') + ')'
1059
+ end
1060
+
1061
+ def deparse_select(node) # rubocop:disable Metrics/CyclomaticComplexity
1062
+ output = []
1063
+
1064
+ output << deparse_item(node['withClause']) if node['withClause']
1065
+
1066
+ if node['op'] == 1
1067
+ output << '(' if node['larg']['SelectStmt']['sortClause']
1068
+ output << deparse_item(node['larg'])
1069
+ output << ')' if node['larg']['SelectStmt']['sortClause']
1070
+ output << 'UNION'
1071
+ output << 'ALL' if node['all']
1072
+ output << '(' if node['rarg']['SelectStmt']['sortClause']
1073
+ output << deparse_item(node['rarg'])
1074
+ output << ')' if node['rarg']['SelectStmt']['sortClause']
1075
+ output.join(' ')
1076
+ end
1077
+
1078
+ if node['op'] == 3
1079
+ output << deparse_item(node['larg'])
1080
+ output << 'EXCEPT'
1081
+ output << deparse_item(node['rarg'])
1082
+ output.join(' ')
1083
+ end
1084
+
1085
+ output << 'SELECT' if node[FROM_CLAUSE_FIELD] || node[TARGET_LIST_FIELD]
1086
+
1087
+ if node[TARGET_LIST_FIELD]
1088
+ if node['distinctClause']
1089
+ output << 'DISTINCT'
1090
+ unless node['distinctClause'].compact.empty?
1091
+ columns = node['distinctClause'].map { |item| deparse_item(item, :select) }
1092
+ output << "ON (#{columns.join(', ')})"
1093
+ end
1094
+ end
1095
+ output << node[TARGET_LIST_FIELD].map do |item|
1096
+ deparse_item(item, :select)
1097
+ end.join(', ')
1098
+
1099
+ if node['intoClause']
1100
+ output << 'INTO'
1101
+ output << deparse_item(node['intoClause'])
1102
+ end
1103
+ end
1104
+
1105
+ if node[FROM_CLAUSE_FIELD]
1106
+ output << 'FROM'
1107
+ output << node[FROM_CLAUSE_FIELD].map do |item|
1108
+ deparse_item(item)
1109
+ end.join(', ')
1110
+ end
1111
+
1112
+ if node['whereClause']
1113
+ output << 'WHERE'
1114
+ output << deparse_item(node['whereClause'])
1115
+ end
1116
+
1117
+ if node['valuesLists']
1118
+ output << 'VALUES'
1119
+ output << node['valuesLists'].map do |value_list|
1120
+ '(' + value_list.map { |v| deparse_item(v) }.join(', ') + ')'
1121
+ end.join(', ')
1122
+ end
1123
+
1124
+ if node['groupClause']
1125
+ output << 'GROUP BY'
1126
+ output << node['groupClause'].map do |item|
1127
+ deparse_item(item)
1128
+ end.join(', ')
1129
+ end
1130
+
1131
+ if node['havingClause']
1132
+ output << 'HAVING'
1133
+ output << deparse_item(node['havingClause'])
1134
+ end
1135
+
1136
+ if node['sortClause']
1137
+ output << 'ORDER BY'
1138
+ output << node['sortClause'].map do |item|
1139
+ deparse_item(item)
1140
+ end.join(', ')
1141
+ end
1142
+
1143
+ if node['limitCount']
1144
+ output << 'LIMIT'
1145
+ output << deparse_item(node['limitCount'])
1146
+ end
1147
+
1148
+ if node['limitOffset']
1149
+ output << 'OFFSET'
1150
+ output << deparse_item(node['limitOffset'])
1151
+ end
1152
+
1153
+ if node['lockingClause']
1154
+ node['lockingClause'].map do |item|
1155
+ output << deparse_item(item)
1156
+ end
1157
+ end
1158
+
1159
+ output.join(' ')
1160
+ end
1161
+
1162
+ def deparse_sql_value_function(node)
1163
+ output = []
1164
+ lookup = [
1165
+ 'current_date',
1166
+ 'current_time',
1167
+ 'current_time', # with precision
1168
+ 'current_timestamp',
1169
+ 'current_timestamp', # with precision
1170
+ 'localtime',
1171
+ 'localtime', # with precision
1172
+ 'localtimestamp',
1173
+ 'localtimestamp', # with precision
1174
+ 'current_role',
1175
+ 'current_user',
1176
+ 'session_user',
1177
+ 'user',
1178
+ 'current_catalog',
1179
+ 'current_schema'
1180
+ ]
1181
+ output << lookup[node['op']]
1182
+ output << "(#{node['typmod']})" unless node.fetch('typmod', -1) == -1
1183
+ output.join('')
1184
+ end
1185
+
1186
+ def deparse_insert_into(node)
1187
+ output = []
1188
+ output << deparse_item(node['withClause']) if node['withClause']
1189
+
1190
+ output << 'INSERT INTO'
1191
+ output << deparse_item(node['relation'])
1192
+
1193
+ if node['cols']
1194
+ output << '(' + node['cols'].map do |column|
1195
+ deparse_item(column)
1196
+ end.join(', ') + ')'
1197
+ end
1198
+
1199
+ output << deparse_item(node['selectStmt'])
1200
+
1201
+ if node['returningList']
1202
+ output << 'RETURNING'
1203
+ output << node['returningList'].map do |column|
1204
+ deparse_item(column, :select)
1205
+ end.join(', ')
1206
+ end
1207
+
1208
+ output.join(' ')
1209
+ end
1210
+
1211
+ def deparse_update(node)
1212
+ output = []
1213
+ output << deparse_item(node['withClause']) if node['withClause']
1214
+
1215
+ output << 'UPDATE'
1216
+ output << deparse_item(node['relation'])
1217
+
1218
+ if node[TARGET_LIST_FIELD]
1219
+ output << 'SET'
1220
+ columns = node[TARGET_LIST_FIELD].map do |item|
1221
+ deparse_item(item, :update)
1222
+ end
1223
+ output << columns.join(', ')
1224
+ end
1225
+
1226
+ if node['whereClause']
1227
+ output << 'WHERE'
1228
+ output << deparse_item(node['whereClause'])
1229
+ end
1230
+
1231
+ if node['returningList']
1232
+ output << 'RETURNING'
1233
+ output << node['returningList'].map do |item|
1234
+ # RETURNING is formatted like a SELECT
1235
+ deparse_item(item, :select)
1236
+ end.join(', ')
1237
+ end
1238
+
1239
+ output.join(' ')
1240
+ end
1241
+
1242
+ def deparse_typecast(node)
1243
+ if deparse_item(node['typeName']) == 'boolean'
1244
+ deparse_item(node['arg']) == "'t'" ? 'true' : 'false'
1245
+ else
1246
+ context = true if node['arg']['A_Expr']
1247
+ deparse_item(node['arg'], context) + '::' + deparse_typename(node['typeName'][TYPE_NAME])
1248
+ end
1249
+ end
1250
+
1251
+ def deparse_typename(node)
1252
+ names = node['names'].map { |n| deparse_item(n, TYPE_NAME) }
1253
+
1254
+ # Intervals are tricky and should be handled in a separate method because
1255
+ # they require performing some bitmask operations.
1256
+ return deparse_interval_type(node) if names == %w[pg_catalog interval]
1257
+
1258
+ output = []
1259
+ output << 'SETOF' if node['setof']
1260
+
1261
+ if node['typmods']
1262
+ arguments = node['typmods'].map do |item|
1263
+ deparse_item(item)
1264
+ end.join(', ')
1265
+ end
1266
+ output << deparse_typename_cast(names, arguments)
1267
+ output.last << '[]' if node['arrayBounds']
1268
+
1269
+ output.join(' ')
1270
+ end
1271
+
1272
+ def deparse_typename_cast(names, arguments) # rubocop:disable Metrics/CyclomaticComplexity
1273
+ catalog, type = names
1274
+ # Just pass along any custom types.
1275
+ # (The pg_catalog types are built-in Postgres system types and are
1276
+ # handled in the case statement below)
1277
+ return names.join('.') + (arguments.nil? ? '' : "(#{arguments})") if catalog != 'pg_catalog'
1278
+
1279
+ case type
1280
+ when 'bpchar'
1281
+ # char(2) or char(9)
1282
+ "char(#{arguments})"
1283
+ when 'varchar'
1284
+ arguments.nil? ? 'varchar' : "varchar(#{arguments})"
1285
+ when 'numeric'
1286
+ # numeric(3, 5)
1287
+ arguments.nil? ? 'numeric' : "numeric(#{arguments})"
1288
+ when 'bool'
1289
+ 'boolean'
1290
+ when 'int2'
1291
+ 'smallint'
1292
+ when 'int4'
1293
+ 'int'
1294
+ when 'int8'
1295
+ 'bigint'
1296
+ when 'real', 'float4'
1297
+ 'real'
1298
+ when 'float8'
1299
+ 'double precision'
1300
+ when 'time'
1301
+ 'time'
1302
+ when 'timetz'
1303
+ 'time with time zone'
1304
+ when 'timestamp'
1305
+ 'timestamp'
1306
+ when 'timestamptz'
1307
+ 'timestamp with time zone'
1308
+ else
1309
+ raise format("Can't deparse type: %s", type)
1310
+ end
1311
+ end
1312
+
1313
+ # Deparses interval type expressions like `interval year to month` or
1314
+ # `interval hour to second(5)`
1315
+ def deparse_interval_type(node)
1316
+ type = ['interval']
1317
+
1318
+ if node['typmods']
1319
+ typmods = node['typmods'].map { |typmod| deparse_item(typmod) }
1320
+ type << Interval.from_int(typmods.first.to_i).map do |part|
1321
+ # only the `second` type can take an argument.
1322
+ if part == 'second' && typmods.size == 2
1323
+ "second(#{typmods.last})"
1324
+ else
1325
+ part
1326
+ end.downcase
1327
+ end.join(' to ')
1328
+ end
1329
+
1330
+ type.join(' ')
1331
+ end
1332
+
1333
+ def deparse_nulltest(node)
1334
+ output = [deparse_item(node['arg'])]
1335
+ case node['nulltesttype']
1336
+ when 0
1337
+ output << 'IS NULL'
1338
+ when 1
1339
+ output << 'IS NOT NULL'
1340
+ end
1341
+ output.join(' ')
1342
+ end
1343
+
1344
+ TRANSACTION_CMDS = {
1345
+ TRANS_STMT_BEGIN => 'BEGIN',
1346
+ TRANS_STMT_COMMIT => 'COMMIT',
1347
+ TRANS_STMT_ROLLBACK => 'ROLLBACK',
1348
+ TRANS_STMT_SAVEPOINT => 'SAVEPOINT',
1349
+ TRANS_STMT_RELEASE => 'RELEASE',
1350
+ TRANS_STMT_ROLLBACK_TO => 'ROLLBACK TO SAVEPOINT'
1351
+ }.freeze
1352
+ def deparse_transaction(node)
1353
+ output = []
1354
+ output << TRANSACTION_CMDS[node['kind']] || raise(format("Can't deparse TRANSACTION %s", node.inspect))
1355
+
1356
+ if node['options']
1357
+ output += node['options'].map { |item| deparse_item(item) }
1358
+ end
1359
+
1360
+ output.join(' ')
1361
+ end
1362
+
1363
+ def deparse_coalesce(node)
1364
+ format('COALESCE(%s)', node['args'].map { |a| deparse_item(a) }.join(', '))
1365
+ end
1366
+
1367
+ def deparse_defelem(node)
1368
+ case node['defname']
1369
+ when 'as'
1370
+ "AS $$#{deparse_item_list(node['arg'], :defname_as).join("\n")}$$"
1371
+ when 'language'
1372
+ "language #{deparse_item(node['arg'])}"
1373
+ when 'volatility'
1374
+ node['arg']['String']['str'].upcase # volatility does not need to be quoted
1375
+ when 'strict'
1376
+ deparse_item(node['arg']) == '1' ? 'RETURNS NULL ON NULL INPUT' : 'CALLED ON NULL INPUT'
1377
+ else
1378
+ deparse_item(node['arg'])
1379
+ end
1380
+ end
1381
+
1382
+ def deparse_define_stmt(node)
1383
+ dispatch = {
1384
+ 1 => :deparse_create_aggregate,
1385
+ 25 => :deparse_create_operator,
1386
+ 45 => :deparse_create_type
1387
+ }
1388
+ method(dispatch.fetch(node['kind'])).call(node)
1389
+ end
1390
+
1391
+ def deparse_create_aggregate(node)
1392
+ output = ['CREATE AGGREGATE']
1393
+ output << node['defnames'].map(&method(:deparse_item))
1394
+ args = node['args'][0] || [{ A_STAR => nil }]
1395
+ output << "(#{args.map(&method(:deparse_item)).join(', ')})"
1396
+ definitions = node['definition'].map do |definition|
1397
+ definition_output = [definition['DefElem']['defname']]
1398
+ definition_output << definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item)).join(', ') if definition['DefElem'].key?('arg')
1399
+ definition_output.join('=')
1400
+ end
1401
+ output << "(#{definitions.join(', ')})"
1402
+ output.join(' ')
1403
+ end
1404
+
1405
+ def deparse_create_operator(node)
1406
+ output = ['CREATE OPERATOR']
1407
+ output << node['defnames'][0]['String']['str']
1408
+ definitions = node['definition'].map do |definition|
1409
+ definition_output = [definition['DefElem']['defname']]
1410
+ definition_output << definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item)).join(', ') if definition['DefElem'].key?('arg')
1411
+ definition_output.join('=')
1412
+ end
1413
+ output << "(#{definitions.join(', ')})"
1414
+ output.join(' ')
1415
+ end
1416
+
1417
+ def deparse_create_type(node)
1418
+ output = ['CREATE TYPE']
1419
+ output << node['defnames'].map(&method(:deparse_item))
1420
+ if node.key?('definition')
1421
+ definitions = node['definition'].map do |definition|
1422
+ definition_output = [definition['DefElem']['defname']]
1423
+ if definition['DefElem'].key?('arg')
1424
+ definition_output += definition['DefElem']['arg']['TypeName']['names'].map(&method(:deparse_item))
1425
+ end
1426
+ definition_output.join('=')
1427
+ end
1428
+ output << "(#{definitions.join(', ')})"
1429
+ end
1430
+ output.join(' ')
1431
+ end
1432
+
1433
+ def deparse_delete_from(node)
1434
+ output = []
1435
+ output << deparse_item(node['withClause']) if node['withClause']
1436
+
1437
+ output << 'DELETE FROM'
1438
+ output << deparse_item(node['relation'])
1439
+
1440
+ if node['usingClause']
1441
+ output << 'USING'
1442
+ output << node['usingClause'].map do |item|
1443
+ deparse_item(item)
1444
+ end.join(', ')
1445
+ end
1446
+
1447
+ if node['whereClause']
1448
+ output << 'WHERE'
1449
+ output << deparse_item(node['whereClause'])
1450
+ end
1451
+
1452
+ if node['returningList']
1453
+ output << 'RETURNING'
1454
+ output << node['returningList'].map do |item|
1455
+ # RETURNING is formatted like a SELECT
1456
+ deparse_item(item, :select)
1457
+ end.join(', ')
1458
+ end
1459
+
1460
+ output.join(' ')
1461
+ end
1462
+
1463
+ def deparse_discard(node)
1464
+ output = ['DISCARD']
1465
+ output << 'ALL' if (node['target']).zero?
1466
+ output << 'PLANS' if node['target'] == 1
1467
+ output << 'SEQUENCES' if node['target'] == 2
1468
+ output << 'TEMP' if node['target'] == 3
1469
+ output.join(' ')
1470
+ end
1471
+
1472
+ def deparse_drop(node) # rubocop:disable Metrics/CyclomaticComplexity
1473
+ output = ['DROP']
1474
+
1475
+ output << 'ACCESS METHOD' if node['removeType'] == OBJECT_TYPE_ACCESS_METHOD
1476
+ output << 'AGGREGATE' if node['removeType'] == OBJECT_TYPE_AGGREGATE
1477
+ output << 'CAST' if node['removeType'] == OBJECT_TYPE_CAST
1478
+ output << 'COLLATION' if node['removeType'] == OBJECT_TYPE_COLLATION
1479
+ output << 'DOMAIN' if node['removeType'] == OBJECT_TYPE_DOMAIN
1480
+ output << 'CONVERSION' if node['removeType'] == OBJECT_TYPE_CONVERSION
1481
+ output << 'EVENT TRIGGER' if node['removeType'] == OBJECT_TYPE_EVENT_TRIGGER
1482
+ output << 'EXTENSION' if node['removeType'] == OBJECT_TYPE_EXTENSION
1483
+ output << 'FOREIGN DATA WRAPPER' if node['removeType'] == OBJECT_TYPE_FDW
1484
+ output << 'FOREIGN TABLE' if node['removeType'] == OBJECT_TYPE_FOREIGN_TABLE
1485
+ output << 'FUNCTION' if node['removeType'] == OBJECT_TYPE_FUNCTION
1486
+ output << 'INDEX' if node['removeType'] == OBJECT_TYPE_INDEX
1487
+ output << 'MATERIALIZED VIEW' if node['removeType'] == OBJECT_TYPE_MATVIEW
1488
+ output << 'OPERATOR CLASS' if node['removeType'] == OBJECT_TYPE_OPCLASS
1489
+ output << 'OPERATOR FAMILY' if node['removeType'] == OBJECT_TYPE_OPFAMILY
1490
+ output << 'POLICY' if node['removeType'] == OBJECT_TYPE_POLICY
1491
+ output << 'PUBLICATION' if node['removeType'] == OBJECT_TYPE_PUBLICATION
1492
+ output << 'RULE' if node['removeType'] == OBJECT_TYPE_RULE
1493
+ output << 'SCHEMA' if node['removeType'] == OBJECT_TYPE_SCHEMA
1494
+ output << 'SERVER' if node['removeType'] == OBJECT_TYPE_FOREIGN_SERVER
1495
+ output << 'SEQUENCE' if node['removeType'] == OBJECT_TYPE_SEQUENCE
1496
+ output << 'STATISTICS' if node['removeType'] == OBJECT_TYPE_STATISTIC_EXT
1497
+ output << 'TABLE' if node['removeType'] == OBJECT_TYPE_TABLE
1498
+ output << 'TRANSFORM' if node['removeType'] == OBJECT_TYPE_TRANSFORM
1499
+ output << 'TRIGGER' if node['removeType'] == OBJECT_TYPE_TRIGGER
1500
+ output << 'TEXT SEARCH CONFIGURATION' if node['removeType'] == OBJECT_TYPE_TSCONFIGURATION
1501
+ output << 'TEXT SEARCH DICTIONARY' if node['removeType'] == OBJECT_TYPE_TSDICTIONARY
1502
+ output << 'TEXT SEARCH PARSER' if node['removeType'] == OBJECT_TYPE_TSPARSER
1503
+ output << 'TEXT SEARCH TEMPLATE' if node['removeType'] == OBJECT_TYPE_TSTEMPLATE
1504
+ output << 'TYPE' if node['removeType'] == OBJECT_TYPE_TYPE
1505
+ output << 'VIEW' if node['removeType'] == OBJECT_TYPE_VIEW
1506
+
1507
+ output << 'CONCURRENTLY' if node['concurrent']
1508
+ output << 'IF EXISTS' if node['missing_ok']
1509
+
1510
+ objects = node['objects']
1511
+ objects = [objects] unless objects[0].is_a?(Array)
1512
+ case node['removeType']
1513
+ when OBJECT_TYPE_CAST
1514
+ object = objects[0]
1515
+ output << format('(%s)', deparse_item_list(object).join(' AS '))
1516
+ when OBJECT_TYPE_FUNCTION, OBJECT_TYPE_AGGREGATE, OBJECT_TYPE_SCHEMA, OBJECT_TYPE_EXTENSION
1517
+ output << objects.map { |list| list.map { |object_line| deparse_item(object_line) } }.join(', ')
1518
+ when OBJECT_TYPE_OPFAMILY, OBJECT_TYPE_OPCLASS
1519
+ object = objects[0]
1520
+ output << deparse_item(object[1]) if object.length == 2
1521
+ output << deparse_item_list(object[1..-1]).join('.') if object.length == 3
1522
+ output << 'USING'
1523
+ output << deparse_item(object[0])
1524
+ when OBJECT_TYPE_TRIGGER, OBJECT_TYPE_RULE, OBJECT_TYPE_POLICY
1525
+ object = objects[0]
1526
+ output << deparse_item(object[-1])
1527
+ output << 'ON'
1528
+ output << deparse_item(object[0]) if object.length == 2
1529
+ output << deparse_item_list(object[0..1]).join('.') if object.length == 3
1530
+ when OBJECT_TYPE_TRANSFORM
1531
+ object = objects[0]
1532
+ output << 'FOR'
1533
+ output << deparse_item(object[0])
1534
+ output << 'LANGUAGE'
1535
+ output << deparse_item(object[1])
1536
+ else
1537
+ output << objects.map { |list| list.map { |object_line| deparse_item(object_line) }.join('.') }.join(', ')
1538
+ end
1539
+
1540
+ output << 'CASCADE' if node['behavior'] == 1
1541
+
1542
+ output.join(' ')
1543
+ end
1544
+
1545
+ def deparse_drop_role(node)
1546
+ output = ['DROP ROLE']
1547
+ output << 'IF EXISTS' if node['missing_ok']
1548
+ output << node['roles'].map { |role| deparse_identifier(role['RoleSpec']['rolename']) }.join(', ')
1549
+ output.join(' ')
1550
+ end
1551
+
1552
+ def deparse_drop_subscription(node)
1553
+ output = ['DROP SUBSCRIPTION']
1554
+ output << 'IF EXISTS' if node['missing_ok']
1555
+ output << deparse_identifier(node['subname'])
1556
+ output.join(' ')
1557
+ end
1558
+
1559
+ def deparse_drop_tablespace(node)
1560
+ output = ['DROP TABLESPACE']
1561
+ output << 'IF EXISTS' if node['missing_ok']
1562
+ output << node['tablespacename']
1563
+ output.join(' ')
1564
+ end
1565
+
1566
+ def deparse_explain(node)
1567
+ output = ['EXPLAIN']
1568
+ options = node.fetch('options', []).map { |option| option['DefElem']['defname'].upcase }
1569
+ if options.size == 1
1570
+ output.concat(options)
1571
+ elsif options.size > 1
1572
+ output << "(#{options.join(', ')})"
1573
+ end
1574
+ output << deparse_item(node['query'])
1575
+ output.join(' ')
1576
+ end
1577
+
1578
+ # The PG parser adds several pieces of view data onto the RANGEVAR
1579
+ # that need to be printed before deparse_rangevar is called.
1580
+ def relpersistence(rangevar)
1581
+ if rangevar[RANGE_VAR]['relpersistence'] == 't'
1582
+ 'TEMPORARY'
1583
+ elsif rangevar[RANGE_VAR]['relpersistence'] == 'u'
1584
+ 'UNLOGGED'
1585
+ end
1586
+ end
1587
+ end
1588
+ end