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.
@@ -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
- # Then the fifth ALTER_TABLE entry ('DropNotNull') will be selected
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 = ALTER_TABLE[ALTER_TABLE_COMMANDS[node['subtype']]]
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
- # From include/nodes/parsenodes.h:1455
24
- #
25
- # Many of these will not be implemented here as they'll never be part of
26
- # valid SQL. We keep them all here in their original order because we look
27
- # these values up by array index.
28
- NOT_IMPLEMENTED = 'NotImplemented'
29
- ALTER_TABLE = {
30
- # add column
31
- 'AddColumn' => -> (_node) { ['ADD COLUMN'] },
32
- # internal to commands/tablecmds.c
33
- 'AddColumnRecurse' => -> (_node) { NotImplemented },
34
- # implicitly via CREATE OR REPLACE VIEW
35
- 'AddColumnToView' => -> (_node) { NotImplemented },
36
- # alter column default
37
- 'ColumnDefault' => -> (node) { ['ALTER COLUMN', node['def'] ? 'SET DEFAULT' : 'DROP DEFAULT'] },
38
- # alter column drop not null
39
- 'DropNotNull' => -> (_node) { ['ALTER COLUMN', 'DROP NOT NULL'] },
40
- # alter column set not null
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 = @parsetree.dup
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['SELECT']
17
- if statement['SELECT']['op'] == 0
18
- if statement['SELECT']['fromClause']
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['SELECT']['fromClause'].each do |item|
21
- next unless item['RANGESUBSELECT']
22
- statements << item['RANGESUBSELECT']['subquery']
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['SELECT']['fromClause'])
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['SELECT']['whereClause'] if statement['SELECT']['whereClause']
30
+ condition_items << statement[SELECT_STMT]['whereClause'] if statement[SELECT_STMT]['whereClause']
31
31
 
32
32
  # CTEs
33
- if statement['SELECT']['withClause']
34
- statement['SELECT']['withClause']['WITHCLAUSE']['ctes'].each do |item|
35
- statements << item['COMMONTABLEEXPR']['ctequery'] if item['COMMONTABLEEXPR']
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['SELECT']['op'] == 1
39
- statements << statement['SELECT']['larg'] if statement['SELECT']['larg']
40
- statements << statement['SELECT']['rarg'] if statement['SELECT']['rarg']
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['UPDATE']
43
- condition_items << statement['UPDATE']['whereClause'] if statement['UPDATE']['whereClause']
44
- elsif statement['DELETE FROM']
45
- condition_items << statement['DELETE FROM']['whereClause'] if statement['DELETE FROM']['whereClause']
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.keys[0].start_with?('AEXPR') || next_item['ANY']
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['ROW']
59
- condition_items += next_item['ROW']['args']
60
- elsif next_item['COLUMNREF']
61
- column, table = next_item['COLUMNREF']['fields'].reverse
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['NULLTEST']
64
- condition_items << next_item['NULLTEST']['arg']
65
- elsif next_item['FUNCCALL']
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['FUNCCALL']['args'] if next_item['FUNCCALL']['args']
68
- elsif next_item['SUBLINK']
69
- condition_items << next_item['SUBLINK']['testexpr']
70
- statements << next_item['SUBLINK']['subselect']
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['JOINEXPR']
87
+ next unless item[JOIN_EXPR]
86
88
 
87
- joinexpr_items = [item['JOINEXPR']]
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]['JOINEXPR']
94
- joinexpr_items << next_item[side]['JOINEXPR']
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
@@ -1,37 +1,106 @@
1
1
  require 'digest'
2
2
 
3
3
  class PgQuery
4
- def fingerprint # rubocop:disable Metrics/CyclomaticComplexity
5
- normalized_parsetree = deep_dup(parsetree)
6
-
7
- # First delete all simple elements and attributes that can be removed
8
- treewalker! normalized_parsetree do |expr, k, v|
9
- if v.is_a?(Hash) && %w(A_CONST ALIAS PARAMREF).include?(v.keys[0])
10
- # Remove constants, aliases and param references from tree
11
- expr[k] = nil
12
- elsif k == 'location'
13
- # Remove location info in order to ignore whitespace and target list ordering
14
- expr.delete(k)
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
- # Now remove all unnecessary info
19
- treewalker! normalized_parsetree do |expr, k, v|
20
- if k == 'AEXPR IN' && v.is_a?(Hash) && v['rexpr'].is_a?(Array)
21
- # Compact identical IN list elements to one
22
- v['rexpr'].uniq!
23
- elsif k == 'targetList' && v.is_a?(Array)
24
- # Remove SELECT target list names & ignore order
25
- v.each { |v2| v2['RESTARGET']['name'] = nil if v2['RESTARGET'] } # Remove names
26
- v.sort_by!(&:to_s)
27
- expr[k] = v
28
- elsif k == 'cols' && v.is_a?(Array)
29
- # Ignore INSERT cols order
30
- v.sort_by!(&:to_s)
31
- expr[k] = v
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
- Digest::SHA1.hexdigest(normalized_parsetree.to_s)
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