piggly 2.0.0 → 2.1.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: 6bc3e3b06dfb2ce02fc63e702216f7144165c2a0
4
- data.tar.gz: 15a2d1da931923920ffce34f726096c3f31e8492
3
+ metadata.gz: f16c8c0dbea51654e23f87e09524076da758fb8e
4
+ data.tar.gz: 99c21d733eb0fc50e1334e760f7601141eac4c3a
5
5
  SHA512:
6
- metadata.gz: 8409f7c8adde74c01dfaeb3dcf0e1a6df10b5e8d7552701d38586f938d47e9727457c34a3b2a539f1ad54ceb4e5450909bddfc3f4b43485a2094fd9dc045f97c
7
- data.tar.gz: b4d409e5ab4f45112f27444720fa56ce6e034e8c9244c466a2f249fcf0cb3032269287ca8eb48927c01245e7e1e62bf42a0b15a32f3df408ed3278a962eeb1dc
6
+ metadata.gz: 75479a4b5b69f804d2d7dd54272f65784301821a1088a3e969398610e8f4a56f114b48dca7606d34605305d4f440af669a9a7cfecf796a2c4aa59ad3fafff981
7
+ data.tar.gz: fbff8e9856834ad14593596fd5ea76ddfaf49ee9c8625afbb4257450b0e04e1ca4699c9b24e76de20c78283d91204cc25d5666244e6900d498b228eb1f389956
@@ -10,7 +10,7 @@ module Piggly
10
10
  cmd, argv = command(argv)
11
11
 
12
12
  if cmd.nil?
13
- abort "usage: #{$0} {test|report|trace|untrace} --help"
13
+ abort "usage: #{$0} {report|trace|untrace} --help"
14
14
  else
15
15
  cmd.main(argv)
16
16
  end
@@ -25,7 +25,6 @@ module Piggly
25
25
 
26
26
  case head.downcase
27
27
  when "report"; [Report, tail]
28
- when "test"; [Test, tail]
29
28
  when "trace"; [Trace, tail]
30
29
  when "untrace"; [Untrace, tail]
31
30
  end
@@ -68,18 +67,26 @@ module Piggly
68
67
  else
69
68
  head, _ = config.filters
70
69
 
71
- start =
72
- case head.first
73
- when :+; []
74
- when :-; index.procedures
75
- end
70
+ start = index.procedures
76
71
 
77
72
  config.filters.inject(start) do |s, pair|
78
73
  case pair.first
79
- when :+
80
- s | index.procedures.select(&pair.last)
81
- when :-
82
- s.reject(&pair.last)
74
+ when :+
75
+ puts "Selecting procedures"
76
+ o = s.select(&pair.last)
77
+ puts "Selected #{ o.count} procedures"
78
+ puts "Selected:"
79
+ o.each do |x| puts x.name end
80
+ puts "---"
81
+ o
82
+ when :-
83
+ puts "Rejecting procedures"
84
+ o = s.reject(&pair.last)
85
+ puts "Rejected #{s.count - o.count} procedures"
86
+ puts "Rejected:"
87
+ ((o-s)|(s-o)).each do |x| puts x.name end
88
+ puts "---"
89
+ o
83
90
  end
84
91
  end
85
92
  end
@@ -110,11 +117,11 @@ module Piggly
110
117
  end
111
118
 
112
119
  def o_version(config)
113
- lambda { puts "piggly #{VERSION} #{VERSION::RELEASE_DATE}"; exit! }
120
+ lambda {|x| puts "piggly #{VERSION} #{VERSION::RELEASE_DATE}"; exit! }
114
121
  end
115
122
 
116
123
  def o_dry_run(config)
117
- lambda { config.dry_run = true }
124
+ lambda {|x| config.dry_run = true }
118
125
  end
119
126
 
120
127
  def o_select(config)
@@ -2,7 +2,6 @@ module Piggly
2
2
  module Command
3
3
  autoload :Base, "piggly/command/base"
4
4
  autoload :Report, "piggly/command/report"
