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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +321 -0
- data/LICENSE +28 -0
- data/README.md +180 -0
- data/Rakefile +23 -0
- data/ext/pg_query/extconf.rb +46 -0
- data/ext/pg_query/pg_query_ruby.c +129 -0
- data/ext/pg_query/pg_query_ruby.h +10 -0
- data/ext/pg_query/pg_query_ruby.sym +1 -0
- data/lib/pg_query.rb +16 -0
- data/lib/pg_query/deep_dup.rb +16 -0
- data/lib/pg_query/deparse.rb +1588 -0
- data/lib/pg_query/deparse/alter_table.rb +42 -0
- data/lib/pg_query/deparse/interval.rb +105 -0
- data/lib/pg_query/deparse/keywords.rb +159 -0
- data/lib/pg_query/deparse/rename.rb +41 -0
- data/lib/pg_query/filter_columns.rb +107 -0
- data/lib/pg_query/fingerprint.rb +115 -0
- data/lib/pg_query/legacy_parsetree.rb +109 -0
- data/lib/pg_query/node_types.rb +296 -0
- data/lib/pg_query/param_refs.rb +45 -0
- data/lib/pg_query/parse.rb +247 -0
- data/lib/pg_query/parse_error.rb +9 -0
- data/lib/pg_query/treewalker.rb +53 -0
- data/lib/pg_query/truncate.rb +60 -0
- data/lib/pg_query/version.rb +3 -0
- metadata +130 -0
data/Rakefile
ADDED
@@ -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 @@
|
|
1
|
+
_Init_pg_query
|
data/lib/pg_query.rb
ADDED
@@ -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
|