pg_query 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b2251b16a127ee476a1aed7b7f1a8742345ffd51
4
- data.tar.gz: 3ca4be0ecefd3404dc2dad263e9d027ada55ee0f
3
+ metadata.gz: 31d33e3f025beef9defb3beb7c471774873ca41c
4
+ data.tar.gz: 6d2bf5bf6fda9dcd91bf9ecadc4d9a4c3d4af230
5
5
  SHA512:
6
- metadata.gz: c021937633296ed32789f625008c9bece2711f10ed41e4804e998fa8114e403414b2a191d60c7c937089daac4661d79ce670124f3e4b0653dde62d5b1420ae38
7
- data.tar.gz: 5b9fcbc430526ad159ca5aa46a16094462eb5b8de956d377be9d740146e7166348364774efca904b32a975b765df70f937c85730653874ca2bbd768045ecdfd5
6
+ metadata.gz: 8d1053da24fa8ec75b06797d44578ea9d438ce71a00d9626d1b0f2c2ad7e3b4fe993e6c5d0cb6e1264c87c607ec31640abba0013690b113d097350a4fdee606c
7
+ data.tar.gz: 0302e42540073577d6e8e02f21c65c2ca71c15621555be41c7dee50432b57374573b695696260ff2c91ab422c54511e8cbf5d8fed3dc072faaf9f0bd46cd0494
@@ -28,14 +28,14 @@ PG_OBJS = {
28
28
  #
29
29
  # Note: We intentionally use a patched version that fixes bugs in outfuncs.c
30
30
  if !Dir.exists?(pgdir)
31
- unless File.exists?("#{workdir}/postgres.zip")
32
- File.open("#{workdir}/postgres.zip", "wb") do |target_file|
33
- open("https://codeload.github.com/pganalyze/postgres/zip/pg_query", "rb") do |read_file|
31
+ unless File.exists?("#{workdir}/postgres.tar.gz")
32
+ File.open("#{workdir}/postgres.tar.gz", "wb") do |target_file|
33
+ open("https://codeload.github.com/pganalyze/postgres/tar.gz/pg_query", "rb") do |read_file|
34
34
  target_file.write(read_file.read)
35
35
  end
36
36
  end
37
37
  end
38
- system("unzip -q #{workdir}/postgres.zip -d #{workdir}") || raise("ERROR")
38
+ system("tar -xf #{workdir}/postgres.tar.gz") || raise("ERROR")
39
39
  system("mv #{workdir}/postgres-pg_query #{pgdir}") || raise("ERROR")
40
40
  system("cd #{pgdir}; CFLAGS=-fPIC ./configure -q") || raise("ERROR")
41
41
  system("cd #{pgdir}; make -C src/backend lib-recursive") # Ensures headers are generated
@@ -31,3 +31,11 @@ bool proc_exit_inprogress = false;
31
31
  /* src/backend/tcop/postgres.c */
32
32
  #include "miscadmin.h"
33
33
  void check_stack_depth(void) { /* Do nothing */ }
34
+
35
+ /* src/backends/commands/define.c */
36
+ #include "commands/defrem.h"
37
+ #include "nodes/makefuncs.h"
38
+ DefElem * defWithOids(bool value)
39
+ {
40
+ return makeDefElem("oids", (Node *) makeInteger(value));
41
+ }
@@ -2,4 +2,7 @@ require 'pg_query/version'
2
2
  require 'pg_query/parse_error'
3
3
 
4
4
  require 'pg_query/pg_query'
5
- require 'pg_query/parse'
5
+ require 'pg_query/parse'
6
+ require 'pg_query/filter_columns'
7
+ require 'pg_query/fingerprint'
8
+ require 'pg_query/param_refs'
@@ -0,0 +1,87 @@
1
+ class PgQuery
2
+ # Returns a list of columns that the query filters by - this excludes the
3
+ # target list, but includes things like JOIN condition and WHERE clause.
4
+ #
5
+ # Note: This also traverses into sub-selects.
6
+ def filter_columns
7
+ load_tables_and_aliases! if @aliases.nil?
8
+
9
+ # Get condition items from the parsetree
10
+ statements = @parsetree.dup
11
+ condition_items = []
12
+ filter_columns = []
13
+ loop do
14
+ if statement = statements.shift
15
+ if statement["SELECT"]
16
+ if statement["SELECT"]["op"] == 0
17
+ if statement["SELECT"]["fromClause"]
18
+ # FROM subselects
19
+ statement["SELECT"]["fromClause"].each do |item|
20
+ statements << item["RANGESUBSELECT"]["subquery"] if item["RANGESUBSELECT"]
21
+ end
22
+
23
+ # JOIN ON conditions
24
+ condition_items += conditions_from_join_clauses(statement["SELECT"]["fromClause"])
25
+ end
26
+
27
+ # WHERE clause
28
+ condition_items << statement["SELECT"]["whereClause"] if statement["SELECT"]["whereClause"]
29
+ elsif statement["SELECT"]["op"] == 1
30
+ statements << statement["SELECT"]["larg"] if statement["SELECT"]["larg"]
31
+ statements << statement["SELECT"]["rarg"] if statement["SELECT"]["rarg"]
32
+ end
33
+ elsif statement["UPDATE"]
34
+ condition_items << statement["UPDATE"]["whereClause"] if statement["UPDATE"]["whereClause"]
35
+ elsif statement["DELETE FROM"]
36
+ condition_items << statement["DELETE FROM"]["whereClause"] if statement["DELETE FROM"]["whereClause"]
37
+ end
38
+ end
39
+
40
+ # Process both JOIN and WHERE conditions here
41
+ if next_item = condition_items.shift
42
+ if next_item.keys[0].start_with?("AEXPR") || next_item["ANY"]
43
+ ["lexpr", "rexpr"].each do |side|
44
+ next unless expr = next_item.values[0][side]
45
+ next unless expr.is_a?(Hash)
46
+ condition_items << expr
47
+ end
48
+ elsif next_item["ROW"]
49
+ condition_items += next_item["ROW"]["args"]
50
+ elsif next_item["COLUMNREF"]
51
+ column, table = next_item["COLUMNREF"]["fields"].reverse
52
+ filter_columns << [@aliases[table] || table, column]
53
+ elsif next_item["NULLTEST"]
54
+ condition_items << next_item["NULLTEST"]["arg"]
55
+ elsif next_item["FUNCCALL"]
56
+ # FIXME: This should actually be extracted as a funccall and be compared with those indices
57
+ condition_items += next_item["FUNCCALL"]["args"] if next_item["FUNCCALL"]["args"]
58
+ elsif next_item["SUBLINK"]
59
+ condition_items << next_item["SUBLINK"]["testexpr"]
60
+ statements << next_item["SUBLINK"]["subselect"]
61
+ end
62
+ end
63
+
64
+ break if statements.empty? && condition_items.empty?
65
+ end
66
+
67
+ filter_columns.uniq
68
+ end
69
+
70
+ protected
71
+ def conditions_from_join_clauses(from_clause)
72
+ condition_items = []
73
+ from_clause.each do |item|
74
+ next unless item["JOINEXPR"]
75
+
76
+ joinexpr_items = [item["JOINEXPR"]]
77
+ loop do
78
+ break unless next_item = joinexpr_items.shift
79
+ condition_items << next_item["quals"] if next_item["quals"]
80
+ ["larg", "rarg"].each do |side|
81
+ joinexpr_items << next_item[side]["JOINEXPR"] if next_item[side]["JOINEXPR"]
82
+ end
83
+ end
84
+ end
85
+ condition_items
86
+ end
87
+ end
@@ -0,0 +1,62 @@
1
+ require 'digest'
2
+
3
+ class PgQuery
4
+ def fingerprint
5
+ normalized_parsetree = deep_dup(parsetree)
6
+ exprs = normalized_parsetree.dup
7
+ loop do
8
+ expr = exprs.shift
9
+
10
+ if expr.is_a?(Hash)
11
+ expr.each do |k,v|
12
+ if v.is_a?(Hash) && ["A_CONST", "ALIAS", "PARAMREF"].include?(v.keys[0])
13
+ # Remove constants, aliases and param references from tree
14
+ expr[k] = nil
15
+ elsif k == "location"
16
+ # Remove location info in order to ignore whitespace and target list ordering
17
+ expr.delete(k)
18
+ elsif !v.nil?
19
+ # Remove SELECT target list names & ignore order
20
+ if k == "targetList" && v.is_a?(Array)
21
+ v.each {|v| v["RESTARGET"]["name"] = nil if v["RESTARGET"] } # Remove names
22
+ v.sort_by! {|v| v.to_s }
23
+ expr[k] = v
24
+ end
25
+
26
+ # Ignore INSERT cols order
27
+ if k == "cols" && v.is_a?(Array)
28
+ v.sort_by! {|v| v.to_s }
29
+ expr[k] = v
30
+ end
31
+
32
+ # Process sub-expressions
33
+ exprs << v
34
+ end
35
+ end
36
+ elsif expr.is_a?(Array)
37
+ exprs += expr
38
+ end
39
+
40
+ break if exprs.empty?
41
+ end
42
+
43
+ Digest::SHA1.hexdigest(normalized_parsetree.to_s)
44
+ end
45
+
46
+ private
47
+
48
+ def deep_dup(obj)
49
+ case obj
50
+ when Hash
51
+ obj.each_with_object(obj.dup) do |(key, value), hash|
52
+ hash[deep_dup(key)] = deep_dup(value)
53
+ end
54
+ when Array
55
+ obj.map { |it| deep_dup(it) }
56
+ when NilClass, FalseClass, TrueClass, Symbol, Numeric
57
+ obj # Can't be duplicated
58
+ else
59
+ obj.dup
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,40 @@
1
+ class PgQuery
2
+ def param_refs
3
+ results = []
4
+ exprs = parsetree.dup
5
+ loop do
6
+ expr = exprs.shift
7
+
8
+ if expr.is_a?(Hash)
9
+ expr.each do |k,v|
10
+ if v.is_a?(Hash)
11
+ if v["PARAMREF"]
12
+ length = 1 # FIXME: Not true when we have actual paramrefs
13
+ results << {"location" => v["PARAMREF"]["location"], "length" => length}
14
+ next
15
+ elsif (p = v["TYPECAST"]["arg"]["PARAMREF"] rescue false) && (t = v["TYPECAST"]["typeName"]["TYPENAME"] rescue false)
16
+ location = p["location"]
17
+ typeloc = t["location"]
18
+ typename = t["names"].join(".")
19
+ length = 1 # FIXME: Not true when we have actual paramrefs
20
+ if typeloc < location
21
+ length += location - typeloc
22
+ location = typeloc
23
+ end
24
+ results << {"location" => location, "length" => length, "typename" => typename}
25
+ next
26
+ end
27
+ end
28
+
29
+ exprs << v if !v.nil?
30
+ end
31
+ elsif expr.is_a?(Array)
32
+ exprs += expr
33
+ end
34
+
35
+ break if exprs.empty?
36
+ end
37
+ results.sort_by! {|r| r["location"] }
38
+ results
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  class PgQuery
2
- VERSION = '0.4.1'
2
+ VERSION = '0.5.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_query
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lukas Fittl
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-18 00:00:00.000000000 Z
11
+ date: 2015-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -66,6 +66,9 @@ files:
66
66
  - ext/pg_query/pg_query.c
67
67
  - ext/pg_query/pg_query.sym
68
68
  - lib/pg_query.rb
69
+ - lib/pg_query/filter_columns.rb
70
+ - lib/pg_query/fingerprint.rb
71
+ - lib/pg_query/param_refs.rb
69
72
  - lib/pg_query/parse.rb
70
73
  - lib/pg_query/parse_error.rb
71
74
  - lib/pg_query/version.rb
@@ -89,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
92
  version: '0'
90
93
  requirements: []
91
94
  rubyforge_project:
92
- rubygems_version: 2.2.2
95
+ rubygems_version: 2.4.5
93
96
  signing_key:
94
97
  specification_version: 4
95
98
  summary: PostgreSQL query parsing and normalization library