pg_query 1.3.0 → 2.0.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 +86 -52
- data/README.md +72 -65
- data/Rakefile +82 -1
- data/ext/pg_query/extconf.rb +2 -39
- data/ext/pg_query/guc-file.c +0 -0
- data/ext/pg_query/pg_query.c +104 -0
- data/ext/pg_query/pg_query.pb-c.c +37628 -0
- data/ext/pg_query/pg_query_deparse.c +9953 -0
- data/ext/pg_query/pg_query_fingerprint.c +292 -0
- data/ext/pg_query/pg_query_fingerprint.h +8 -0
- data/ext/pg_query/pg_query_internal.h +24 -0
- data/ext/pg_query/pg_query_json_plpgsql.c +738 -0
- data/ext/pg_query/pg_query_json_plpgsql.h +9 -0
- data/ext/pg_query/pg_query_normalize.c +437 -0
- data/ext/pg_query/pg_query_outfuncs.h +10 -0
- data/ext/pg_query/pg_query_outfuncs_json.c +297 -0
- data/ext/pg_query/pg_query_outfuncs_protobuf.c +237 -0
- data/ext/pg_query/pg_query_parse.c +148 -0
- data/ext/pg_query/pg_query_parse_plpgsql.c +460 -0
- data/ext/pg_query/pg_query_readfuncs.h +11 -0
- data/ext/pg_query/pg_query_readfuncs_protobuf.c +142 -0
- data/ext/pg_query/pg_query_ruby.c +108 -12
- data/ext/pg_query/pg_query_scan.c +173 -0
- data/ext/pg_query/pg_query_split.c +221 -0
- data/ext/pg_query/protobuf-c.c +3660 -0
- data/ext/pg_query/src_backend_catalog_namespace.c +1051 -0
- data/ext/pg_query/src_backend_catalog_pg_proc.c +142 -0
- data/ext/pg_query/src_backend_commands_define.c +117 -0
- data/ext/pg_query/src_backend_libpq_pqcomm.c +651 -0
- data/ext/pg_query/src_backend_nodes_bitmapset.c +513 -0
- data/ext/pg_query/src_backend_nodes_copyfuncs.c +6013 -0
- data/ext/pg_query/src_backend_nodes_equalfuncs.c +4003 -0
- data/ext/pg_query/src_backend_nodes_extensible.c +99 -0
- data/ext/pg_query/src_backend_nodes_list.c +922 -0
- data/ext/pg_query/src_backend_nodes_makefuncs.c +417 -0
- data/ext/pg_query/src_backend_nodes_nodeFuncs.c +1363 -0
- data/ext/pg_query/src_backend_nodes_value.c +84 -0
- data/ext/pg_query/src_backend_parser_gram.c +47456 -0
- data/ext/pg_query/src_backend_parser_parse_expr.c +313 -0
- data/ext/pg_query/src_backend_parser_parser.c +497 -0
- data/ext/pg_query/src_backend_parser_scan.c +7091 -0
- data/ext/pg_query/src_backend_parser_scansup.c +160 -0
- data/ext/pg_query/src_backend_postmaster_postmaster.c +2230 -0
- data/ext/pg_query/src_backend_storage_ipc_ipc.c +192 -0
- data/ext/pg_query/src_backend_storage_lmgr_s_lock.c +370 -0
- data/ext/pg_query/src_backend_tcop_postgres.c +776 -0
- data/ext/pg_query/src_backend_utils_adt_datum.c +326 -0
- data/ext/pg_query/src_backend_utils_adt_expandeddatum.c +98 -0
- data/ext/pg_query/src_backend_utils_adt_format_type.c +136 -0
- data/ext/pg_query/src_backend_utils_adt_ruleutils.c +1683 -0
- data/ext/pg_query/src_backend_utils_error_assert.c +74 -0
- data/ext/pg_query/src_backend_utils_error_elog.c +1748 -0
- data/ext/pg_query/src_backend_utils_fmgr_fmgr.c +570 -0
- data/ext/pg_query/src_backend_utils_hash_dynahash.c +1086 -0
- data/ext/pg_query/src_backend_utils_init_globals.c +168 -0
- data/ext/pg_query/src_backend_utils_mb_mbutils.c +839 -0
- data/ext/pg_query/src_backend_utils_misc_guc.c +1831 -0
- data/ext/pg_query/src_backend_utils_mmgr_aset.c +1560 -0
- data/ext/pg_query/src_backend_utils_mmgr_mcxt.c +1006 -0
- data/ext/pg_query/src_common_encnames.c +158 -0
- data/ext/pg_query/src_common_keywords.c +39 -0
- data/ext/pg_query/src_common_kwlist_d.h +1081 -0
- data/ext/pg_query/src_common_kwlookup.c +91 -0
- data/ext/pg_query/src_common_psprintf.c +158 -0
- data/ext/pg_query/src_common_string.c +86 -0
- data/ext/pg_query/src_common_stringinfo.c +336 -0
- data/ext/pg_query/src_common_wchar.c +1651 -0
- data/ext/pg_query/src_pl_plpgsql_src_pl_comp.c +1133 -0
- data/ext/pg_query/src_pl_plpgsql_src_pl_funcs.c +877 -0
- data/ext/pg_query/src_pl_plpgsql_src_pl_gram.c +6533 -0
- data/ext/pg_query/src_pl_plpgsql_src_pl_handler.c +107 -0
- data/ext/pg_query/src_pl_plpgsql_src_pl_reserved_kwlist_d.h +123 -0
- data/ext/pg_query/src_pl_plpgsql_src_pl_scanner.c +671 -0
- data/ext/pg_query/src_pl_plpgsql_src_pl_unreserved_kwlist_d.h +255 -0
- data/ext/pg_query/src_port_erand48.c +127 -0
- data/ext/pg_query/src_port_pg_bitutils.c +246 -0
- data/ext/pg_query/src_port_pgsleep.c +69 -0
- data/ext/pg_query/src_port_pgstrcasecmp.c +83 -0
- data/ext/pg_query/src_port_qsort.c +240 -0
- data/ext/pg_query/src_port_random.c +31 -0
- data/ext/pg_query/src_port_snprintf.c +1449 -0
- data/ext/pg_query/src_port_strerror.c +324 -0
- data/ext/pg_query/src_port_strnlen.c +39 -0
- data/ext/pg_query/xxhash.c +43 -0
- data/lib/pg_query.rb +7 -4
- data/lib/pg_query/constants.rb +21 -0
- data/lib/pg_query/deparse.rb +15 -1673
- data/lib/pg_query/filter_columns.rb +86 -85
- data/lib/pg_query/fingerprint.rb +122 -87
- data/lib/pg_query/json_field_names.rb +1402 -0
- data/lib/pg_query/node.rb +31 -0
- data/lib/pg_query/param_refs.rb +42 -37
- data/lib/pg_query/parse.rb +220 -203
- data/lib/pg_query/parse_error.rb +1 -1
- data/lib/pg_query/pg_query_pb.rb +3211 -0
- data/lib/pg_query/scan.rb +23 -0
- data/lib/pg_query/treewalker.rb +24 -40
- data/lib/pg_query/truncate.rb +64 -43
- data/lib/pg_query/version.rb +2 -2
- metadata +101 -11
- data/ext/pg_query/pg_query_ruby.h +0 -10
- data/lib/pg_query/deep_dup.rb +0 -16
- data/lib/pg_query/deparse/alter_table.rb +0 -42
- data/lib/pg_query/deparse/interval.rb +0 -105
- data/lib/pg_query/deparse/keywords.rb +0 -159
- data/lib/pg_query/deparse/rename.rb +0 -41
- data/lib/pg_query/legacy_parsetree.rb +0 -109
- data/lib/pg_query/node_types.rb +0 -297
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module PgQuery
|
|
2
|
+
# Patch the auto-generated generic node type with additional convenience functions
|
|
3
|
+
class Node
|
|
4
|
+
def inspect
|
|
5
|
+
node ? format('<PgQuery::Node: %s: %s>', node, public_send(node).inspect) : '<PgQuery::Node>'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Make it easier to initialize nodes from a given node child object
|
|
9
|
+
def self.from(node_field_val)
|
|
10
|
+
# This needs to match libpg_query naming for the Node message field names
|
|
11
|
+
# (see "underscore" method in libpg_query's scripts/generate_protobuf_and_funcs.rb)
|
|
12
|
+
node_field_name = node_field_val.class.name.split('::').last
|
|
13
|
+
node_field_name.gsub!(/^([A-Z\d])([A-Z][a-z])/, '\1__\2')
|
|
14
|
+
node_field_name.gsub!(/([A-Z\d]+[a-z]+)([A-Z][a-z])/, '\1_\2')
|
|
15
|
+
node_field_name.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
|
16
|
+
node_field_name.tr!('-', '_')
|
|
17
|
+
node_field_name.downcase!
|
|
18
|
+
|
|
19
|
+
PgQuery::Node.new(node_field_name => node_field_val)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Make it easier to initialize value nodes
|
|
23
|
+
def self.from_string(str)
|
|
24
|
+
PgQuery::Node.new(string: PgQuery::String.new(str: str))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.from_integer(ival)
|
|
28
|
+
PgQuery::Node.new(integer: PgQuery::Integer.new(ival: ival))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/pg_query/param_refs.rb
CHANGED
|
@@ -1,45 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
location
|
|
1
|
+
module PgQuery
|
|
2
|
+
class ParserResult
|
|
3
|
+
def param_refs # rubocop:disable Metrics/CyclomaticComplexity
|
|
4
|
+
results = []
|
|
5
|
+
|
|
6
|
+
treewalker! @tree do |_, _, node, location|
|
|
7
|
+
case node
|
|
8
|
+
when PgQuery::ParamRef
|
|
9
|
+
# Ignore param refs inside type casts, as these are already handled
|
|
10
|
+
next if location[-3..-1] == %i[type_cast arg param_ref]
|
|
11
|
+
|
|
12
|
+
results << { 'location' => node.location,
|
|
13
|
+
'length' => param_ref_length(node) }
|
|
14
|
+
when PgQuery::TypeCast
|
|
15
|
+
next unless node.arg && node.type_name
|
|
16
|
+
|
|
17
|
+
p = node.arg.param_ref
|
|
18
|
+
t = node.type_name
|
|
19
|
+
next unless p && t
|
|
20
|
+
|
|
21
|
+
location = p.location
|
|
22
|
+
typeloc = t.location
|
|
23
|
+
length = param_ref_length(p)
|
|
24
|
+
|
|
25
|
+
if location == -1
|
|
26
|
+
location = typeloc
|
|
27
|
+
elsif typeloc < location
|
|
28
|
+
length += location - typeloc
|
|
29
|
+
location = typeloc
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
results << { 'location' => location, 'length' => length, 'typename' => t.names.map { |n| n.string.str } }
|
|
26
33
|
end
|
|
27
|
-
|
|
28
|
-
results << { 'location' => location, 'length' => length, 'typename' => typename }
|
|
29
34
|
end
|
|
30
|
-
end
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
results.sort_by! { |r| r['location'] }
|
|
37
|
+
results
|
|
38
|
+
end
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
private
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
def param_ref_length(paramref_node)
|
|
43
|
+
if paramref_node.number == 0 # rubocop:disable Style/NumericPredicate
|
|
44
|
+
1 # Actually a ? replacement character
|
|
45
|
+
else
|
|
46
|
+
('$' + paramref_node.number.to_s).size
|
|
47
|
+
end
|
|
43
48
|
end
|
|
44
49
|
end
|
|
45
50
|
end
|
data/lib/pg_query/parse.rb
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
class PgQuery
|
|
1
|
+
module PgQuery
|
|
4
2
|
def self.parse(query)
|
|
5
|
-
|
|
3
|
+
result, stderr = parse_protobuf(query)
|
|
6
4
|
|
|
7
5
|
begin
|
|
8
|
-
|
|
9
|
-
rescue
|
|
10
|
-
raise ParseError.new('Failed to parse
|
|
6
|
+
result = PgQuery::ParseResult.decode(result)
|
|
7
|
+
rescue Google::Protobuf::ParseError
|
|
8
|
+
raise PgQuery::ParseError.new('Failed to parse tree', __FILE__, __LINE__, -1)
|
|
11
9
|
end
|
|
12
10
|
|
|
13
11
|
warnings = []
|
|
@@ -16,232 +14,251 @@ class PgQuery
|
|
|
16
14
|
warnings << line.strip
|
|
17
15
|
end
|
|
18
16
|
|
|
19
|
-
PgQuery.new(query,
|
|
17
|
+
PgQuery::ParserResult.new(query, result, warnings)
|
|
20
18
|
end
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def initialize(query, tree, warnings = [])
|
|
27
|
-
@query = query
|
|
28
|
-
@tree = tree
|
|
29
|
-
@warnings = warnings
|
|
30
|
-
@tables = nil
|
|
31
|
-
@aliases = nil
|
|
32
|
-
@cte_names = nil
|
|
33
|
-
end
|
|
20
|
+
class ParserResult
|
|
21
|
+
attr_reader :query
|
|
22
|
+
attr_reader :tree
|
|
23
|
+
attr_reader :warnings
|
|
34
24
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
def initialize(query, tree, warnings = [])
|
|
26
|
+
@query = query
|
|
27
|
+
@tree = tree
|
|
28
|
+
@warnings = warnings
|
|
29
|
+
@tables = nil
|
|
30
|
+
@aliases = nil
|
|
31
|
+
@cte_names = nil
|
|
32
|
+
end
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
def dup_tree
|
|
35
|
+
ParseResult.decode(ParseResult.encode(@tree))
|
|
36
|
+
end
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
def tables
|
|
39
|
+
tables_with_details.map { |t| t[:name] }.uniq
|
|
40
|
+
end
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
def select_tables
|
|
43
|
+
tables_with_details.select { |t| t[:type] == :select }.map { |t| t[:name] }.uniq
|
|
44
|
+
end
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
end
|
|
46
|
+
def dml_tables
|
|
47
|
+
tables_with_details.select { |t| t[:type] == :dml }.map { |t| t[:name] }.uniq
|
|
48
|
+
end
|
|
55
49
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
end
|
|
50
|
+
def ddl_tables
|
|
51
|
+
tables_with_details.select { |t| t[:type] == :ddl }.map { |t| t[:name] }.uniq
|
|
52
|
+
end
|
|
60
53
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
54
|
+
def cte_names
|
|
55
|
+
load_tables_and_aliases! if @cte_names.nil?
|
|
56
|
+
@cte_names
|
|
57
|
+
end
|
|
65
58
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
59
|
+
def aliases
|
|
60
|
+
load_tables_and_aliases! if @aliases.nil?
|
|
61
|
+
@aliases
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def tables_with_details
|
|
65
|
+
load_tables_and_aliases! if @tables.nil?
|
|
66
|
+
@tables
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
protected
|
|
70
|
+
|
|
71
|
+
def load_tables_and_aliases! # rubocop:disable Metrics/CyclomaticComplexity
|
|
72
|
+
@tables = [] # types: select, dml, ddl
|
|
73
|
+
@cte_names = []
|
|
74
|
+
@aliases = {}
|
|
75
|
+
|
|
76
|
+
statements = @tree.stmts.dup.to_a.map(&:stmt)
|
|
77
|
+
from_clause_items = [] # types: select, dml, ddl
|
|
78
|
+
subselect_items = []
|
|
79
|
+
|
|
80
|
+
loop do
|
|
81
|
+
statement = statements.shift
|
|
82
|
+
if statement
|
|
83
|
+
case statement.node
|
|
84
|
+
when :list
|
|
85
|
+
statements += statement.list.items
|
|
86
|
+
# The following statement types do not modify tables and are added to from_clause_items
|
|
87
|
+
# (and subsequently @tables)
|
|
88
|
+
when :select_stmt
|
|
89
|
+
subselect_items.concat(statement.select_stmt.target_list)
|
|
90
|
+
subselect_items << statement.select_stmt.where_clause if statement.select_stmt.where_clause
|
|
91
|
+
subselect_items.concat(statement.select_stmt.sort_clause.collect { |h| h.sort_by.node })
|
|
92
|
+
subselect_items.concat(statement.select_stmt.group_clause)
|
|
93
|
+
subselect_items << statement.select_stmt.having_clause if statement.select_stmt.having_clause
|
|
94
|
+
|
|
95
|
+
case statement.select_stmt.op
|
|
96
|
+
when :SETOP_NONE
|
|
97
|
+
(statement.select_stmt.from_clause || []).each do |item|
|
|
98
|
+
if item.node == :range_subselect
|
|
99
|
+
statements << item.range_subselect.subquery
|
|
100
|
+
else
|
|
101
|
+
from_clause_items << { item: item, type: :select }
|
|
102
|
+
end
|
|
93
103
|
end
|
|
104
|
+
when :SETOP_UNION
|
|
105
|
+
statements << PgQuery::Node.new(select_stmt: statement.select_stmt.larg) if statement.select_stmt.larg
|
|
106
|
+
statements << PgQuery::Node.new(select_stmt: statement.select_stmt.rarg) if statement.select_stmt.rarg
|
|
94
107
|
end
|
|
95
|
-
when 1
|
|
96
|
-
statements << statement[SELECT_STMT]['larg'] if statement[SELECT_STMT]['larg']
|
|
97
|
-
statements << statement[SELECT_STMT]['rarg'] if statement[SELECT_STMT]['rarg']
|
|
98
|
-
end
|
|
99
108
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
end
|
|
105
|
-
# The following statements modify the contents of a table
|
|
106
|
-
when INSERT_STMT, UPDATE_STMT, DELETE_STMT
|
|
107
|
-
value = statement.values[0]
|
|
108
|
-
from_clause_items << { item: value['relation'], type: :dml }
|
|
109
|
-
statements << value['selectStmt'] if value.key?('selectStmt')
|
|
110
|
-
statements << value['withClause'] if value.key?('withClause')
|
|
111
|
-
|
|
112
|
-
if (with_clause = value['withClause'])
|
|
113
|
-
cte_statements, cte_names = statements_and_cte_names_for_with_clause(with_clause)
|
|
114
|
-
@cte_names.concat(cte_names)
|
|
115
|
-
statements.concat(cte_statements)
|
|
116
|
-
end
|
|
117
|
-
when COPY_STMT
|
|
118
|
-
from_clause_items << { item: statement.values[0]['relation'], type: :dml } if statement.values[0]['relation']
|
|
119
|
-
statements << statement.values[0]['query']
|
|
120
|
-
# The following statement types are DDL (changing table structure)
|
|
121
|
-
when ALTER_TABLE_STMT, CREATE_STMT
|
|
122
|
-
from_clause_items << { item: statement.values[0]['relation'], type: :ddl }
|
|
123
|
-
when CREATE_TABLE_AS_STMT
|
|
124
|
-
if statement[CREATE_TABLE_AS_STMT]['into'] && statement[CREATE_TABLE_AS_STMT]['into'][INTO_CLAUSE]['rel']
|
|
125
|
-
from_clause_items << { item: statement[CREATE_TABLE_AS_STMT]['into'][INTO_CLAUSE]['rel'], type: :ddl }
|
|
126
|
-
end
|
|
127
|
-
if statement[CREATE_TABLE_AS_STMT]['query']
|
|
128
|
-
statements << statement[CREATE_TABLE_AS_STMT]['query']
|
|
129
|
-
end
|
|
130
|
-
when TRUNCATE_STMT
|
|
131
|
-
from_clause_items += statement.values[0]['relations'].map { |r| { item: r, type: :ddl } }
|
|
132
|
-
when VIEW_STMT
|
|
133
|
-
from_clause_items << { item: statement[VIEW_STMT]['view'], type: :ddl }
|
|
134
|
-
statements << statement[VIEW_STMT]['query']
|
|
135
|
-
when VACUUM_STMT, INDEX_STMT, CREATE_TRIG_STMT, RULE_STMT
|
|
136
|
-
from_clause_items << { item: statement.values[0]['relation'], type: :ddl }
|
|
137
|
-
when REFRESH_MAT_VIEW_STMT
|
|
138
|
-
from_clause_items << { item: statement[REFRESH_MAT_VIEW_STMT]['relation'], type: :ddl }
|
|
139
|
-
when DROP_STMT
|
|
140
|
-
objects = statement[DROP_STMT]['objects'].map do |obj|
|
|
141
|
-
if obj.is_a?(Array)
|
|
142
|
-
obj.map { |obj2| obj2['String'] && obj2['String']['str'] }
|
|
143
|
-
else
|
|
144
|
-
obj['String'] && obj['String']['str']
|
|
109
|
+
if statement.select_stmt.with_clause
|
|
110
|
+
cte_statements, cte_names = statements_and_cte_names_for_with_clause(statement.select_stmt.with_clause)
|
|
111
|
+
@cte_names.concat(cte_names)
|
|
112
|
+
statements.concat(cte_statements)
|
|
145
113
|
end
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
case statement[GRANT_STMT]['objtype']
|
|
156
|
-
when 0 # Column # rubocop:disable Lint/EmptyWhen
|
|
157
|
-
# FIXME
|
|
158
|
-
when 1 # Table
|
|
159
|
-
from_clause_items += objects.map { |o| { item: o, type: :ddl } }
|
|
160
|
-
when 2 # Sequence # rubocop:disable Lint/EmptyWhen
|
|
161
|
-
# FIXME
|
|
162
|
-
end
|
|
163
|
-
when LOCK_STMT
|
|
164
|
-
from_clause_items += statement.values[0]['relations'].map { |r| { item: r, type: :ddl } }
|
|
165
|
-
# The following are other statements that don't fit into query/DML/DDL
|
|
166
|
-
when EXPLAIN_STMT
|
|
167
|
-
statements << statement[EXPLAIN_STMT]['query']
|
|
168
|
-
end
|
|
114
|
+
# The following statements modify the contents of a table
|
|
115
|
+
when :insert_stmt, :update_stmt, :delete_stmt
|
|
116
|
+
value = statement.public_send(statement.node)
|
|
117
|
+
from_clause_items << { item: PgQuery::Node.new(range_var: value.relation), type: :dml }
|
|
118
|
+
statements << value.select_stmt if statement.node == :insert_stmt && value.select_stmt
|
|
119
|
+
|
|
120
|
+
subselect_items.concat(statement.update_stmt.target_list) if statement.node == :update_stmt
|
|
121
|
+
subselect_items << statement.update_stmt.where_clause if statement.node == :update_stmt && statement.update_stmt.where_clause
|
|
122
|
+
subselect_items << statement.delete_stmt.where_clause if statement.node == :delete_stmt && statement.delete_stmt.where_clause
|
|
169
123
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
124
|
+
if value.with_clause
|
|
125
|
+
cte_statements, cte_names = statements_and_cte_names_for_with_clause(value.with_clause)
|
|
126
|
+
@cte_names.concat(cte_names)
|
|
127
|
+
statements.concat(cte_statements)
|
|
128
|
+
end
|
|
129
|
+
when :copy_stmt
|
|
130
|
+
from_clause_items << { item: PgQuery::Node.new(range_var: statement.copy_stmt.relation), type: :dml } if statement.copy_stmt.relation
|
|
131
|
+
statements << statement.copy_stmt.query
|
|
132
|
+
# The following statement types are DDL (changing table structure)
|
|
133
|
+
when :alter_table_stmt
|
|
134
|
+
from_clause_items << { item: PgQuery::Node.new(range_var: statement.alter_table_stmt.relation), type: :ddl }
|
|
135
|
+
when :create_stmt
|
|
136
|
+
from_clause_items << { item: PgQuery::Node.new(range_var: statement.create_stmt.relation), type: :ddl }
|
|
137
|
+
when :create_table_as_stmt
|
|
138
|
+
if statement.create_table_as_stmt.into && statement.create_table_as_stmt.into.rel
|
|
139
|
+
from_clause_items << { item: PgQuery::Node.new(range_var: statement.create_table_as_stmt.into.rel), type: :ddl }
|
|
140
|
+
end
|
|
141
|
+
statements << statement.create_table_as_stmt.query if statement.create_table_as_stmt.query
|
|
142
|
+
when :truncate_stmt
|
|
143
|
+
from_clause_items += statement.truncate_stmt.relations.map { |r| { item: r, type: :ddl } }
|
|
144
|
+
when :view_stmt
|
|
145
|
+
from_clause_items << { item: PgQuery::Node.new(range_var: statement.view_stmt.view), type: :ddl }
|
|
146
|
+
statements << statement.view_stmt.query
|
|
147
|
+
when :index_stmt
|
|
148
|
+
from_clause_items << { item: PgQuery::Node.new(range_var: statement.index_stmt.relation), type: :ddl }
|
|
149
|
+
when :create_trig_stmt
|
|
150
|
+
from_clause_items << { item: PgQuery::Node.new(range_var: statement.create_trig_stmt.relation), type: :ddl }
|
|
151
|
+
when :rule_stmt
|
|
152
|
+
from_clause_items << { item: PgQuery::Node.new(range_var: statement.rule_stmt.relation), type: :ddl }
|
|
153
|
+
when :vacuum_stmt
|
|
154
|
+
from_clause_items += statement.vacuum_stmt.rels.map { |r| { item: PgQuery::Node.new(range_var: r.vacuum_relation.relation), type: :ddl } if r.node == :vacuum_relation }
|
|
155
|
+
when :refresh_mat_view_stmt
|
|
156
|
+
from_clause_items << { item: PgQuery::Node.new(range_var: statement.refresh_mat_view_stmt.relation), type: :ddl }
|
|
157
|
+
when :drop_stmt
|
|
158
|
+
objects = statement.drop_stmt.objects.map do |obj|
|
|
159
|
+
case obj.node
|
|
160
|
+
when :list
|
|
161
|
+
obj.list.items.map { |obj2| obj2.string.str if obj2.node == :string }
|
|
162
|
+
when :string
|
|
163
|
+
obj.string.str
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
case statement.drop_stmt.remove_type
|
|
167
|
+
when :OBJECT_TABLE
|
|
168
|
+
@tables += objects.map { |r| { name: r.join('.'), type: :ddl } }
|
|
169
|
+
when :OBJECT_RULE, :OBJECT_TRIGGER
|
|
170
|
+
@tables += objects.map { |r| { name: r[0..-2].join('.'), type: :ddl } }
|
|
171
|
+
end
|
|
172
|
+
when :grant_stmt
|
|
173
|
+
objects = statement.grant_stmt.objects
|
|
174
|
+
case statement.grant_stmt.objtype
|
|
175
|
+
when :OBJECT_COLUMN # Column # rubocop:disable Lint/EmptyWhen
|
|
176
|
+
# FIXME
|
|
177
|
+
when :OBJECT_TABLE # Table
|
|
178
|
+
from_clause_items += objects.map { |o| { item: o, type: :ddl } }
|
|
179
|
+
when :OBJECT_SEQUENCE # Sequence # rubocop:disable Lint/EmptyWhen
|
|
180
|
+
# FIXME
|
|
181
|
+
end
|
|
182
|
+
when :lock_stmt
|
|
183
|
+
from_clause_items += statement.lock_stmt.relations.map { |r| { item: r, type: :ddl } }
|
|
184
|
+
# The following are other statements that don't fit into query/DML/DDL
|
|
185
|
+
when :explain_stmt
|
|
186
|
+
statements << statement.explain_stmt.query
|
|
187
|
+
end
|
|
177
188
|
end
|
|
178
|
-
end
|
|
179
189
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
next_item = subselect_items.shift
|
|
191
|
+
if next_item
|
|
192
|
+
case next_item.node
|
|
193
|
+
when :a_expr
|
|
194
|
+
%w[lexpr rexpr].each do |side|
|
|
195
|
+
elem = next_item.a_expr.public_send(side)
|
|
196
|
+
next unless elem
|
|
197
|
+
if elem.is_a?(Array) # FIXME: this needs to traverse a list
|
|
198
|
+
subselect_items += elem
|
|
199
|
+
else
|
|
200
|
+
subselect_items << elem
|
|
201
|
+
end
|
|
191
202
|
end
|
|
203
|
+
when :bool_expr
|
|
204
|
+
subselect_items.concat(next_item.bool_expr.args)
|
|
205
|
+
when :res_target
|
|
206
|
+
subselect_items << next_item.res_target.val
|
|
207
|
+
when :sub_link
|
|
208
|
+
statements << next_item.sub_link.subselect
|
|
192
209
|
end
|
|
193
|
-
when BOOL_EXPR
|
|
194
|
-
subselect_items.concat(next_item.values[0]['args'])
|
|
195
|
-
when RES_TARGET
|
|
196
|
-
subselect_items << next_item[RES_TARGET]['val']
|
|
197
|
-
when SUB_LINK
|
|
198
|
-
statements << next_item[SUB_LINK]['subselect']
|
|
199
210
|
end
|
|
211
|
+
|
|
212
|
+
break if subselect_items.empty? && statements.empty?
|
|
200
213
|
end
|
|
201
214
|
|
|
202
|
-
|
|
203
|
-
|
|
215
|
+
loop do
|
|
216
|
+
next_item = from_clause_items.shift
|
|
217
|
+
break unless next_item && next_item[:item]
|
|
204
218
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
219
|
+
case next_item[:item].node
|
|
220
|
+
when :join_expr
|
|
221
|
+
from_clause_items << { item: next_item[:item].join_expr.larg, type: next_item[:type] }
|
|
222
|
+
from_clause_items << { item: next_item[:item].join_expr.rarg, type: next_item[:type] }
|
|
223
|
+
when :row_expr
|
|
224
|
+
from_clause_items += next_item[:item].row_expr.args.map { |a| { item: a, type: next_item[:type] } }
|
|
225
|
+
when :range_var
|
|
226
|
+
rangevar = next_item[:item].range_var
|
|
227
|
+
next if rangevar.schemaname.empty? && @cte_names.include?(rangevar.relname)
|
|
208
228
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
229
|
+
table = [rangevar.schemaname, rangevar.relname].reject { |s| s.nil? || s.empty? }.join('.')
|
|
230
|
+
@tables << {
|
|
231
|
+
name: table,
|
|
232
|
+
type: next_item[:type],
|
|
233
|
+
location: rangevar.location,
|
|
234
|
+
schemaname: (rangevar.schemaname unless rangevar.schemaname.empty?),
|
|
235
|
+
relname: rangevar.relname,
|
|
236
|
+
inh: rangevar.inh
|
|
237
|
+
}
|
|
238
|
+
@aliases[rangevar.alias.aliasname] = table if rangevar.alias
|
|
239
|
+
when :range_subselect
|
|
240
|
+
from_clause_items << { item: next_item[:item].range_subselect.subquery, type: next_item[:type] }
|
|
241
|
+
when :select_stmt
|
|
242
|
+
from_clause = next_item[:item].select_stmt.from_clause
|
|
243
|
+
from_clause_items += from_clause.map { |r| { item: r, type: next_item[:type] } } if from_clause
|
|
213
244
|
end
|
|
214
|
-
when ROW_EXPR
|
|
215
|
-
from_clause_items += next_item[:item][ROW_EXPR]['args'].map { |a| { item: a, type: next_item[:type] } }
|
|
216
|
-
when RANGE_VAR
|
|
217
|
-
rangevar = next_item[:item][RANGE_VAR]
|
|
218
|
-
next if !rangevar['schemaname'] && @cte_names.include?(rangevar['relname'])
|
|
219
|
-
|
|
220
|
-
table = [rangevar['schemaname'], rangevar['relname']].compact.join('.')
|
|
221
|
-
@tables << { table: table, type: next_item[:type] }
|
|
222
|
-
@aliases[rangevar['alias'][ALIAS]['aliasname']] = table if rangevar['alias']
|
|
223
|
-
when RANGE_SUBSELECT
|
|
224
|
-
from_clause_items << { item: next_item[:item][RANGE_SUBSELECT]['subquery'], type: next_item[:type] }
|
|
225
|
-
when SELECT_STMT
|
|
226
|
-
from_clause = next_item[:item][SELECT_STMT][FROM_CLAUSE_FIELD]
|
|
227
|
-
from_clause_items += from_clause.map { |r| { item: r, type: next_item[:type] } } if from_clause
|
|
228
245
|
end
|
|
246
|
+
|
|
247
|
+
@tables.uniq!
|
|
248
|
+
@cte_names.uniq!
|
|
229
249
|
end
|
|
230
250
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
251
|
+
def statements_and_cte_names_for_with_clause(with_clause) # FIXME
|
|
252
|
+
statements = []
|
|
253
|
+
cte_names = []
|
|
234
254
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
255
|
+
with_clause.ctes.each do |item|
|
|
256
|
+
next unless item.node == :common_table_expr
|
|
257
|
+
cte_names << item.common_table_expr.ctename
|
|
258
|
+
statements << item.common_table_expr.ctequery
|
|
259
|
+
end
|
|
238
260
|
|
|
239
|
-
|
|
240
|
-
next unless item[COMMON_TABLE_EXPR]
|
|
241
|
-
cte_names << item[COMMON_TABLE_EXPR]['ctename']
|
|
242
|
-
statements << item[COMMON_TABLE_EXPR]['ctequery']
|
|
261
|
+
[statements, cte_names]
|
|
243
262
|
end
|
|
244
|
-
|
|
245
|
-
[statements, cte_names]
|
|
246
263
|
end
|
|
247
264
|
end
|