pg_query 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +47 -61
- data/ext/pg_query/extconf.rb +15 -4
- data/ext/pg_query/pg_query_ruby.c +42 -0
- data/lib/pg_query.rb +4 -0
- data/lib/pg_query/deep_dup.rb +16 -0
- data/lib/pg_query/deparse.rb +166 -116
- data/lib/pg_query/deparse/alter_table.rb +21 -122
- data/lib/pg_query/filter_columns.rb +37 -35
- data/lib/pg_query/fingerprint.rb +95 -26
- data/lib/pg_query/legacy_parsetree.rb +128 -0
- data/lib/pg_query/node_types.rb +218 -0
- data/lib/pg_query/param_refs.rb +9 -9
- data/lib/pg_query/parse.rb +48 -50
- data/lib/pg_query/treewalker.rb +17 -11
- data/lib/pg_query/truncate.rb +10 -8
- data/lib/pg_query/version.rb +1 -1
- metadata +5 -2
@@ -5,9 +5,8 @@ class PgQuery
|
|
5
5
|
# will be placed before the column name and the second, if present, will be
|
6
6
|
# placed after.
|
7
7
|
#
|
8
|
-
# If node['subtype'] is the integer 4,
|
9
|
-
#
|
10
|
-
# And the return value of this method will be:
|
8
|
+
# If node['subtype'] is the integer 4 (AT_DropNotNull),
|
9
|
+
# then return value of this method will be:
|
11
10
|
#
|
12
11
|
# ['ALTER COLUMN', 'DROP NOT NULL']
|
13
12
|
#
|
@@ -16,128 +15,28 @@ class PgQuery
|
|
16
15
|
# ALTER COLUMN {column_name} DROP NOT NULL
|
17
16
|
#
|
18
17
|
def self.commands(node)
|
19
|
-
action =
|
18
|
+
action = ALTER_TABLE_TYPES_MAPPING[node['subtype']] || fail(format("Can't deparse: %s", node.inspect))
|
20
19
|
PgQuery::Deparse.instance_exec(node, &action)
|
21
20
|
end
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
'SetNotNull' => -> (_node) { ['ALTER COLUMN', 'SET NOT NULL'] },
|
42
|
-
# alter column set statistics
|
43
|
-
'SetStatistics' => -> (_node) { ['ALTER COLUMN', 'SET STATISTICS'] },
|
44
|
-
# alter column set ( options )
|
45
|
-
'SetOptions' => -> (_node) { ['ALTER COLUMN', 'SET'] },
|
46
|
-
# alter column reset ( options )
|
47
|
-
'ResetOptions' => -> (_node) { ['ALTER COLUMN', 'RESET'] },
|
48
|
-
# alter column set storage
|
49
|
-
'SetStorage' => -> (_node) { ['ALTER COLUMN', 'SET STORAGE'] },
|
50
|
-
# drop column
|
51
|
-
'DropColumn' => -> (_node) { ['DROP'] },
|
52
|
-
# internal to commands/tablecmds.c
|
53
|
-
'DropColumnRecurse' => -> (_node) { NotImplemented },
|
54
|
-
# add index
|
55
|
-
'AddIndex' => -> (_node) { ['ADD INDEX'] },
|
56
|
-
# internal to commands/tablecmds.c
|
57
|
-
'ReAddIndex' => -> (_node) { NotImplemented },
|
58
|
-
# add constraint
|
59
|
-
'AddConstraint' => -> (_node) { ['ADD'] },
|
60
|
-
# internal to commands/tablecmds.c
|
61
|
-
'AddConstraintRecurse' => -> (_node) { NotImplemented },
|
62
|
-
# internal to commands/tablecmds.c
|
63
|
-
'ReAddConstraint' => -> (_node) { NotImplemented },
|
64
|
-
# alter constraint
|
65
|
-
'AlterConstraint' => -> (_node) { ['ALTER CONSTRAINT'] },
|
66
|
-
# validate constraint
|
67
|
-
'ValidateConstraint' => -> (_node) { ['VALIDATE CONSTRAINT'] },
|
68
|
-
# internal to commands/tablecmds.c
|
69
|
-
'ValidateConstraintRecurse' => -> (_node) { NotImplemented },
|
70
|
-
# pre-processed add constraint (local in parser/parse_utilcmd.c)
|
71
|
-
'ProcessedConstraint' => -> (_node) { NotImplemented },
|
72
|
-
# add constraint using existing index
|
73
|
-
'AddIndexConstraint' => -> (_node) { NotImplemented },
|
74
|
-
# drop constraint
|
75
|
-
'DropConstraint' => -> (_node) { ['DROP CONSTRAINT'] },
|
76
|
-
# internal to commands/tablecmds.c
|
77
|
-
'DropConstraintRecurse' => -> (_node) { NotImplemented },
|
78
|
-
# alter column type
|
79
|
-
'AlterColumnType' => -> (_node) { ['ALTER COLUMN', 'TYPE'] },
|
80
|
-
# alter column OPTIONS (...)
|
81
|
-
'AlterColumnGenericOptions' => -> (_node) { ['ALTER COLUMN', 'OPTIONS'] },
|
82
|
-
# change owner
|
83
|
-
'ChangeOwner' => -> (_node) { NotImplemented },
|
84
|
-
# CLUSTER ON
|
85
|
-
'ClusterOn' => -> (_node) { NotImplemented },
|
86
|
-
# SET WITHOUT CLUSTER
|
87
|
-
'DropCluster' => -> (_node) { NotImplemented },
|
88
|
-
# SET WITH OIDS
|
89
|
-
'AddOids' => -> (_node) { NotImplemented },
|
90
|
-
# internal to commands/tablecmds.c
|
91
|
-
'AddOidsRecurse' => -> (_node) { NotImplemented },
|
92
|
-
# SET WITHOUT OIDS
|
93
|
-
'DropOids' => -> (_node) { NotImplemented },
|
94
|
-
# SET TABLESPACE
|
95
|
-
'SetTableSpace' => -> (_node) { NotImplemented },
|
96
|
-
# SET (...) -- AM specific parameters
|
97
|
-
'SetRelOptions' => -> (_node) { NotImplemented },
|
98
|
-
# RESET (...) -- AM specific parameters
|
99
|
-
'ResetRelOptions' => -> (_node) { NotImplemented },
|
100
|
-
# replace reloption list in its entirety
|
101
|
-
'ReplaceRelOptions' => -> (_node) { NotImplemented },
|
102
|
-
# ENABLE TRIGGER name
|
103
|
-
'EnableTrig' => -> (_node) { NotImplemented },
|
104
|
-
# ENABLE ALWAYS TRIGGER name
|
105
|
-
'EnableAlwaysTrig' => -> (_node) { NotImplemented },
|
106
|
-
# ENABLE REPLICA TRIGGER name
|
107
|
-
'EnableReplicaTrig' => -> (_node) { NotImplemented },
|
108
|
-
# DISABLE TRIGGER name
|
109
|
-
'DisableTrig' => -> (_node) { NotImplemented },
|
110
|
-
# ENABLE TRIGGER ALL
|
111
|
-
'EnableTrigAll' => -> (_node) { NotImplemented },
|
112
|
-
# DISABLE TRIGGER ALL
|
113
|
-
'DisableTrigAll' => -> (_node) { NotImplemented },
|
114
|
-
# ENABLE TRIGGER USER
|
115
|
-
'EnableTrigUser' => -> (_node) { NotImplemented },
|
116
|
-
# DISABLE TRIGGER USER
|
117
|
-
'DisableTrigUser' => -> (_node) { NotImplemented },
|
118
|
-
# ENABLE RULE name
|
119
|
-
'EnableRule' => -> (_node) { NotImplemented },
|
120
|
-
# ENABLE ALWAYS RULE name
|
121
|
-
'EnableAlwaysRule' => -> (_node) { NotImplemented },
|
122
|
-
# ENABLE REPLICA RULE name
|
123
|
-
'EnableReplicaRule' => -> (_node) { NotImplemented },
|
124
|
-
# DISABLE RULE name
|
125
|
-
'DisableRule' => -> (_node) { NotImplemented },
|
126
|
-
# INHERIT parent
|
127
|
-
'AddInherit' => -> (_node) { NotImplemented },
|
128
|
-
# NO INHERIT parent
|
129
|
-
'DropInherit' => -> (_node) { NotImplemented },
|
130
|
-
# OF <type_name>
|
131
|
-
'AddOf' => -> (_node) { NotImplemented },
|
132
|
-
# NOT OF
|
133
|
-
'DropOf' => -> (_node) { NotImplemented },
|
134
|
-
# REPLICA IDENTITY
|
135
|
-
'ReplicaIdentity' => -> (_node) { NotImplemented },
|
136
|
-
# OPTIONS (...)
|
137
|
-
'GenericOptions' => -> (_node) { NotImplemented }
|
138
|
-
}
|
139
|
-
# Relying on Ruby's hashes maintaining key sort order
|
140
|
-
ALTER_TABLE_COMMANDS = ALTER_TABLE.keys
|
22
|
+
ALTER_TABLE_TYPES_MAPPING = {
|
23
|
+
AT_AddColumn => -> (_node) { ['ADD COLUMN'] },
|
24
|
+
AT_ColumnDefault => -> (node) { ['ALTER COLUMN', node['def'] ? 'SET DEFAULT' : 'DROP DEFAULT'] },
|
25
|
+
AT_DropNotNull => -> (_node) { ['ALTER COLUMN', 'DROP NOT NULL'] },
|
26
|
+
AT_SetNotNull => -> (_node) { ['ALTER COLUMN', 'SET NOT NULL'] },
|
27
|
+
AT_SetStatistics => -> (_node) { ['ALTER COLUMN', 'SET STATISTICS'] },
|
28
|
+
AT_SetOptions => -> (_node) { ['ALTER COLUMN', 'SET'] },
|
29
|
+
AT_ResetOptions => -> (_node) { ['ALTER COLUMN', 'RESET'] },
|
30
|
+
AT_SetStorage => -> (_node) { ['ALTER COLUMN', 'SET STORAGE'] },
|
31
|
+
AT_DropColumn => -> (_node) { ['DROP'] },
|
32
|
+
AT_AddIndex => -> (_node) { ['ADD INDEX'] },
|
33
|
+
AT_AddConstraint => -> (_node) { ['ADD'] },
|
34
|
+
AT_AlterConstraint => -> (_node) { ['ALTER CONSTRAINT'] },
|
35
|
+
AT_ValidateConstraint => -> (_node) { ['VALIDATE CONSTRAINT'] },
|
36
|
+
AT_DropConstraint => -> (_node) { ['DROP CONSTRAINT'] },
|
37
|
+
AT_AlterColumnType => -> (_node) { ['ALTER COLUMN', 'TYPE'] },
|
38
|
+
AT_AlterColumnGenericOptions => -> (_node) { ['ALTER COLUMN', 'OPTIONS'] }
|
39
|
+
}.freeze
|
141
40
|
end
|
142
41
|
end
|
143
42
|
end
|
@@ -7,67 +7,69 @@ class PgQuery
|
|
7
7
|
load_tables_and_aliases! if @aliases.nil?
|
8
8
|
|
9
9
|
# Get condition items from the parsetree
|
10
|
-
statements = @
|
10
|
+
statements = @tree.dup
|
11
11
|
condition_items = []
|
12
12
|
filter_columns = []
|
13
13
|
loop do
|
14
14
|
statement = statements.shift
|
15
15
|
if statement
|
16
|
-
if statement[
|
17
|
-
if statement[
|
18
|
-
if statement[
|
16
|
+
if statement[SELECT_STMT]
|
17
|
+
if statement[SELECT_STMT]['op'] == 0
|
18
|
+
if statement[SELECT_STMT][FROM_CLAUSE_FIELD]
|
19
19
|
# FROM subselects
|
20
|
-
statement[
|
21
|
-
next unless item['
|
22
|
-
statements << item['
|
20
|
+
statement[SELECT_STMT][FROM_CLAUSE_FIELD].each do |item|
|
21
|
+
next unless item['RangeSubselect']
|
22
|
+
statements << item['RangeSubselect']['subquery']
|
23
23
|
end
|
24
24
|
|
25
25
|
# JOIN ON conditions
|
26
|
-
condition_items += conditions_from_join_clauses(statement[
|
26
|
+
condition_items += conditions_from_join_clauses(statement[SELECT_STMT][FROM_CLAUSE_FIELD])
|
27
27
|
end
|
28
28
|
|
29
29
|
# WHERE clause
|
30
|
-
condition_items << statement[
|
30
|
+
condition_items << statement[SELECT_STMT]['whereClause'] if statement[SELECT_STMT]['whereClause']
|
31
31
|
|
32
32
|
# CTEs
|
33
|
-
if statement[
|
34
|
-
statement[
|
35
|
-
statements << item['
|
33
|
+
if statement[SELECT_STMT]['withClause']
|
34
|
+
statement[SELECT_STMT]['withClause']['WithClause']['ctes'].each do |item|
|
35
|
+
statements << item['CommonTableExpr']['ctequery'] if item['CommonTableExpr']
|
36
36
|
end
|
37
37
|
end
|
38
|
-
elsif statement[
|
39
|
-
statements << statement[
|
40
|
-
statements << statement[
|
38
|
+
elsif statement[SELECT_STMT]['op'] == 1
|
39
|
+
statements << statement[SELECT_STMT]['larg'] if statement[SELECT_STMT]['larg']
|
40
|
+
statements << statement[SELECT_STMT]['rarg'] if statement[SELECT_STMT]['rarg']
|
41
41
|
end
|
42
|
-
elsif statement['
|
43
|
-
condition_items << statement['
|
44
|
-
elsif statement['
|
45
|
-
condition_items << statement['
|
42
|
+
elsif statement['UpdateStmt']
|
43
|
+
condition_items << statement['UpdateStmt']['whereClause'] if statement['UpdateStmt']['whereClause']
|
44
|
+
elsif statement['DeleteStmt']
|
45
|
+
condition_items << statement['DeleteStmt']['whereClause'] if statement['DeleteStmt']['whereClause']
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
49
|
# Process both JOIN and WHERE conditions here
|
50
50
|
next_item = condition_items.shift
|
51
51
|
if next_item
|
52
|
-
if next_item
|
52
|
+
if next_item[A_EXPR]
|
53
53
|
%w(lexpr rexpr).each do |side|
|
54
54
|
expr = next_item.values[0][side]
|
55
55
|
next unless expr && expr.is_a?(Hash)
|
56
56
|
condition_items << expr
|
57
57
|
end
|
58
|
-
elsif next_item[
|
59
|
-
condition_items += next_item[
|
60
|
-
elsif next_item[
|
61
|
-
|
58
|
+
elsif next_item[BOOL_EXPR]
|
59
|
+
condition_items += next_item[BOOL_EXPR]['args']
|
60
|
+
elsif next_item[ROW_EXPR]
|
61
|
+
condition_items += next_item[ROW_EXPR]['args']
|
62
|
+
elsif next_item[COLUMN_REF]
|
63
|
+
column, table = next_item[COLUMN_REF]['fields'].map { |f| f['String']['str'] }.reverse
|
62
64
|
filter_columns << [@aliases[table] || table, column]
|
63
|
-
elsif next_item[
|
64
|
-
condition_items << next_item[
|
65
|
-
elsif next_item[
|
65
|
+
elsif next_item[NULL_TEST]
|
66
|
+
condition_items << next_item[NULL_TEST]['arg']
|
67
|
+
elsif next_item[FUNC_CALL]
|
66
68
|
# FIXME: This should actually be extracted as a funccall and be compared with those indices
|
67
|
-
condition_items += next_item[
|
68
|
-
elsif next_item[
|
69
|
-
condition_items << next_item[
|
70
|
-
statements << next_item[
|
69
|
+
condition_items += next_item[FUNC_CALL]['args'] if next_item[FUNC_CALL]['args']
|
70
|
+
elsif next_item[SUB_LINK]
|
71
|
+
condition_items << next_item[SUB_LINK]['testexpr']
|
72
|
+
statements << next_item[SUB_LINK]['subselect']
|
71
73
|
end
|
72
74
|
end
|
73
75
|
|
@@ -82,16 +84,16 @@ class PgQuery
|
|
82
84
|
def conditions_from_join_clauses(from_clause)
|
83
85
|
condition_items = []
|
84
86
|
from_clause.each do |item|
|
85
|
-
next unless item[
|
87
|
+
next unless item[JOIN_EXPR]
|
86
88
|
|
87
|
-
joinexpr_items = [item[
|
89
|
+
joinexpr_items = [item[JOIN_EXPR]]
|
88
90
|
loop do
|
89
91
|
next_item = joinexpr_items.shift
|
90
92
|
break unless next_item
|
91
93
|
condition_items << next_item['quals'] if next_item['quals']
|
92
94
|
%w(larg rarg).each do |side|
|
93
|
-
next unless next_item[side][
|
94
|
-
joinexpr_items << next_item[side][
|
95
|
+
next unless next_item[side][JOIN_EXPR]
|
96
|
+
joinexpr_items << next_item[side][JOIN_EXPR]
|
95
97
|
end
|
96
98
|
end
|
97
99
|
end
|
data/lib/pg_query/fingerprint.rb
CHANGED
@@ -1,37 +1,106 @@
|
|
1
1
|
require 'digest'
|
2
2
|
|
3
3
|
class PgQuery
|
4
|
-
def fingerprint
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
4
|
+
def fingerprint
|
5
|
+
hash = Digest::SHA1.new
|
6
|
+
fingerprint_tree(hash)
|
7
|
+
format('%02x', FINGERPRINT_VERSION) + hash.hexdigest
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
FINGERPRINT_VERSION = 1
|
13
|
+
|
14
|
+
class FingerprintSubHash
|
15
|
+
attr_reader :parts
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@parts = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def update(part)
|
22
|
+
@parts << part
|
23
|
+
end
|
24
|
+
|
25
|
+
def flush_to(hash)
|
26
|
+
parts.each do |part|
|
27
|
+
hash.update part
|
15
28
|
end
|
16
29
|
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def ignored_fingerprint_value?(val)
|
33
|
+
[nil, 0, false, [], ''].include?(val)
|
34
|
+
end
|
35
|
+
|
36
|
+
def fingerprint_value(val, hash, field_name, need_to_write_name)
|
37
|
+
return if ignored_fingerprint_value?(val)
|
38
|
+
|
39
|
+
subhash = FingerprintSubHash.new
|
40
|
+
|
41
|
+
if val.is_a?(Hash)
|
42
|
+
fingerprint_node(val, subhash, field_name)
|
43
|
+
elsif val.is_a?(Array)
|
44
|
+
fingerprint_list(val, subhash, field_name)
|
45
|
+
else
|
46
|
+
subhash.update val.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
return if subhash.parts.empty?
|
17
50
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
51
|
+
hash.update(field_name) if need_to_write_name
|
52
|
+
subhash.flush_to(hash)
|
53
|
+
end
|
54
|
+
|
55
|
+
def fingerprint_node(node, hash, parent_field_name = nil) # rubocop:disable Metrics/CyclomaticComplexity
|
56
|
+
node_name = node.keys.first
|
57
|
+
return if [A_CONST, ALIAS, PARAM_REF, SET_TO_DEFAULT, INT_LIST, OID_LIST, NULL].include?(node_name)
|
58
|
+
|
59
|
+
hash.update node_name
|
60
|
+
|
61
|
+
node.values.first.sort_by { |k, _| k }.each do |field_name, val|
|
62
|
+
next if ignored_fingerprint_value?(val)
|
63
|
+
|
64
|
+
case field_name
|
65
|
+
when 'location'
|
66
|
+
next
|
67
|
+
when 'name'
|
68
|
+
next if node_name == RES_TARGET && parent_field_name == TARGET_LIST_FIELD
|
69
|
+
next if [PREPARE_STMT, EXECUTE_STMT, DEALLOCATE_STMT].include?(node_name)
|
70
|
+
when 'gid'
|
71
|
+
next if node_name == TRANSACTION_STMT
|
72
|
+
when 'options'
|
73
|
+
next if node_name == TRANSACTION_STMT
|
74
|
+
end
|
75
|
+
|
76
|
+
fingerprint_value(val, hash, field_name, true)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def fingerprint_list(values, hash, parent_field_name)
|
81
|
+
if [FROM_CLAUSE_FIELD, TARGET_LIST_FIELD, COLS_FIELD, REXPR_FIELD].include?(parent_field_name)
|
82
|
+
values_subhashes = values.map do |val|
|
83
|
+
subhash = FingerprintSubHash.new
|
84
|
+
fingerprint_value(val, subhash, parent_field_name, false)
|
85
|
+
subhash
|
86
|
+
end
|
87
|
+
|
88
|
+
values_subhashes.uniq!(&:parts)
|
89
|
+
values_subhashes.sort_by!(&:parts)
|
90
|
+
|
91
|
+
values_subhashes.each do |subhash|
|
92
|
+
subhash.flush_to(hash)
|
93
|
+
end
|
94
|
+
else
|
95
|
+
values.each do |val|
|
96
|
+
fingerprint_value(val, hash, parent_field_name, false)
|
32
97
|
end
|
33
98
|
end
|
99
|
+
end
|
34
100
|
|
35
|
-
|
101
|
+
def fingerprint_tree(hash)
|
102
|
+
@tree.each do |node|
|
103
|
+
fingerprint_node(node, hash)
|
104
|
+
end
|
36
105
|
end
|
37
106
|
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
class PgQuery
|
2
|
+
# Legacy parsetree from 0.7 and earlier versions - migrate to "tree" format if you can
|
3
|
+
def parsetree # rubocop:disable Metrics/CyclomaticComplexity
|
4
|
+
@parsetree ||= transform_nodes!(@tree) do |node|
|
5
|
+
key = node.keys[0]
|
6
|
+
new_key = LEGACY_NODE_NAMES[key] || key.upcase
|
7
|
+
|
8
|
+
case key
|
9
|
+
when A_CONST
|
10
|
+
transform_parsetree_a_const(node)
|
11
|
+
when A_EXPR
|
12
|
+
transform_string_list(node[A_EXPR]['name'])
|
13
|
+
new_key = LEGACY_A_EXPR_NAMES[node[A_EXPR]['kind']]
|
14
|
+
node[A_EXPR].delete('kind')
|
15
|
+
when COLUMN_REF
|
16
|
+
transform_string_list(node[COLUMN_REF]['fields'])
|
17
|
+
when CREATE_FUNCTION_STMT
|
18
|
+
transform_string_list(node[CREATE_FUNCTION_STMT]['funcname'])
|
19
|
+
when CREATE_TRIG_STMT
|
20
|
+
transform_string_list(node[CREATE_TRIG_STMT]['funcname'])
|
21
|
+
when CONSTRAINT
|
22
|
+
node[CONSTRAINT]['contype'] = LEGACY_CONSTRAINT_TYPES[node[CONSTRAINT]['contype']]
|
23
|
+
transform_string_list(node[CONSTRAINT]['keys'])
|
24
|
+
when COPY_STMT
|
25
|
+
transform_string_list(node[COPY_STMT]['attlist'])
|
26
|
+
when DEF_ELEM
|
27
|
+
node[DEF_ELEM]['arg'] = node[DEF_ELEM]['arg'][INTEGER]['ival'] if node[DEF_ELEM]['arg'].is_a?(Hash) && node[DEF_ELEM]['arg'].keys[0] == INTEGER
|
28
|
+
node[DEF_ELEM]['arg'] = node[DEF_ELEM]['arg'][STRING]['str'] if node[DEF_ELEM]['arg'].is_a?(Hash) && node[DEF_ELEM]['arg'].keys[0] == STRING
|
29
|
+
transform_string_list(node[DEF_ELEM]['arg']) if node[DEF_ELEM]['arg'].is_a?(Array)
|
30
|
+
when DROP_STMT
|
31
|
+
node[DROP_STMT]['objects'].each { |obj| transform_string_list(obj) }
|
32
|
+
when FUNC_CALL
|
33
|
+
transform_string_list(node[FUNC_CALL]['funcname'])
|
34
|
+
when GRANT_ROLE_STMT
|
35
|
+
transform_string_list(node[GRANT_ROLE_STMT]['grantee_roles'])
|
36
|
+
when TYPE_NAME
|
37
|
+
transform_string_list(node[TYPE_NAME]['names'])
|
38
|
+
end
|
39
|
+
|
40
|
+
node[new_key] = node.delete(key)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
LEGACY_NODE_NAMES = {
|
47
|
+
SELECT_STMT => 'SELECT',
|
48
|
+
ALTER_TABLE_CMD => 'ALTER TABLE CMD',
|
49
|
+
ALTER_TABLE_STMT => 'ALTER TABLE',
|
50
|
+
CHECK_POINT_STMT => 'CHECKPOINT',
|
51
|
+
CREATE_SCHEMA_STMT => 'CREATE SCHEMA',
|
52
|
+
CREATE_TABLE_AS_STMT => 'CREATE TABLE AS',
|
53
|
+
COPY_STMT => 'COPY',
|
54
|
+
DELETE_STMT => 'DELETE FROM',
|
55
|
+
DROP_STMT => 'DROP',
|
56
|
+
INSERT_STMT => 'INSERT INTO',
|
57
|
+
EXPLAIN_STMT => 'EXPLAIN',
|
58
|
+
LOCK_STMT => 'LOCK',
|
59
|
+
TRANSACTION_STMT => 'TRANSACTION',
|
60
|
+
TRUNCATE_STMT => 'TRUNCATE',
|
61
|
+
UPDATE_STMT => 'UPDATE',
|
62
|
+
VACUUM_STMT => 'VACUUM',
|
63
|
+
VARIABLE_SET_STMT => 'SET',
|
64
|
+
VARIABLE_SHOW_STMT => 'SHOW'
|
65
|
+
# All others default to simply upper-casing the input name
|
66
|
+
}.freeze
|
67
|
+
|
68
|
+
LEGACY_A_EXPR_NAMES = {
|
69
|
+
AEXPR_OP => 'AEXPR',
|
70
|
+
# AEXPR_OP_ANY = 1 # normal operator
|
71
|
+
# AEXPR_OP_ALL = 2 # scalar op ALL (array)
|
72
|
+
# AEXPR_DISTINCT = 3 # IS DISTINCT FROM - name must be "="
|
73
|
+
# AEXPR_NULLIF = 4 # NULLIF - name must be "="
|
74
|
+
# AEXPR_OF = 5 # IS [NOT] OF - name must be "=" or "<>"
|
75
|
+
# AEXPR_IN = 6 # [NOT] IN - name must be "=" or "<>"
|
76
|
+
# AEXPR_LIKE = 7 # [NOT] LIKE - name must be "~~" or "!~~"
|
77
|
+
# AEXPR_ILIKE = 8 # [NOT] ILIKE - name must be "~~*" or "!~~*"
|
78
|
+
AEXPR_SIMILAR => 'AEXPR',
|
79
|
+
# AEXPR_BETWEEN = 10 # name must be "BETWEEN"
|
80
|
+
# AEXPR_NOT_BETWEEN = 11 # name must be "NOT BETWEEN"
|
81
|
+
# AEXPR_BETWEEN_SYM = 12 # name must be "BETWEEN SYMMETRIC"
|
82
|
+
# AEXPR_NOT_BETWEEN_SYM = 13 # name must be "NOT BETWEEN SYMMETRIC"
|
83
|
+
# AEXPR_PAREN = 14 # nameless dummy node for parentheses
|
84
|
+
}.freeze
|
85
|
+
|
86
|
+
LEGACY_CONSTRAINT_TYPES = {
|
87
|
+
# CONSTR_TYPE_NULL = 0 # not standard SQL, but a lot of people expect it
|
88
|
+
# CONSTR_TYPE_NOTNULL = 1
|
89
|
+
# CONSTR_TYPE_DEFAULT = 2
|
90
|
+
# CONSTR_TYPE_CHECK = 3
|
91
|
+
CONSTR_TYPE_PRIMARY => 'PRIMARY_KEY',
|
92
|
+
# CONSTR_TYPE_UNIQUE = 5
|
93
|
+
# CONSTR_TYPE_EXCLUSION = 6
|
94
|
+
# CONSTR_TYPE_FOREIGN = 7
|
95
|
+
# CONSTR_TYPE_ATTR_DEFERRABLE = 8 # attributes for previous constraint node
|
96
|
+
# CONSTR_TYPE_ATTR_NOT_DEFERRABLE = 9
|
97
|
+
# CONSTR_TYPE_ATTR_DEFERRED = 10
|
98
|
+
# CONSTR_TYPE_ATTR_IMMEDIATE = 11
|
99
|
+
}.freeze
|
100
|
+
|
101
|
+
def transform_parsetree_a_const(node)
|
102
|
+
type_key = node[A_CONST]['val'].keys[0]
|
103
|
+
|
104
|
+
case type_key
|
105
|
+
when INTEGER
|
106
|
+
node[A_CONST]['type'] = 'integer'
|
107
|
+
node[A_CONST]['val'] = node[A_CONST]['val'][INTEGER]['ival']
|
108
|
+
when STRING
|
109
|
+
node[A_CONST]['type'] = 'integer'
|
110
|
+
node[A_CONST]['val'] = node[A_CONST]['val'][STRING]['ival']
|
111
|
+
when FLOAT
|
112
|
+
node[A_CONST]['type'] = 'float'
|
113
|
+
node[A_CONST]['val'] = node[A_CONST]['val'][FLOAT]['str'].to_f
|
114
|
+
when BIT_STRING
|
115
|
+
node[A_CONST]['type'] = 'bitstring'
|
116
|
+
node[A_CONST]['val'] = node[A_CONST]['val'][BIT_STRING]['str']
|
117
|
+
when NULL
|
118
|
+
node[A_CONST]['type'] = 'null'
|
119
|
+
node[A_CONST]['val'] = nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def transform_string_list(list)
|
124
|
+
return if list.nil?
|
125
|
+
|
126
|
+
list.map! { |node| node.keys[0] == STRING ? node[STRING]['str'] : node }
|
127
|
+
end
|
128
|
+
end
|