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.
@@ -0,0 +1,218 @@
1
+ # rubocop:disable Style/ConstantName
2
+ class PgQuery
3
+ # NODE TYPES
4
+
5
+ A_ARRAY_EXPR = 'A_ArrayExpr'.freeze
6
+ A_CONST = 'A_Const'.freeze
7
+ A_EXPR = 'A_Expr'.freeze
8
+ A_INDICES = 'A_Indices'.freeze
9
+ A_INDIRECTION = 'A_Indirection'.freeze
10
+ A_STAR = 'A_Star'.freeze
11
+ ACCESS_PRIV = 'AccessPriv'.freeze
12
+ ALIAS = 'Alias'.freeze
13
+ ALTER_TABLE_CMD = 'AlterTableCmd'.freeze
14
+ ALTER_TABLE_STMT = 'AlterTableStmt'.freeze
15
+ BIT_STRING = 'BitString'.freeze
16
+ BOOL_EXPR = 'BoolExpr'.freeze
17
+ CASE_EXPR = 'CaseExpr'.freeze
18
+ CASE_WHEN = 'CaseWhen'.freeze
19
+ CHECK_POINT_STMT = 'CheckPointStmt'.freeze
20
+ COALESCE_EXPR = 'CoalesceExpr'.freeze
21
+ COLUMN_DEF = 'ColumnDef'.freeze
22
+ COLUMN_REF = 'ColumnRef'.freeze
23
+ COMMON_TABLE_EXPR = 'CommonTableExpr'.freeze
24
+ CONSTRAINT = 'Constraint'.freeze
25
+ COPY_STMT = 'CopyStmt'.freeze
26
+ CREATE_FUNCTION_STMT = 'CreateFunctionStmt'.freeze
27
+ CREATE_SCHEMA_STMT = 'CreateSchemaStmt'.freeze
28
+ CREATE_STMT = 'CreateStmt'.freeze
29
+ CREATE_TABLE_AS_STMT = 'CreateTableAsStmt'.freeze
30
+ CREATE_TRIG_STMT = 'CreateTrigStmt'.freeze
31
+ DEF_ELEM = 'DefElem'.freeze
32
+ DELETE_STMT = 'DeleteStmt'.freeze
33
+ DROP_STMT = 'DropStmt'.freeze
34
+ EXPLAIN_STMT = 'ExplainStmt'.freeze
35
+ FLOAT = 'Float'.freeze
36
+ FUNC_CALL = 'FuncCall'.freeze
37
+ FUNCTION_PARAMETER = 'FunctionParameter'.freeze
38
+ GRANT_STMT = 'GrantStmt'.freeze
39
+ GRANT_ROLE_STMT = 'GrantRoleStmt'.freeze
40
+ INDEX_ELEM = 'IndexElem'.freeze
41
+ INDEX_STMT = 'IndexStmt'.freeze
42
+ INSERT_STMT = 'InsertStmt'.freeze
43
+ INTO_CLAUSE = 'IntoClause'.freeze
44
+ JOIN_EXPR = 'JoinExpr'.freeze
45
+ LOCK_STMT = 'LockStmt'.freeze
46
+ LOCKING_CLAUSE = 'LockingClause'.freeze
47
+ NULL_TEST = 'NullTest'.freeze
48
+ RANGE_FUNCTION = 'RangeFunction'.freeze
49
+ PARAM_REF = 'ParamRef'.freeze
50
+ RANGE_SUBSELECT = 'RangeSubselect'.freeze
51
+ RANGE_VAR = 'RangeVar'.freeze
52
+ REFRESH_MAT_VIEW_STMT = 'RefreshMatViewStmt'.freeze
53
+ RENAME_STMT = 'RenameStmt'.freeze
54
+ RES_TARGET = 'ResTarget'.freeze
55
+ ROW_EXPR = 'RowExpr'.freeze
56
+ RULE_STMT = 'RuleStmt'.freeze
57
+ ROLE_SPEC = 'RoleSpec'.freeze
58
+ SELECT_STMT = 'SelectStmt'.freeze
59
+ SORT_BY = 'SortBy'.freeze
60
+ SUB_LINK = 'SubLink'.freeze
61
+ TRANSACTION_STMT = 'TransactionStmt'.freeze
62
+ TRUNCATE_STMT = 'TruncateStmt'.freeze
63
+ TYPE_CAST = 'TypeCast'.freeze
64
+ TYPE_NAME = 'TypeName'.freeze
65
+ UPDATE_STMT = 'UpdateStmt'.freeze
66
+ VACUUM_STMT = 'VacuumStmt'.freeze
67
+ VARIABLE_SET_STMT = 'VariableSetStmt'.freeze
68
+ VARIABLE_SHOW_STMT = 'VariableShowStmt'.freeze
69
+ VIEW_STMT = 'ViewStmt'.freeze
70
+ WINDOW_DEF = 'WindowDef'.freeze
71
+ WITH_CLAUSE = 'WithClause'.freeze
72
+ STRING = 'String'.freeze
73
+ INTEGER = 'Integer'.freeze
74
+ SET_TO_DEFAULT = 'SetToDefault'.freeze
75
+ PREPARE_STMT = 'PrepareStmt'.freeze
76
+ EXECUTE_STMT = 'ExecuteStmt'.freeze
77
+ DEALLOCATE_STMT = 'DeallocateStmt'.freeze
78
+ NULL = 'Null'.freeze
79
+ INT_LIST = 'IntList'.freeze
80
+ OID_LIST = 'OidList'.freeze
81
+
82
+ # FIELDS
83
+
84
+ FROM_CLAUSE_FIELD = 'fromClause'.freeze
85
+ TARGET_LIST_FIELD = 'targetList'.freeze
86
+ COLS_FIELD = 'cols'.freeze
87
+ REXPR_FIELD = 'rexpr'.freeze
88
+
89
+ # ENUMS
90
+
91
+ CONSTR_TYPE_NULL = 0 # not standard SQL, but a lot of people expect it
92
+ CONSTR_TYPE_NOTNULL = 1
93
+ CONSTR_TYPE_DEFAULT = 2
94
+ CONSTR_TYPE_CHECK = 3
95
+ CONSTR_TYPE_PRIMARY = 4
96
+ CONSTR_TYPE_UNIQUE = 5
97
+ CONSTR_TYPE_EXCLUSION = 6
98
+ CONSTR_TYPE_FOREIGN = 7
99
+ CONSTR_TYPE_ATTR_DEFERRABLE = 8 # attributes for previous constraint node
100
+ CONSTR_TYPE_ATTR_NOT_DEFERRABLE = 9
101
+ CONSTR_TYPE_ATTR_DEFERRED = 10
102
+ CONSTR_TYPE_ATTR_IMMEDIATE = 11
103
+
104
+ OBJECT_TYPE_INDEX = 19
105
+ OBJECT_TYPE_RULE = 28
106
+ OBJECT_TYPE_SCHEMA = 29
107
+ OBJECT_TYPE_TABLE = 32
108
+ OBJECT_TYPE_TRIGGER = 35
109
+ OBJECT_TYPE_VIEW = 42
110
+
111
+ BOOL_EXPR_AND = 0
112
+ BOOL_EXPR_OR = 1
113
+ BOOL_EXPR_NOT = 2
114
+
115
+ AEXPR_OP = 0 # normal operator
116
+ AEXPR_OP_ANY = 1 # scalar op ANY (array)
117
+ AEXPR_OP_ALL = 2 # scalar op ALL (array)
118
+ AEXPR_DISTINCT = 3 # IS DISTINCT FROM - name must be "="
119
+ AEXPR_NULLIF = 4 # NULLIF - name must be "="
120
+ AEXPR_OF = 5 # IS [NOT] OF - name must be "=" or "<>"
121
+ AEXPR_IN = 6 # [NOT] IN - name must be "=" or "<>"
122
+ AEXPR_LIKE = 7 # [NOT] LIKE - name must be "~~" or "!~~"
123
+ AEXPR_ILIKE = 8 # [NOT] ILIKE - name must be "~~*" or "!~~*"
124
+ AEXPR_SIMILAR = 9 # [NOT] SIMILAR - name must be "~" or "!~"
125
+ AEXPR_BETWEEN = 10 # name must be "BETWEEN"
126
+ AEXPR_NOT_BETWEEN = 11 # name must be "NOT BETWEEN"
127
+ AEXPR_BETWEEN_SYM = 12 # name must be "BETWEEN SYMMETRIC"
128
+ AEXPR_NOT_BETWEEN_SYM = 13 # name must be "NOT BETWEEN SYMMETRIC"
129
+ AEXPR_PAREN = 14 # nameless dummy node for parentheses
130
+
131
+ TRANS_STMT_BEGIN = 0
132
+ TRANS_STMT_START = 1 # semantically identical to BEGIN
133
+ TRANS_STMT_COMMIT = 2
134
+ TRANS_STMT_ROLLBACK = 3
135
+ TRANS_STMT_SAVEPOINT = 4
136
+ TRANS_STMT_RELEASE = 5
137
+ TRANS_STMT_ROLLBACK_TO = 6
138
+ TRANS_STMT_PREPARE = 7
139
+ TRANS_STMT_COMMIT_PREPARED = 8
140
+ TRANS_STMT_ROLLBACK_PREPARED = 9
141
+
142
+ SUBLINK_TYPE_EXISTS = 0 # EXISTS(SELECT ...)
143
+ SUBLINK_TYPE_ALL = 1 # (lefthand) op ALL (SELECT ...)
144
+ SUBLINK_TYPE_ANY = 2 # (lefthand) op ANY (SELECT ...)
145
+ SUBLINK_TYPE_ROWCOMPARE = 3 # (lefthand) op (SELECT ...)
146
+ SUBLINK_TYPE_EXPR = 4 # (SELECT with single targetlist item ...)
147
+ SUBLINK_TYPE_MULTIEXPR = 5 # (SELECT with multiple targetlist items ...)
148
+ SUBLINK_TYPE_ARRAY = 6 # ARRAY(SELECT with single targetlist item ...)
149
+ SUBLINK_TYPE_CTE = 7 # WITH query (never actually part of an expression), for SubPlans only
150
+
151
+ LCS_NONE = 0 # no such clause - only used in PlanRowMark
152
+ LCS_FORKEYSHARE = 1 # FOR KEY SHARE
153
+ LCS_FORSHARE = 2 # FOR SHARE
154
+ LCS_FORNOKEYUPDATE = 3 # FOR NO KEY UPDATE
155
+ LCS_FORUPDATE = 4 # FOR UPDATE
156
+
157
+ AT_AddColumn = 0 # add column
158
+ AT_AddColumnRecurse = 1 # internal to commands/tablecmds.c
159
+ AT_AddColumnToView = 2 # implicitly via CREATE OR REPLACE VIEW
160
+ AT_ColumnDefault = 3 # alter column default
161
+ AT_DropNotNull = 4 # alter column drop not null
162
+ AT_SetNotNull = 5 # alter column set not null
163
+ AT_SetStatistics = 6 # alter column set statistics
164
+ AT_SetOptions = 7 # alter column set ( options )
165
+ AT_ResetOptions = 8 # alter column reset ( options )
166
+ AT_SetStorage = 9 # alter column set storage
167
+ AT_DropColumn = 10 # drop column
168
+ AT_DropColumnRecurse = 11 # internal to commands/tablecmds.c
169
+ AT_AddIndex = 12 # add index
170
+ AT_ReAddIndex = 13 # internal to commands/tablecmds.c
171
+ AT_AddConstraint = 14 # add constraint
172
+ AT_AddConstraintRecurse = 15 # internal to commands/tablecmds.c
173
+ AT_ReAddConstraint = 16 # internal to commands/tablecmds.c
174
+ AT_AlterConstraint = 17 # alter constraint
175
+ AT_ValidateConstraint = 18 # validate constraint
176
+ AT_ValidateConstraintRecurse = 19 # internal to commands/tablecmds.c
177
+ AT_ProcessedConstraint = 20 # pre-processed add constraint (local in parser/parse_utilcmd.c)
178
+ AT_AddIndexConstraint = 21 # add constraint using existing index
179
+ AT_DropConstraint = 22 # drop constraint
180
+ AT_DropConstraintRecurse = 23 # internal to commands/tablecmds.c
181
+ AT_ReAddComment = 24 # internal to commands/tablecmds.c
182
+ AT_AlterColumnType = 25 # alter column type
183
+ AT_AlterColumnGenericOptions = 26 # alter column OPTIONS (...)
184
+ AT_ChangeOwner = 27 # change owner
185
+ AT_ClusterOn = 28 # CLUSTER ON
186
+ AT_DropCluster = 29 # SET WITHOUT CLUSTER
187
+ AT_SetLogged = 30 # SET LOGGED
188
+ AT_SetUnLogged = 31 # SET UNLOGGED
189
+ AT_AddOids = 32 # SET WITH OIDS
190
+ AT_AddOidsRecurse = 33 # internal to commands/tablecmds.c
191
+ AT_DropOids = 34 # SET WITHOUT OIDS
192
+ AT_SetTableSpace = 35 # SET TABLESPACE
193
+ AT_SetRelOptions = 36 # SET (...) -- AM specific parameters
194
+ AT_ResetRelOptions = 37 # RESET (...) -- AM specific parameters
195
+ AT_ReplaceRelOptions = 38 # replace reloption list in its entirety
196
+ AT_EnableTrig = 39 # ENABLE TRIGGER name
197
+ AT_EnableAlwaysTrig = 40 # ENABLE ALWAYS TRIGGER name
198
+ AT_EnableReplicaTrig = 41 # ENABLE REPLICA TRIGGER name
199
+ AT_DisableTrig = 42 # DISABLE TRIGGER name
200
+ AT_EnableTrigAll = 43 # ENABLE TRIGGER ALL
201
+ AT_DisableTrigAll = 44 # DISABLE TRIGGER ALL
202
+ AT_EnableTrigUser = 45 # ENABLE TRIGGER USER
203
+ AT_DisableTrigUser = 46 # DISABLE TRIGGER USER
204
+ AT_EnableRule = 47 # ENABLE RULE name
205
+ AT_EnableAlwaysRule = 48 # ENABLE ALWAYS RULE name
206
+ AT_EnableReplicaRule = 49 # ENABLE REPLICA RULE name
207
+ AT_DisableRule = 50 # DISABLE RULE name
208
+ AT_AddInherit = 51 # INHERIT parent
209
+ AT_DropInherit = 52 # NO INHERIT parent
210
+ AT_AddOf = 53 # OF <type_name>
211
+ AT_DropOf = 54 # NOT OF
212
+ AT_ReplicaIdentity = 55 # REPLICA IDENTITY
213
+ AT_EnableRowSecurity = 56 # ENABLE ROW SECURITY
214
+ AT_DisableRowSecurity = 57 # DISABLE ROW SECURITY
215
+ AT_ForceRowSecurity = 58 # FORCE ROW SECURITY
216
+ AT_NoForceRowSecurity = 59 # NO FORCE ROW SECURITY
217
+ AT_GenericOptions = 60 # OPTIONS (...)
218
+ end
@@ -2,22 +2,22 @@ class PgQuery
2
2
  def param_refs # rubocop:disable Metrics/CyclomaticComplexity