5
- autoload :Test, "piggly/command/test"
6
5
  autoload :Trace, "piggly/command/trace"
7
6
  autoload :Untrace, "piggly/command/untrace"
8
7
  end
@@ -22,6 +22,7 @@ module Piggly
22
22
  cache = CacheDir.new(cache_path(procedure.source_path(@config)))
23
23
 
24
24
  if stale?(procedure)
25
+ begin
25
26
  $stdout.puts "Compiling #{procedure.name}"
26
27
  tree = Parser.parse(IO.read(procedure.source_path(@config)))
27
28
  tree = tree.force! if tree.respond_to?(:thunk?)
@@ -30,6 +31,17 @@ module Piggly
30
31
  code = traverse(tree, procedure.oid, tags)
31
32
 
32
33
  cache.replace(:tree => tree, :code => code, :tags => tags)
34
+ rescue RuntimeError => e
35
+ $stdout.puts <<-EXMSG
36
+ ****
37
+ Error compiling procedure #{procedure.name}
38
+ Source: #{procedure.source_path(@config)}
39
+ Exception Message:
40
+ #{e.message}
41
+ ****
42
+ EXMSG
43
+ end
44
+
33
45
  end
34
46
 
35
47
  cache
@@ -49,10 +61,10 @@ module Piggly
49
61
  # IF(test) becomes IF(piggly_cond(TAG, test)) instead of IFpiggly_cond(TAG, (test))
