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.
@@ -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