3
3
  results = []
4
4
 
5
- treewalker! parsetree do |_, _, v|
5
+ treewalker! @tree do |_, _, v|
6
6
  next unless v.is_a?(Hash)
7
7
 
8
- if v['PARAMREF']
9
- results << { 'location' => v['PARAMREF']['location'],
10
- 'length' => param_ref_length(v['PARAMREF']) }
11
- elsif v['TYPECAST']
12
- next unless v['TYPECAST']['arg'] && v['TYPECAST']['typeName']
8
+ if v[PARAM_REF]
9
+ results << { 'location' => v[PARAM_REF]['location'],
10
+ 'length' => param_ref_length(v[PARAM_REF]) }
11
+ elsif v[TYPE_CAST]
12
+ next unless v[TYPE_CAST]['arg'] && v[TYPE_CAST]['typeName']
13
13
 
14
- p = v['TYPECAST']['arg'].delete('PARAMREF')
15
- t = v['TYPECAST']['typeName'].delete('TYPENAME')
14
+ p = v[TYPE_CAST]['arg'].delete(PARAM_REF)
15
+ t = v[TYPE_CAST]['typeName'].delete(TYPE_NAME)
16
16
  next unless p && t
17
17
 
18
18
  location = p['location']
19
19
  typeloc = t['location']
