pg_query 1.0.2 → 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 +5 -5
- data/CHANGELOG.md +162 -40
- data/README.md +80 -69
- data/Rakefile +85 -4
- data/ext/pg_query/extconf.rb +4 -32
- 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 +16 -991
- 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 -200
- 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 +102 -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/legacy_parsetree.rb +0 -109
- data/lib/pg_query/node_types.rb +0 -282
|
@@ -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,229 +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
|
-
when TRUNCATE_STMT
|
|
128
|
-
from_clause_items += statement.values[0]['relations'].map { |r| { item: r, type: :ddl } }
|
|
129
|
-
when VIEW_STMT
|
|
130
|
-
from_clause_items << { item: statement[VIEW_STMT]['view'], type: :ddl }
|
|
131
|
-
statements << statement[VIEW_STMT]['query']
|
|
132
|
-
when VACUUM_STMT, INDEX_STMT, CREATE_TRIG_STMT, RULE_STMT
|
|
133
|
-
from_clause_items << { item: statement.values[0]['relation'], type: :ddl }
|
|
134
|
-
when REFRESH_MAT_VIEW_STMT
|
|
135
|
-
from_clause_items << { item: statement[REFRESH_MAT_VIEW_STMT]['relation'], type: :ddl }
|
|
136
|
-
when DROP_STMT
|
|
137
|
-
objects = statement[DROP_STMT]['objects'].map do |obj|
|
|
138
|
-
if obj.is_a?(Array)
|
|
139
|
-
obj.map { |obj2| obj2['String'] && obj2['String']['str'] }
|
|
140
|
-
else
|
|
141
|
-
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)
|
|
142
113
|
end
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
case statement[GRANT_STMT]['objtype']
|
|
153
|
-
when 0 # Column # rubocop:disable Lint/EmptyWhen
|
|
154
|
-
# FIXME
|
|
155
|
-
when 1 # Table
|
|
156
|
-
from_clause_items += objects.map { |o| { item: o, type: :ddl } }
|
|
157
|
-
when 2 # Sequence # rubocop:disable Lint/EmptyWhen
|
|
158
|
-
# FIXME
|
|
159
|
-
end
|
|
160
|
-
when LOCK_STMT
|
|
161
|
-
from_clause_items += statement.values[0]['relations'].map { |r| { item: r, type: :ddl } }
|
|
162
|
-
# The following are other statements that don't fit into query/DML/DDL
|
|
163
|
-
when EXPLAIN_STMT
|
|
164
|
-
statements << statement[EXPLAIN_STMT]['query']
|
|
165
|
-
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
|
|
166
123
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
|
174
188
|
end
|
|
175
|
-
end
|
|
176
189
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
|
188
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
|
|
189
209
|
end
|
|
190
|
-
when BOOL_EXPR
|
|
191
|
-
subselect_items.concat(next_item.values[0]['args'])
|
|
192
|
-
when RES_TARGET
|
|
193
|
-
subselect_items << next_item[RES_TARGET]['val']
|
|
194
|
-
when SUB_LINK
|
|
195
|
-
statements << next_item[SUB_LINK]['subselect']
|
|
196
210
|
end
|
|
211
|
+
|
|
212
|
+
break if subselect_items.empty? && statements.empty?
|
|
197
213
|
end
|
|
198
214
|
|
|
199
|
-
|
|
200
|
-
|
|
215
|
+
loop do
|
|
216
|
+
next_item = from_clause_items.shift
|
|
217
|
+
break unless next_item && next_item[:item]
|
|
201
218
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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)
|
|
205
228
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
|
210
244
|
end
|
|
211
|
-
when ROW_EXPR
|
|
212
|
-
from_clause_items += next_item[:item][ROW_EXPR]['args'].map { |a| { item: a, type: next_item[:type] } }
|
|
213
|
-
when RANGE_VAR
|
|
214
|
-
rangevar = next_item[:item][RANGE_VAR]
|
|
215
|
-
next if !rangevar['schemaname'] && @cte_names.include?(rangevar['relname'])
|
|
216
|
-
|
|
217
|
-
table = [rangevar['schemaname'], rangevar['relname']].compact.join('.')
|
|
218
|
-
@tables << { table: table, type: next_item[:type] }
|
|
219
|
-
@aliases[rangevar['alias'][ALIAS]['aliasname']] = table if rangevar['alias']
|
|
220
|
-
when RANGE_SUBSELECT
|
|
221
|
-
from_clause_items << { item: next_item[:item][RANGE_SUBSELECT]['subquery'], type: next_item[:type] }
|
|
222
|
-
when SELECT_STMT
|
|
223
|
-
from_clause = next_item[:item][SELECT_STMT][FROM_CLAUSE_FIELD]
|
|
224
|
-
from_clause_items += from_clause.map { |r| { item: r, type: next_item[:type] } } if from_clause
|
|
225
245
|
end
|
|
246
|
+
|
|
247
|
+
@tables.uniq!
|
|
248
|
+
@cte_names.uniq!
|
|
226
249
|
end
|
|
227
250
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
251
|
+
def statements_and_cte_names_for_with_clause(with_clause) # FIXME
|
|
252
|
+
statements = []
|
|
253
|
+
cte_names = []
|
|
231
254
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
|
235
260
|
|
|
236
|
-
|
|
237
|
-
next unless item[COMMON_TABLE_EXPR]
|
|
238
|
-
cte_names << item[COMMON_TABLE_EXPR]['ctename']
|
|
239
|
-
statements << item[COMMON_TABLE_EXPR]['ctequery']
|
|
261
|
+
[statements, cte_names]
|
|
240
262
|
end
|
|
241
|
-
|
|
242
|
-
[statements, cte_names]
|
|
243
263
|
end
|
|
244
264
|
end
|