50
62
  pre, cond = node.cond.expr.text_value.match(/\A(\(?[\t\n\r ]*)(.+)\z/m).captures
51
63
  node.cond.source_text = ""
52
-
64
+
53
65
  tags << node.cond.tag(oid)
54
66
 
55
- node.condStub.source_text = "#{pre}piggly_cond($PIGGLY$#{node.cond.tag_id}$PIGGLY$, (#{cond}))"
67
+ node.condStub.source_text = "#{pre}public.piggly_cond($PIGGLY$#{node.cond.tag_id}$PIGGLY$, (#{cond}))"
56
68
  node.condStub.source_text << traverse(node.cond.tail, oid, tags) # preserve trailing whitespace
57
69
  end
58
70
 
@@ -63,17 +75,17 @@ module Piggly
63
75
 
64
76
  # Hack to simulate a loop conditional statement in stmtForLoop and stmtLoop.
65
77
  # signal condition is true when body is executed, and false when exit stub is reached
66
- node.bodyStub.source_text = "perform piggly_cond($PIGGLY$#{node.cond.tag_id}$PIGGLY$, true);#{node.indent(:bodySpace)}"
67
- node.bodyStub.source_text << "perform piggly_branch($PIGGLY$#{node.body.tag_id}$PIGGLY$);#{node.indent(:bodySpace)}"
78
+ node.bodyStub.source_text = "perform public.piggly_cond($PIGGLY$#{node.cond.tag_id}$PIGGLY$, true);#{node.indent(:bodySpace)}"
79
+ node.bodyStub.source_text << "perform public.piggly_branch($PIGGLY$#{node.body.tag_id}$PIGGLY$);#{node.indent(:bodySpace)}"
68
80
 
69
81
  if node.respond_to?(:doneStub)
70
82
  # Signal the end of an iteration was reached
71
- node.doneStub.source_text = "#{node.indent(:bodySpace)}perform piggly_signal($PIGGLY$#{node.cond.tag_id}$PIGGLY$, $PIGGLY$@$PIGGLY$);"
83
+ node.doneStub.source_text = "#{node.indent(:bodySpace)}perform public.piggly_signal($PIGGLY$#{node.cond.tag_id}$PIGGLY$, $PIGGLY$@$PIGGLY$);"
72
84
  node.doneStub.source_text << node.body.indent
73
85
  end
74
86
 
75
87
  # Signal the loop terminated
76
- node.exitStub.source_text = "\n#{node.indent}perform piggly_cond($PIGGLY$#{node.cond.tag_id}$PIGGLY$, false);"
88
+ node.exitStub.source_text = "\n#{node.indent}perform public.piggly_cond($PIGGLY$#{node.cond.tag_id}$PIGGLY$, false);"
77
89
  elsif node.respond_to?(:body)
78
90
  # Unconditional branches (or blocks)
79
91
  # BEGIN ... END;
@@ -81,7 +93,7 @@ module Piggly
81
93
  # CONTINUE label;
82
94
  # EXIT label;
83
95
  tags << node.body.tag(oid)
84
- node.bodyStub.source_text = "perform piggly_branch($PIGGLY$#{node.body.tag_id}$PIGGLY$);#{node.indent(:bodySpace)}"
96
+ node.bodyStub.source_text = "perform public.piggly_branch($PIGGLY$#{node.body.tag_id}$PIGGLY$);#{node.indent(:bodySpace)}"
85
97
  end
86
98
  end
87
99
 
@@ -4,27 +4,55 @@ module Piggly
4
4
  class QualifiedType
5
5
  attr_reader :schema, :name
6
6
 
7
- def initialize(schema, name)
8
- @schema, @name = schema, normalize(name)
7
+ def self.parse(name, rest = nil)
8
+ if rest.to_s == ""
9
+ schema = nil
10
+ else
11
+ schema = name
12
+ name = rest
13
+ end
14
+
15
+ case name
16
+ when /(.*)\[\]$/
17
+ name = $1
18
+ array = "[]"
19
+ else
20
+ array = ""
21
+ end
22
+
23
+ if schema.to_s == ""
24
+ fst, snd = name.split(".", 2)
25
+ if snd.nil?
26
+ new(nil, fst, array)
27
+ else
28
+ new(fst, snd, array)
29
+ end
30
+ else
31
+ new(schema, name, array)
32
+ end
33
+ end
34
+
35
+ def initialize(schema, name, array)
36
+ @schema, @name, @array = schema, name, array
9
37
  end
10
38
 
11
39
  def shorten
12
- self.class.new(nil, @name)
40
+ self.class.new(nil, @name, @array)
13
41
  end
14
42
 
15
43
  def quote
16
44
  if @schema
17
- '"' + @schema + '"."' + @name + '"'
45
+ '"' + @schema + '"."' + @name + '"' + @array
18
46
  else
19
- '"' + @name + '"'
47
+ '"' + @name + '"' + @array
20
48
  end
21
49
  end
22
50
 
23
51
  def to_s
24
52
  if @schema and !%w[public pg_catalog].include?(@schema)
25
- @schema + "." + readable(@name)
53
+ @schema + "." + readable(@name) + @array
26
54
  else
27
- readable(@name)
55
+ readable(@name) + @array
28
56
  end
29
57
  end
30
58
 
@@ -42,7 +70,6 @@ module Piggly
42
70
  # group by ret.typname, format_type(ret.oid, null)
43
71
  # order by format_type(ret.oid, null);
44
72
  case name
45
- when /(.*)\[\]/ then "_#{normalize($1)}"
46
73
  when '"any"' then "any"
47
74
  when "bigint" then "int8"
48
75
  when "bit varying" then "varbit"
@@ -63,10 +63,11 @@ module Piggly
63
63
  end
64
64
 
65
65
  def defaults(exprs, count, total)
66
- exprs = exprs.split(", ")
66
+ exprs = if exprs.nil? then [] else exprs.split(", ") end
67
+
67
68
  nreqd = total - count
68
69
 
69
- if nreqd > 0 and exprs.length == count
70
+ if nreqd >= 0 and exprs.length == count
70
71
  Array.new(nreqd) + exprs
71
72
  else
72
73
  raise "Couldn't parse default arguments"
@@ -125,17 +126,25 @@ module Piggly
125
126
  def from_hash(hash)
126
127
  new(hash["source"],
127
128
  hash["oid"],
128
- QualifiedName.new(hash["nschema"], hash["name"]),
129
+ QualifiedName.new(hash["nschema"].to_s, hash["name"].to_s),
129
130
  hash["strict"] == "t",
130
131
  hash["secdef"] == "t",
131
132
  hash["setof"] == "t",
132
- QualifiedType.new(hash["tschema"], hash["type"]),
133
+ QualifiedType.parse(hash["tschema"].to_s, hash["type"].to_s),
133
134
  volatility(hash["volatility"]),
134
- hash["arg_modes"].to_s.split(",").map{|x| mode(x.strip) },
135
+ coalesce(hash["arg_modes"].to_s.split(",").map{|x| mode(x.strip) }, ["in"]*hash["arg_count"].to_i),
135
136
  hash["arg_names"].to_s.split(",").map{|x| QualifiedName.new(nil, x.strip) },
136
- hash["arg_types"].to_s.split(",").map{|x| QualifiedType.new(nil, x.strip) },
137
+ hash["arg_types"].to_s.split(",").map{|x| QualifiedType.parse(x.strip) },
137
138
  defaults(hash["arg_defaults"], hash["arg_defaults_count"].to_i, hash["arg_count"].to_i))
138
139
  end
140
+
141
+ def coalesce(value, default)
142
+ if [nil, "", []].include?(value)
143
+ default
144
+ else
145
+ value
146
+ end
147
+ end
139
148
  end
140
149
 
141
150
  end
@@ -256,9 +256,7 @@ grammar Piggly
256
256
  end
257
257
 
258
258
  rule stmtGetDiag
259
- kwGET tSpace
260
- kwPERFORM tSpace
261
- notImplemented
259
+ kwGET tSpace kwSTACKED tSpace kwDIAGNOSTICS tSpace expressionUntilSemiColon ';' <Piggly::Parser::Nodes::Expression>
262
260
  end
263
261
 
264
262
  rule stmtNull
@@ -482,7 +480,8 @@ grammar Piggly
482
480
  / 'create'
483
481
  / 'set'
484
482
  / 'start'
485
- / 'notify' ) ![a-z0-9] <Piggly::Parser::Nodes::TKeyword>
483
+ / 'notify'
484
+ / 'with' ) ![a-z0-9] <Piggly::Parser::Nodes::TKeyword>
486
485
  end