20
- typename = t['names'].join('.')
20
+ typename = t['names']
21
21
  length = param_ref_length(p)
22
22
 
23
23
  if typeloc < location
@@ -2,10 +2,10 @@ require 'json'
2
2
 
3
3
  class PgQuery
4
4
  def self.parse(query)
5
- parsetree, stderr = _raw_parse(query)
5
+ tree, stderr = _raw_parse(query)
6
6
 
7
7
  begin
8
- parsetree = JSON.parse(parsetree, max_nesting: 1000)
8
+ tree = JSON.parse(tree, max_nesting: 1000)
9
9
  rescue JSON::ParserError
10
10
  raise ParseError.new('Failed to parse JSON', __FILE__, __LINE__, -1)
11
11
  end
@@ -16,16 +16,16 @@ class PgQuery
16
16
  warnings << line.strip
17
17
  end
18
18
 
19
- PgQuery.new(query, parsetree, warnings)
19
+ PgQuery.new(query, tree, warnings)
20
20
  end
21
21
 
22
22
  attr_reader :query
23
- attr_reader :parsetree
23
+ attr_reader :tree
24
24
  attr_reader :warnings
25
25
 
26
- def initialize(query, parsetree, warnings = [])
26
+ def initialize(query, tree, warnings = [])
27
27
  @query = query
