pg_query 0.8.0 → 0.9.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 +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
|