487
486
 
488
487
  rule keyword
@@ -692,6 +691,10 @@ grammar Piggly
692
691
  'scroll' ![a-z0-9_] <Piggly::Parser::Nodes::TKeyword>
693
692
  end
694
693
 
694
+ rule kwSTACKED
695
+ 'stacked' ![a-z0-9_] <Piggly::Parser::Nodes::TKeyword>
696
+ end
697
+
695
698
  rule kwSTRICT
696
699
  'strict' ![a-z0-9_] <Piggly::Parser::Nodes::TKeyword>
697
700
  end
@@ -99,7 +99,7 @@ module Piggly
99
99
  if m = pattern.match(message)
100
100
  ping(m.captures[0], m.captures[1])
101
101
  else
102
- stderr.puts(message)
102
+ stderr.puts("unknown trace: #{message}")
103
103
  end
104
104
  end
105
105
  end
@@ -1,10 +1,10 @@
1
1
  module Piggly
2
2
  module VERSION
3
3
  MAJOR = 2
4
- MINOR = 0
4
+ MINOR = 1
5
5
  TINY = 0
6
6
 
7
- RELEASE_DATE = "2016-05-03"
7
+ RELEASE_DATE = "2017-09-07"
8
8
  end
9
9
 
10
10
  class << VERSION