28
- @parsetree = parsetree
28
+ @tree = tree
29
29
  @warnings = warnings
30
30
  end
31
31
 
@@ -45,7 +45,7 @@ class PgQuery
45
45
  @tables = []
46
46
  @aliases = {}
47
47
 
48
- statements = @parsetree.dup
48
+ statements = @tree.dup
49
49
  from_clause_items = []
50
50
  where_clause_items = []
51
51
 
@@ -53,44 +53,44 @@ class PgQuery
53
53
  statement = statements.shift
54
54
  if statement
55
55
  case statement.keys[0]
56
- when 'SELECT'
57
- if statement['SELECT']['op'] == 0
58
- (statement['SELECT']['fromClause'] || []).each do |item|
59
- if item['RANGESUBSELECT']
60
- statements << item['RANGESUBSELECT']['subquery']
56
+ when SELECT_STMT
57
+ if statement[SELECT_STMT]['op'] == 0
58
+ (statement[SELECT_STMT][FROM_CLAUSE_FIELD] || []).each do |item|
59
+ if item[RANGE_SUBSELECT]
60
+ statements << item[RANGE_SUBSELECT]['subquery']
61
61
  else
62
62
  from_clause_items << item
63
63
  end
64
64
  end
65
65
 
66
66
  # CTEs
