Class: PgQuery::ParserResult
- Inherits:
-
Object
- Object
- PgQuery::ParserResult
show all
- Defined in:
- lib/pg_query/parse.rb,
lib/pg_query/deparse.rb,
lib/pg_query/truncate.rb,
lib/pg_query/param_refs.rb,
lib/pg_query/treewalker.rb,
lib/pg_query/fingerprint.rb,
lib/pg_query/filter_columns.rb
Defined Under Namespace
Classes: FingerprintSubHash, PossibleTruncation
Instance Attribute Summary collapse
Instance Method Summary
collapse
Constructor Details
#initialize(query, tree, warnings = []) ⇒ ParserResult
Returns a new instance of ParserResult.
25
26
27
28
29
30
31
32
|
# File 'lib/pg_query/parse.rb', line 25
def initialize(query, tree, warnings = [])
@query = query
@tree = tree
@warnings = warnings
@tables = nil
@aliases = nil
@cte_names = nil
end
|
Instance Attribute Details
#query ⇒ Object
Returns the value of attribute query.
21
22
23
|
# File 'lib/pg_query/parse.rb', line 21
def query
@query
end
|
#tree ⇒ Object
Returns the value of attribute tree.
22
23
24
|
# File 'lib/pg_query/parse.rb', line 22
def tree
@tree
end
|
#warnings ⇒ Object
Returns the value of attribute warnings.
23
24
25
|
# File 'lib/pg_query/parse.rb', line 23
def warnings
@warnings
end
|
Instance Method Details
#aliases ⇒ Object
59
60
61
62
|
# File 'lib/pg_query/parse.rb', line 59
def aliases
load_tables_and_aliases! if @aliases.nil?
@aliases
end
|
#cte_names ⇒ Object
54
55
56
57
|
# File 'lib/pg_query/parse.rb', line 54
def cte_names
load_tables_and_aliases! if @cte_names.nil?
@cte_names
end
|
#ddl_tables ⇒ Object
50
51
52
|
# File 'lib/pg_query/parse.rb', line 50
def ddl_tables
tables_with_details.select { |t| t[:type] == :ddl }.map { |t| t[:name] }.uniq
end
|
#deparse ⇒ Object
3
4
5
|
# File 'lib/pg_query/deparse.rb', line 3
def deparse
PgQuery.deparse(@tree)
end
|
#dml_tables ⇒ Object
46
47
48
|
# File 'lib/pg_query/parse.rb', line 46
def dml_tables
tables_with_details.select { |t| t[:type] == :dml }.map { |t| t[:name] }.uniq
end
|
#dup_tree ⇒ Object
34
35
36
|
# File 'lib/pg_query/parse.rb', line 34
def dup_tree
ParseResult.decode(ParseResult.encode(@tree))
end
|
#filter_columns ⇒ Object
Returns a list of columns that the query filters by - this excludes the target list, but includes things like JOIN condition and WHERE clause.
Note: This also traverses into sub-selects.
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
# File 'lib/pg_query/filter_columns.rb', line 7
def filter_columns load_tables_and_aliases! if @aliases.nil?
statements = @tree.stmts.dup.to_a.map(&:stmt)
condition_items = []
filter_columns = []
loop do
statement = statements.shift
if statement
case statement.node
when :list
statements += statement.list.items
when :raw_stmt
statements << statement.raw_stmt.stmt
when :select_stmt
case statement.select_stmt.op
when :SETOP_NONE
if statement.select_stmt.from_clause
statement.select_stmt.from_clause.each do |item|
next unless item['RangeSubselect']
statements << item['RangeSubselect']['subquery']
end
condition_items += conditions_from_join_clauses(statement.select_stmt.from_clause)
end
condition_items << statement.select_stmt.where_clause if statement.select_stmt.where_clause
if statement.select_stmt.with_clause
statement.select_stmt.with_clause.ctes.each do |item|
statements << item.common_table_expr.ctequery if item.node == :common_table_expr
end
end
when :SETOP_UNION
statements << statement.select_stmt.larg if statement.select_stmt.larg
statements << statement.select_stmt.rarg if statement.select_stmt.rarg
end
when :update_stmt
condition_items << statement.update_stmt.where_clause if statement.update_stmt.where_clause
when :delete_stmt
condition_items << statement.delete_stmt.where_clause if statement.delete_stmt.where_clause
end
end
next_item = condition_items.shift
if next_item
case next_item.node
when :a_expr
condition_items << next_item.a_expr.lexpr if next_item.a_expr.lexpr
condition_items << next_item.a_expr.rexpr if next_item.a_expr.rexpr
when :bool_expr
condition_items += next_item.bool_expr.args
when :coalesce_expr
condition_items += next_item.coalesce_expr.args
when :row_expr
condition_items += next_item.row_expr.args
when :column_ref
column, table = next_item.column_ref.fields.map { |f| f.string.str }.reverse
filter_columns << [@aliases[table] || table, column]
when :null_test
condition_items << next_item.null_test.arg
when :boolean_test
condition_items << next_item.boolean_test.arg
when :func_call
condition_items += next_item.func_call.args if next_item.func_call.args
when :sub_link
condition_items << next_item.sub_link.testexpr
statements << next_item.sub_link.subselect
end
end
break if statements.empty? && condition_items.empty?
end
filter_columns.uniq
end
|
#fingerprint ⇒ Object
5
6
7
8
9
10
|
# File 'lib/pg_query/fingerprint.rb', line 5
def fingerprint
hash = FingerprintSubHash.new
fingerprint_tree(hash)
fp = PgQuery.hash_xxh3_64(hash.parts.join, FINGERPRINT_VERSION)
format('%016x', fp)
end
|
#param_refs ⇒ Object
rubocop:disable Metrics/CyclomaticComplexity
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
# File 'lib/pg_query/param_refs.rb', line 3
def param_refs results = []
treewalker! @tree do |_, _, node, location|
case node
when PgQuery::ParamRef
next if location[-3..-1] == %i[type_cast arg param_ref]
results << { 'location' => node.location,
'length' => param_ref_length(node) }
when PgQuery::TypeCast
next unless node.arg && node.type_name
p = node.arg.param_ref
t = node.type_name
next unless p && t
location = p.location
typeloc = t.location
length = param_ref_length(p)
if location == -1
location = typeloc
elsif typeloc < location
length += location - typeloc
location = typeloc
end
results << { 'location' => location, 'length' => length, 'typename' => t.names.map { |n| n.string.str } }
end
end
results.sort_by! { |r| r['location'] }
results
end
|
#select_tables ⇒ Object
42
43
44
|
# File 'lib/pg_query/parse.rb', line 42
def select_tables
tables_with_details.select { |t| t[:type] == :select }.map { |t| t[:name] }.uniq
end
|
#tables ⇒ Object
38
39
40
|
# File 'lib/pg_query/parse.rb', line 38
def tables
tables_with_details.map { |t| t[:name] }.uniq
end
|
#tables_with_details ⇒ Object
64
65
66
67
|
# File 'lib/pg_query/parse.rb', line 64
def tables_with_details
load_tables_and_aliases! if @tables.nil?
@tables
end
|
#truncate(max_length) ⇒ Object
Truncates the query string to be below the specified length, first trying to omit less important parts of the query, and only then cutting off the end.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
# File 'lib/pg_query/truncate.rb', line 8
def truncate(max_length) output = deparse
return output if output.size <= max_length
truncations = find_possible_truncations
truncations.sort_by! { |t| [-t.location.size, -t.length] }
tree = dup_tree
truncations.each do |truncation|
next if truncation.length < 3
find_tree_location(tree, truncation.location) do |node, _k|
dummy_column_ref = PgQuery::Node.new(column_ref: PgQuery::ColumnRef.new(fields: [PgQuery::Node.new(string: PgQuery::String.new(str: '…'))]))
case truncation.node_type
when :target_list
res_target_name = '…' if node.is_a?(PgQuery::UpdateStmt) || node.is_a?(PgQuery::OnConflictClause)
node.target_list.replace(
[
PgQuery::Node.new(res_target: PgQuery::ResTarget.new(name: res_target_name, val: dummy_column_ref))
]
)
when :where_clause
node.where_clause = dummy_column_ref
when :ctequery
node.ctequery = PgQuery::Node.new(select_stmt: PgQuery::SelectStmt.new(where_clause: dummy_column_ref, op: :SETOP_NONE))
when :cols
node.cols.replace([PgQuery::Node.from(PgQuery::ResTarget.new(name: '…'))]) if node.is_a?(PgQuery::InsertStmt)
else
raise ArgumentError, format('Unexpected truncation node type: %s', truncation.node_type)
end
end
output = PgQuery.deparse(tree).gsub('SELECT WHERE "…"', '...').gsub('"…"', '...')
return output if output.size <= max_length
end
output[0..max_length - 4] + '...'
end
|