data/spec/028_spec.rb ADDED
@@ -0,0 +1,48 @@
1
+ require "spec_helper"
2
+
3
+ module Piggly
4
+ describe "github issue #28" do
5
+ include GrammarHelper
6
+
7
+ it "can parse the a GET DIAGNOSTICS expression" do
8
+ body = 'GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT, text_var2 = PG_EXCEPTION_DETAIL, text_var3 = PG_EXCEPTION_HINT;'
9
+
10
+ node = parse(:statement, body)
11
+ node.should be_statement
12
+ end
13
+
14
+ it "can parse a procedure with GET DIAGNOSTICS" do
15
+ body = <<-SQL
16
+ DECLARE
17
+ text_var1 text;
18
+ text_var2 text;
19
+ text_var3 text;
20
+ BEGIN
21
+ RETURN 1/0;
22
+ EXCEPTION WHEN SQLSTATE '22012' THEN
23
+ GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT,
24
+ text_var2 = PG_EXCEPTION_DETAIL,
25
+ text_var3 = PG_EXCEPTION_HINT;
26
+ END
27
+ SQL
28
+
29
+ node = parse(:start, body.strip)
30
+ node.count{|e| e.branch? }.should == 1 # catch
31
+ node.find{|e| e.branch? }.body.source_text.strip.should =~ /^GET.+HINT;/m
32
+ end
33
+
34
+ it "can parse WITH <> AS <> SELECT <> SQL query" do
35
+ body = <<-SQL
36
+ DECLARE
37
+ BEGIN
38
+ WITH n AS (SELECT first_name FROM users where id > 1000)
39
+ SELECT * FROM people WHERE people.first_name = n.first_name;
40
+ RETURN 1;
41
+ END
42
+ SQL
43
+
44
+ node = parse(:start, body.strip.downcase)
45
+ node.count{|e| e.sql? }.should == 1
46
+ end
47
+ end
48
+ end
@@ -44,7 +44,7 @@ describe Profile do
44
44
  context "when message doesn't match PATTERN" do
45
45
  it "prints the message to stderr" do
46
46
  message = "WARNING: Parameter was NULL and I don't like it!"
47
- @stderr.should_receive(:puts).with(message)
47
+ @stderr.should_receive(:puts).with("unknown trace: #{message}")
48
48
  @callback.call(message)
49
49
  end
50
50
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: piggly
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kvle Putnam
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-03 00:00:00.000000000 Z
11
+ date: 2017-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: treetop
@@ -52,7 +52,6 @@ files:
52
52
  - lib/piggly/command.rb
53
53
  - lib/piggly/command/base.rb
54
54
  - lib/piggly/command/report.rb
55
- - lib/piggly/command/test.rb
56
55
  - lib/piggly/command/trace.rb
57
56
  - lib/piggly/command/untrace.rb
58
57
  - lib/piggly/compiler.rb
@@ -91,6 +90,7 @@ files:
91
90
  - lib/piggly/util/process_queue.rb
92
91
  - lib/piggly/util/thunk.rb
93
92
  - lib/piggly/version.rb
93
+ - spec/028_spec.rb
94
94
  - spec/examples/compiler/cacheable_spec.rb
95
95
  - spec/examples/compiler/report_spec.rb
96
96
  - spec/examples/compiler/trace_spec.rb
@@ -157,8 +157,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
157
  version: '0'
158
158
  requirements: []
159
159
  rubyforge_project:
160
- rubygems_version: 2.5.1
160
+ rubygems_version: 2.2.2
161
161
  signing_key:
162
162
  specification_version: 4
163
163
  summary: PL/pgSQL code coverage tool
164
164
  test_files: []