67
- if statement['SELECT']['withClause']
68
- statement['SELECT']['withClause']['WITHCLAUSE']['ctes'].each do |item|
69
- statements << item['COMMONTABLEEXPR']['ctequery'] if item['COMMONTABLEEXPR']
67
+ if statement[SELECT_STMT]['withClause']
68
+ statement[SELECT_STMT]['withClause'][WITH_CLAUSE]['ctes'].each do |item|
69
+ statements << item[COMMON_TABLE_EXPR]['ctequery'] if item[COMMON_TABLE_EXPR]
70
70
  end
71
71
  end
72
- elsif statement['SELECT']['op'] == 1
73
- statements << statement['SELECT']['larg'] if statement['SELECT']['larg']
74
- statements << statement['SELECT']['rarg'] if statement['SELECT']['rarg']
72
+ elsif statement[SELECT_STMT]['op'] == 1
73
+ statements << statement[SELECT_STMT]['larg'] if statement[SELECT_STMT]['larg']
74
+ statements << statement[SELECT_STMT]['rarg'] if statement[SELECT_STMT]['rarg']
75
75
  end
76
- when 'INSERT INTO', 'UPDATE', 'DELETE FROM', 'VACUUM', 'COPY', 'ALTER TABLE', 'CREATESTMT', 'INDEXSTMT', 'RULESTMT', 'CREATETRIGSTMT'
76
+ when INSERT_STMT, UPDATE_STMT, DELETE_STMT, VACUUM_STMT, COPY_STMT, ALTER_TABLE_STMT, CREATE_STMT, INDEX_STMT, RULE_STMT, CREATE_TRIG_STMT
77
77
  from_clause_items << statement.values[0]['relation']
