piggly 2.0.0 → 2.1.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 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