165
+ has_rdoc: false
@@ -1,157 +0,0 @@
1
- module Piggly
2
- module Command
3
-
4
- #
5
- # This command handles all the setup and teardown for running Ruby tests, that can
6
- # otherwise be accomplished in a more manual fashion, using the other commands. It
7
- # assumes that the test files will automatically establish a connection to the correct
8
- # database when they are loaded (this is the case for Rails).
9
- #
10
- module Test
11
- class << self
12
-
13
- def main(argv)
14
- benchmark do
15
- tests, filters = parse_options(argv)
16
- profile = Profile.new
17
-
18
- # don't let rspec get these when loading spec files
19
- ARGV.clear
20
-
21
- load_tests(tests)
22
- Command.connect_to_database
23
-
24
- procedures = dump_procedures(filters)
25
-
26
- if procedures.empty?
27
- abort "No stored procedures in the database#{' matched your criteria' if filters.any?}"
28
- end
29
-
30
- result =
31
- begin
32
- trace(procedures)
33
- clear_coverage
34
- execute_tests
35
- ensure
36
- untrace(procedures)
37
- end
38
-
39
- create_index(procedures)
40
- create_reports(procedures)
41
- store_coverage
42
-
43
- exit! result # avoid running tests again
44
- end
45
- end
46
-
47
- private
48
-
49
- def benchmark
50
- start = Time.now
51
- value = yield
52
- puts " > Completed in #{'%0.2f' % (Time.now - start)} seconds"
53
- return value
54
- end
55
-
56
- #
57
- # Parses command-line options
58
- #
59
- def parse_options(argv)
60
- filters = []
61
-
62
- opts = OptionParser.new do |opts|
63
- opts.on("-I", "--include PATHS", "Prepend paths to $LOAD_PATH (colon separated list)", &Command.method(:opt_include_path))
64
- opts.on("-o", "--report-root PATH", "Report output directory", &Command.method(:opt_report_root))
65
- opts.on("-c", "--cache-root PATH", "Local cache directory", &Command.method(:opt_cache_root))
66
-
67
- opts.on("-n", "--name PATTERN", "Trace stored procedures matching PATTERN") do |opt|
68
- if m = opt.match(%r{^/(.+)/$})
69
- filters << lambda{|p| p.name.match(m.captures.first) }
70
- else
71
- filters << lambda{|p| p.name === opt }
72
- end
73
- end
74
-
75
- opts.on("-a", "--aggregate", "Aggregate data from the previous run", &Command.method(:opt_aggregate))
76
- opts.on("-V", "--version", "Show version", &Command.method(:opt_version))
77
- opts.on("-h", "--help", "Show this message") do
78
- puts opts
79
- exit! 0
80
- end
81
- end
82
-
83
- begin
84
- opts.parse! argv
85
- raise OptionParser::MissingArgument, "no tests specified" if argv.empty?
86
- rescue OptionParser::InvalidOption,
87
- OptionParser::InvalidArgument,
88
- OptionParser::MissingArgument
89
- puts opts
90
- puts
91
- puts $!
92
-
93
- exit! 1
94
- end
95
-
96
- test_paths = argv.map{|p| Dir[p] }.flatten.sort
97
-
98
- return test_paths, filters
99
- end
100
-
101
- def load_tests(tests)
102
- puts "Loading #{tests.size} test files"
103
- benchmark { tests.each{|file| load file }}
104
- end
105
-
106
- #
107
- # Writes all stored procedures in the database to disk
108
- #
109
- def dump_procedures(filters)
110
- Command::Trace.dump_procedures(filters)
111
- end
112
-
113
- #
114
- # Compiles all the stored procedures on disk and installs them
115
- #
116
- def trace(procedures)
117
- benchmark { Command::Trace.trace(procedures) }
118
- end
119
-
120
- def clear_coverage
121
- Command::Report.clear_coverage
122
- end
123
-
124
- def execute_tests
125
- if defined? ::Test::Unit::AutoRunner
126
- ::Test::Unit::AutoRunner.run
127
- elsif defined? ::RSpec::Core
128
- ::Rspec::Core::Runner.run(ARGV, $stderr, $stdout)
129
- elsif defined? ::Spec::Runner
130
- ::Spec::Runner.run
131
- elsif defined? ::MiniTest::Unit
132
- ::MiniTest::Unit.new.run(ARGV)
133
- else
134
- raise "Neither Test::Unit, MiniTest, nor RSpec were detected"
135
- end
136
- end
137
-
138
- def store_coverage(profile)
139
- benchmark { Command::Report.store_coverage(profile) }
140
- end
141
-
142
- def untrace(procedures)
143
- benchmark { Command::Untrace.untrace(procedures) }
144
- end
145
-
146
- def create_index(procedures, profile)
147
- benchmark { Command::Report.create_index(procedures, profile) }
148
- end
149
-
150
- def create_reports(procedures, profile)
151
- benchmark { Command::Report.create_reports(procedures, profile) }
152
- end
153
-
154
- end
155
- end
156
- end
157
- end