78
- when 'VIEWSTMT'
79
- from_clause_items << statement['VIEWSTMT']['view']
80
- statements << statement['VIEWSTMT']['query']
81
- when 'REFRESHMATVIEWSTMT'
82
- from_clause_items << statement['REFRESHMATVIEWSTMT']['relation']
83
- when 'EXPLAIN'
84
- statements << statement['EXPLAIN']['query']
85
- when 'CREATE TABLE AS'
86
- if statement['CREATE TABLE AS']['into'] && statement['CREATE TABLE AS']['into']['INTOCLAUSE']['rel']
87
- from_clause_items << statement['CREATE TABLE AS']['into']['INTOCLAUSE']['rel']
78
+ when VIEW_STMT
79
+ from_clause_items << statement[VIEW_STMT]['view']
80
+ statements << statement[VIEW_STMT]['query']
81
+ when REFRESH_MAT_VIEW_STMT
82
+ from_clause_items << statement[REFRESH_MAT_VIEW_STMT]['relation']
83
+ when EXPLAIN_STMT
84
+ statements << statement[EXPLAIN_STMT]['query']
85
+ when CREATE_TABLE_AS_STMT
86
+ if statement[CREATE_TABLE_AS_STMT]['into'] && statement[CREATE_TABLE_AS_STMT]['into'][INTO_CLAUSE]['rel']
87
+ from_clause_items << statement[CREATE_TABLE_AS_STMT]['into'][INTO_CLAUSE]['rel']
88
88
  end
89
- when 'LOCK', 'TRUNCATE'
89
+ when LOCK_STMT, TRUNCATE_STMT
90
90
  from_clause_items += statement.values[0]['relations']
91
- when 'GRANTSTMT'
92
- objects = statement['GRANTSTMT']['objects']
93
- case statement['GRANTSTMT']['objtype']
91
+ when GRANT_STMT
92
+ objects = statement[GRANT_STMT]['objects']
93
+ case statement[GRANT_STMT]['objtype']
94
94
  when 0 # Column
95
95
  # FIXME
96
96
  when 1 # Table
@@ -98,14 +98,12 @@ class PgQuery
98
98
  when 2 # Sequence
99
99
  # FIXME
100
100
  end
