gitlab-pg_query 1.3.0

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 = '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