101
- when 'DROP'
102
- objects = statement['DROP']['objects']
103
- case statement['DROP']['removeType']
104
- when 26 # Table
101
+ when DROP_STMT
102
+ objects = statement[DROP_STMT]['objects'].map { |list| list.map { |obj| obj['String']['str'] } }
103
+ case statement[DROP_STMT]['removeType']
104
+ when OBJECT_TYPE_TABLE
105
105
  @tables += objects.map { |r| r.join('.') }
106
- when 23 # Rule
107
- @tables += objects.map { |r| r[0..-2].join('.') }
108
- when 28 # Trigger
106
+ when OBJECT_TYPE_RULE, OBJECT_TYPE_TRIGGER
109
107
  @tables += objects.map { |r| r[0..-2].join('.') }
110
108
  end
111
109
  end
@@ -117,7 +115,7 @@ class PgQuery
117
115
  next_item = where_clause_items.shift
118
116
  if next_item
119
117
  case next_item.keys[0]
120
- when /^AEXPR/, 'ANY'
118
+ when A_EXPR
121
119
  %w(lexpr rexpr).each do |side|
122
120
  elem = next_item.values[0][side]
123
121
  next unless elem
@@ -127,8 +125,8 @@ class PgQuery
127
125
  where_clause_items << elem
128
126
  end
129
127
  end
130
- when 'SUBLINK'
131
- statements << next_item['SUBLINK']['subselect']
128
+ when SUB_LINK
129
+ statements << next_item[SUB_LINK]['subselect']
132
130
  end
133
131
  end
134
132
 
@@ -140,17 +138,17 @@ class PgQuery
140
138
  break unless next_item
141
139
 
142
140
  case next_item.keys[0]
143
- when 'JOINEXPR'
141
+ when JOIN_EXPR
144
142
  %w(larg rarg).each do |side|
145
- from_clause_items << next_item['JOINEXPR'][side]
143
+ from_clause_items << next_item[JOIN_EXPR][side]
146
144
  end
147
- when 'ROW'
148
- from_clause_items += next_item['ROW']['args']
149
- when 'RANGEVAR'
150
- rangevar = next_item['RANGEVAR']
145
+ when ROW_EXPR
146
+ from_clause_items += next_item[ROW_EXPR]['args']
147
+ when RANGE_VAR
148
+ rangevar = next_item[RANGE_VAR]
151
149
  table = [rangevar['schemaname'], rangevar['relname']].compact.join('.')
152
150
  @tables << table
153
- @aliases[rangevar['alias']['ALIAS']['aliasname']] = table if rangevar['alias']
151
+ @aliases[rangevar['alias'][ALIAS]['aliasname']] = table if rangevar['alias']
154
152
  end
155
153
  end
156
154
 
@@ -30,18 +30,24 @@ class PgQuery
30
30
  end
31
31
  end
32
32
 
33
- def deep_dup(obj)
34
- case obj
35
- when Hash
36
- obj.each_with_object(obj.dup) do |(key, value), hash|
37
- hash[deep_dup(key)] = deep_dup(value)
33
+ def transform_nodes!(parsetree, &block)
34
+ result = deep_dup(parsetree)
35
+ exprs = result.dup
36
+
37
+ loop do
38
+ expr = exprs.shift
39
+
40
+ if expr.is_a?(Hash)
41
+ block.call(expr) if expr.size == 1 && expr.keys[0][/^[A-Z]+/]
42
+
43
+ exprs += expr.values.compact
44
+ elsif expr.is_a?(Array)
45
+ exprs += expr
38
46
  end
39
- when Array
40
- obj.map { |it| deep_dup(it) }
41
- when NilClass, FalseClass, TrueClass, Symbol, Numeric
42
- obj # Can't be duplicated
43
- else
44
- obj.dup
47
+
48
+ break if exprs.empty?
45
49
  end
50
+
51
+ result
46
52
  end
47
53
  end