piggly-nsd 2.3.4 → 2.3.5
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 +4 -4
- data/README.md +17 -3
- data/lib/piggly/command/report.rb +25 -7
- data/lib/piggly/compiler/coverage_report.rb +1 -1
- data/lib/piggly/compiler/line_coverage.rb +7 -9
- data/lib/piggly/parser/grammar.tt +760 -748
- data/lib/piggly/reporter/schema_csv.rb +98 -0
- data/lib/piggly/reporter/sonar.rb +6 -1
- data/lib/piggly/reporter.rb +1 -0
- data/lib/piggly/task.rb +4 -1
- data/lib/piggly/util/line_numbers.rb +25 -0
- data/lib/piggly/util.rb +1 -0
- data/lib/piggly/version.rb +2 -2
- data/spec/examples/grammar/statements/loop_spec.rb +9 -0
- data/spec/examples/grammar/statements/sql_spec.rb +14 -0
- data/spec/examples/reporter/schema_csv_spec.rb +72 -0
- data/spec/examples/util/line_numbers_spec.rb +59 -0
- data/spec/issues/037_spec.rb +30 -0
- data/spec/issues/case_in_condition_spec.rb +50 -0
- data/spec/issues/commit_spec.rb +34 -0
- data/spec/spec_helper.rb +1 -1
- metadata +23 -2
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require "csv"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
|
|
4
|
+
module Piggly
|
|
5
|
+
module Reporter
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# Generates schema-level coverage summary in CSV format.
|
|
9
|
+
#
|
|
10
|
+
class SchemaCsv < Base
|
|
11
|
+
HEADERS = ["No", "Schema Name", "Objects Count", "Covered Objects", "Line Coverage Percent"].freeze
|
|
12
|
+
NO_SCHEMA = "<no schema>".freeze
|
|
13
|
+
|
|
14
|
+
def initialize(config, profile, output_path = nil)
|
|
15
|
+
@config = config
|
|
16
|
+
@profile = profile
|
|
17
|
+
@output_path = output_path || File.join(@config.report_root, "coverage_by_schema.csv")
|
|
18
|
+
@line_coverage = Compiler::LineCoverage.new(config)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Generate CSV schema coverage report for all procedures
|
|
22
|
+
# @param procedures [Array<Dumper::ReifiedProcedure>] list of procedures
|
|
23
|
+
def report(procedures)
|
|
24
|
+
FileUtils.makedirs(File.dirname(@output_path))
|
|
25
|
+
aggregates = aggregate_by_schema(procedures)
|
|
26
|
+
|
|
27
|
+
CSV.open(@output_path, "wb:UTF-8") do |csv|
|
|
28
|
+
csv << HEADERS
|
|
29
|
+
|
|
30
|
+
aggregates.each_with_index do |row, index|
|
|
31
|
+
csv << [
|
|
32
|
+
index + 1,
|
|
33
|
+
row[:schema_name],
|
|
34
|
+
row[:objects_count],
|
|
35
|
+
row[:covered_objects],
|
|
36
|
+
format("%0.2f", row[:coverage_percent])
|
|
37
|
+
]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@output_path
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def aggregate_by_schema(procedures)
|
|
47
|
+
grouped = Hash.new do |hash, key|
|
|
48
|
+
hash[key] = {
|
|
49
|
+
schema_name: key,
|
|
50
|
+
objects_count: 0,
|
|
51
|
+
covered_objects: 0,
|
|
52
|
+
coverage_values: []
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
procedures.each do |procedure|
|
|
57
|
+
schema_name = schema_label(procedure)
|
|
58
|
+
row = grouped[schema_name]
|
|
59
|
+
row[:objects_count] += 1
|
|
60
|
+
|
|
61
|
+
begin
|
|
62
|
+
percent = line_coverage_percent(procedure)
|
|
63
|
+
next if percent.nil?
|
|
64
|
+
|
|
65
|
+
row[:covered_objects] += 1 if percent > 0.0
|
|
66
|
+
row[:coverage_values] << percent
|
|
67
|
+
rescue => e
|
|
68
|
+
$stderr.puts "Warning: Could not calculate schema CSV coverage for #{procedure.name}: #{e.message}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
grouped.keys.sort.map do |schema_name|
|
|
73
|
+
row = grouped[schema_name]
|
|
74
|
+
values = row[:coverage_values]
|
|
75
|
+
average = values.empty? ? 0.0 : values.inject(0.0, :+) / values.size
|
|
76
|
+
|
|
77
|
+
{
|
|
78
|
+
schema_name: row[:schema_name],
|
|
79
|
+
objects_count: row[:objects_count],
|
|
80
|
+
covered_objects: row[:covered_objects],
|
|
81
|
+
coverage_percent: average
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def schema_label(procedure)
|
|
87
|
+
schema = procedure.name.schema.to_s.strip
|
|
88
|
+
schema.empty? ? NO_SCHEMA : schema
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def line_coverage_percent(procedure)
|
|
92
|
+
coverage = @line_coverage.calculate(procedure, @profile)
|
|
93
|
+
@line_coverage.summary(coverage)[:percent]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -52,9 +52,14 @@ module Piggly
|
|
|
52
52
|
source_path = make_relative_path(absolute_path)
|
|
53
53
|
|
|
54
54
|
io.puts " <file path=\"#{escape_xml(source_path)}\">"
|
|
55
|
-
|
|
55
|
+
|
|
56
|
+
source = procedure.source(@config)
|
|
57
|
+
max_line = Util::LineNumbers.count(source)
|
|
58
|
+
|
|
56
59
|
# Sort lines and output coverage data
|
|
57
60
|
coverage.keys.sort.each do |line|
|
|
61
|
+
next if line < 1 || line > max_line
|
|
62
|
+
|
|
58
63
|
line_data = coverage[line]
|
|
59
64
|
write_line_coverage(io, line, line_data)
|
|
60
65
|
end
|
data/lib/piggly/reporter.rb
CHANGED
data/lib/piggly/task.rb
CHANGED
|
@@ -118,12 +118,14 @@ module Piggly
|
|
|
118
118
|
class ReportTask < AbstractTask
|
|
119
119
|
attr_accessor :report_root, # Where to store reports (default piggly/report)
|
|
120
120
|
:accumulate, # Accumulate coverage from the previous run (default false)
|
|
121
|
-
:trace_file
|
|
121
|
+
:trace_file,
|
|
122
|
+
:schema_csv_path
|
|
122
123
|
|
|
123
124
|
def initialize(name = :report)
|
|
124
125
|
@accumulate = false
|
|
125
126
|
@trace_file = nil
|
|
126
127
|
@report_root = nil
|
|
128
|
+
@schema_csv_path = nil
|
|
127
129
|
super(name)
|
|
128
130
|
end
|
|
129
131
|
|
|
@@ -141,6 +143,7 @@ module Piggly
|
|
|
141
143
|
opts.concat(["--trace-file", @trace_file])
|
|
142
144
|
opts.concat(["--cache-root", @cache_root]) if @cache_root
|
|
143
145
|
opts.concat(["--report-root", @report_root]) if @report_root
|
|
146
|
+
opts.concat(["--schema-csv-path", @schema_csv_path]) if @schema_csv_path
|
|
144
147
|
|
|
145
148
|
case @procedures
|
|
146
149
|
when String then opts.concat(["--name", @procedures])
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Piggly
|
|
2
|
+
module Util
|
|
3
|
+
|
|
4
|
+
module LineNumbers
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def count(source)
|
|
8
|
+
return 0 if source.nil? || source.empty?
|
|
9
|
+
|
|
10
|
+
source.lines.count
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# 1-based line number for a byte offset, clamped to [1, count(source)].
|
|
14
|
+
def at_offset(source, offset)
|
|
15
|
+
return 1 if source.nil? || source.empty?
|
|
16
|
+
|
|
17
|
+
offset = [[offset, 0].max, source.length].min
|
|
18
|
+
line = source[0...offset].count("\n") + 1
|
|
19
|
+
max_line = count(source)
|
|
20
|
+
[[line, 1].max, max_line].min
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/piggly/util.rb
CHANGED
data/lib/piggly/version.rb
CHANGED
|
@@ -23,6 +23,15 @@ module Piggly
|
|
|
23
23
|
cond.source_text.should == 'SELECT * FROM table '
|
|
24
24
|
cond.should be_sql
|
|
25
25
|
end
|
|
26
|
+
|
|
27
|
+
it "can loop over parenthesized query without whitespace after IN" do
|
|
28
|
+
node = parse(:stmtForLoop, 'FOR x IN(SELECT * FROM table) LOOP a := x; END LOOP;')
|
|
29
|
+
node.should be_statement
|
|
30
|
+
|
|
31
|
+
cond = node.find{|e| e.named?(:cond) }
|
|
32
|
+
cond.source_text.should == '(SELECT * FROM table) '
|
|
33
|
+
cond.should be_expression
|
|
34
|
+
end
|
|
26
35
|
end
|
|
27
36
|
|
|
28
37
|
describe "while loops" do
|
|
@@ -13,6 +13,20 @@ module Piggly
|
|
|
13
13
|
rest.should == ''
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
it "parses MERGE statements" do
|
|
17
|
+
node, rest = parse_some(:statement, <<-SQL.strip)
|
|
18
|
+
MERGE INTO users u
|
|
19
|
+
USING (SELECT 1 AS id) s
|
|
20
|
+
ON s.id = u.id
|
|
21
|
+
WHEN NOT MATCHED THEN
|
|
22
|
+
INSERT (id) VALUES (s.id);
|
|
23
|
+
SQL
|
|
24
|
+
node.should be_statement
|
|
25
|
+
node.count{|e| e.sql? }.should == 1
|
|
26
|
+
node.find{|e| e.sql? }.source_text.should =~ /\Amerge into users/mi
|
|
27
|
+
rest.should == ''
|
|
28
|
+
end
|
|
29
|
+
|
|
16
30
|
it "must end with a semicolon" do
|
|
17
31
|
expect{ parse(:statement, 'SELECT id FROM users') }.to raise_error(Piggly::Parser::Failure)
|
|
18
32
|
expect{ parse_some(:statement, 'SELECT id FROM users') }.to raise_error(Piggly::Parser::Failure)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "csv"
|
|
3
|
+
require "tmpdir"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module Piggly
|
|
7
|
+
|
|
8
|
+
describe Reporter::SchemaCsv do
|
|
9
|
+
let(:profile) { double(:profile) }
|
|
10
|
+
let(:line_coverage) { double(:line_coverage) }
|
|
11
|
+
let(:tmpdir) { Dir.mktmpdir("piggly-schema-csv") }
|
|
12
|
+
let(:config) { double(:config, :report_root => tmpdir) }
|
|
13
|
+
let(:output_path) { File.join(tmpdir, "schema-coverage.csv") }
|
|
14
|
+
|
|
15
|
+
def procedure(schema, name)
|
|
16
|
+
qualified_name = Dumper::QualifiedName.new(schema, name)
|
|
17
|
+
double(:procedure, :name => qualified_name)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
before do
|
|
21
|
+
allow(Compiler::LineCoverage).to receive(:new).with(config).and_return(line_coverage)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
after do
|
|
25
|
+
FileUtils.remove_entry(tmpdir) if File.exist?(tmpdir)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "writes schema coverage CSV with headers and aggregated rows" do
|
|
29
|
+
first_public = procedure("public", "alpha")
|
|
30
|
+
second_public = procedure("public", "beta")
|
|
31
|
+
app_proc = procedure("app", "gamma")
|
|
32
|
+
no_schema_proc = procedure(nil, "delta")
|
|
33
|
+
|
|
34
|
+
allow(line_coverage).to receive(:calculate).with(first_public, profile).and_return(:cov_public_1)
|
|
35
|
+
allow(line_coverage).to receive(:calculate).with(second_public, profile).and_return(:cov_public_2)
|
|
36
|
+
allow(line_coverage).to receive(:calculate).with(app_proc, profile).and_return(:cov_app)
|
|
37
|
+
allow(line_coverage).to receive(:calculate).with(no_schema_proc, profile).and_return(:cov_no_schema)
|
|
38
|
+
|
|
39
|
+
allow(line_coverage).to receive(:summary).with(:cov_public_1).and_return(:percent => 100.0)
|
|
40
|
+
allow(line_coverage).to receive(:summary).with(:cov_public_2).and_return(:percent => 0.0)
|
|
41
|
+
allow(line_coverage).to receive(:summary).with(:cov_app).and_return(:percent => nil)
|
|
42
|
+
allow(line_coverage).to receive(:summary).with(:cov_no_schema).and_return(:percent => 75.0)
|
|
43
|
+
|
|
44
|
+
reporter = Reporter::SchemaCsv.new(config, profile, output_path)
|
|
45
|
+
result_path = reporter.report([first_public, second_public, app_proc, no_schema_proc])
|
|
46
|
+
|
|
47
|
+
expect(result_path).to eq(output_path)
|
|
48
|
+
expect(File).to exist(output_path)
|
|
49
|
+
|
|
50
|
+
rows = CSV.read(output_path, :headers => true)
|
|
51
|
+
|
|
52
|
+
expect(rows.headers).to eq(["No", "Schema Name", "Objects Count", "Covered Objects", "Line Coverage Percent"])
|
|
53
|
+
|
|
54
|
+
by_schema = rows.each_with_object({}) do |row, hash|
|
|
55
|
+
hash[row["Schema Name"]] = row
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
expect(by_schema.fetch("public")["Objects Count"]).to eq("2")
|
|
59
|
+
expect(by_schema.fetch("public")["Covered Objects"]).to eq("1")
|
|
60
|
+
expect(by_schema.fetch("public")["Line Coverage Percent"]).to eq("50.00")
|
|
61
|
+
|
|
62
|
+
expect(by_schema.fetch("app")["Objects Count"]).to eq("1")
|
|
63
|
+
expect(by_schema.fetch("app")["Covered Objects"]).to eq("0")
|
|
64
|
+
expect(by_schema.fetch("app")["Line Coverage Percent"]).to eq("0.00")
|
|
65
|
+
|
|
66
|
+
expect(by_schema.fetch("<no schema>")["Objects Count"]).to eq("1")
|
|
67
|
+
expect(by_schema.fetch("<no schema>")["Covered Objects"]).to eq("1")
|
|
68
|
+
expect(by_schema.fetch("<no schema>")["Line Coverage Percent"]).to eq("75.00")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Piggly::Util
|
|
4
|
+
|
|
5
|
+
describe LineNumbers do
|
|
6
|
+
describe ".count" do
|
|
7
|
+
it "returns 0 for empty source" do
|
|
8
|
+
expect(LineNumbers.count("")).to eq(0)
|
|
9
|
+
expect(LineNumbers.count(nil)).to eq(0)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "counts lines without a trailing newline" do
|
|
13
|
+
expect(LineNumbers.count("a\nb\nc")).to eq(3)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "does not over-count when source ends with a newline" do
|
|
17
|
+
expect(LineNumbers.count("a\nb\nc\n")).to eq(3)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "counts CRLF sources consistently" do
|
|
21
|
+
expect(LineNumbers.count("a\r\nb\r\nc\r\n")).to eq(3)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "matches String#lines for a large trailing-newline body" do
|
|
25
|
+
source = (1..366).map { |i| "line#{i}" }.join("\n") + "\n"
|
|
26
|
+
expect(LineNumbers.count(source)).to eq(366)
|
|
27
|
+
expect(LineNumbers.count(source)).to eq(source.lines.count)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe ".at_offset" do
|
|
32
|
+
let(:source) { "a\nb\nc\n" }
|
|
33
|
+
|
|
34
|
+
it "returns 1 for empty source" do
|
|
35
|
+
expect(LineNumbers.at_offset("", 0)).to eq(1)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "maps byte offsets to 1-based line numbers" do
|
|
39
|
+
expect(LineNumbers.at_offset(source, 0)).to eq(1)
|
|
40
|
+
expect(LineNumbers.at_offset(source, 2)).to eq(2)
|
|
41
|
+
expect(LineNumbers.at_offset(source, 4)).to eq(3)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "clamps offsets past EOF to the last line" do
|
|
45
|
+
expect(LineNumbers.at_offset(source, source.length)).to eq(3)
|
|
46
|
+
expect(LineNumbers.at_offset(source, source.length + 10)).to eq(3)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "does not report a line beyond count when offset is on trailing newline" do
|
|
50
|
+
source = (1..366).map { |i| "line#{i}" }.join("\n") + "\n"
|
|
51
|
+
max_line = LineNumbers.count(source)
|
|
52
|
+
|
|
53
|
+
expect(LineNumbers.at_offset(source, source.length - 1)).to eq(max_line)
|
|
54
|
+
expect(LineNumbers.at_offset(source, source.length)).to eq(max_line)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
module Piggly
|
|
4
|
+
describe "GET CURRENT DIAGNOSTICS" do
|
|
5
|
+
include GrammarHelper
|
|
6
|
+
|
|
7
|
+
it "can parse a GET CURRENT DIAGNOSTICS statement" do
|
|
8
|
+
body = "GET CURRENT DIAGNOSTICS l_wdc_inserted := ROW_COUNT;"
|
|
9
|
+
|
|
10
|
+
node = parse(:statement, body)
|
|
11
|
+
node.should be_statement
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "can parse a procedure with GET CURRENT DIAGNOSTICS" do
|
|
15
|
+
body = <<-SQL
|
|
16
|
+
DECLARE
|
|
17
|
+
l_wdc_inserted bigint;
|
|
18
|
+
BEGIN
|
|
19
|
+
INSERT INTO foo DEFAULT VALUES;
|
|
20
|
+
GET CURRENT DIAGNOSTICS l_wdc_inserted := ROW_COUNT;
|
|
21
|
+
RETURN l_wdc_inserted;
|
|
22
|
+
END;
|
|
23
|
+
SQL
|
|
24
|
+
|
|
25
|
+
node = parse(:start, body.strip.downcase)
|
|
26
|
+
node.count { |e| e.assignment? }.should == 0
|
|
27
|
+
node.count { |e| Parser::Nodes::Return === e }.should == 1
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
module Piggly
|
|
4
|
+
describe "SQL CASE in PL/pgSQL conditions" do
|
|
5
|
+
include GrammarHelper
|
|
6
|
+
|
|
7
|
+
it "can parse IF with CASE in a function argument" do
|
|
8
|
+
body = <<-SQL
|
|
9
|
+
BEGIN
|
|
10
|
+
IF 0 = public.f_test(
|
|
11
|
+
p_doc_type_mnemo => line_rec.doc_type_mnemo,
|
|
12
|
+
p_person_id => CASE
|
|
13
|
+
WHEN l_f_sub_acc = 1 THEN p_client_id
|
|
14
|
+
WHEN l_f_sub_acc = 0 THEN l_person_id
|
|
15
|
+
END,
|
|
16
|
+
p_repres_id => NULL
|
|
17
|
+
) THEN
|
|
18
|
+
NULL;
|
|
19
|
+
END IF;
|
|
20
|
+
END;
|
|
21
|
+
SQL
|
|
22
|
+
|
|
23
|
+
node = parse(:start, body.strip.downcase)
|
|
24
|
+
node.count { |e| e.if? }.should == 1
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "can parse ELSIF with CASE in a function argument" do
|
|
28
|
+
body = <<-SQL
|
|
29
|
+
BEGIN
|
|
30
|
+
IF false THEN
|
|
31
|
+
NULL;
|
|
32
|
+
ELSIF 0 = public.f_test(
|
|
33
|
+
p_person_id => CASE WHEN l_flag = 1 THEN p_a ELSE p_b END
|
|
34
|
+
) THEN
|
|
35
|
+
NULL;
|
|
36
|
+
END IF;
|
|
37
|
+
END;
|
|
38
|
+
SQL
|
|
39
|
+
|
|
40
|
+
node = parse(:start, body.strip.downcase)
|
|
41
|
+
node.count { |e| e.if? }.should == 2
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "does not break simple IF conditions" do
|
|
45
|
+
node = parse(:statement, "IF cond THEN a := 10; END IF;")
|
|
46
|
+
node.should be_statement
|
|
47
|
+
node.count { |e| e.if? }.should == 1
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
module Piggly
|
|
4
|
+
describe "standalone transaction statements" do
|
|
5
|
+
include GrammarHelper
|
|
6
|
+
|
|
7
|
+
it "can parse commit without trailing expression" do
|
|
8
|
+
node = parse(:statement, "commit;")
|
|
9
|
+
node.should be_statement
|
|
10
|
+
node.count { |e| e.sql? }.should == 1
|
|
11
|
+
node.find { |e| e.sql? }.source_text.should == "commit;"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "can parse rollback without trailing expression" do
|
|
15
|
+
node = parse(:statement, "rollback;")
|
|
16
|
+
node.should be_statement
|
|
17
|
+
node.count { |e| e.sql? }.should == 1
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "can parse a procedure with multiple commit statements" do
|
|
21
|
+
body = <<-SQL
|
|
22
|
+
BEGIN
|
|
23
|
+
call i_schema.create_choice1(p_session_id => l_session_id);
|
|
24
|
+
commit;
|
|
25
|
+
call i_schema.create_choice2(l_session_id);
|
|
26
|
+
commit;
|
|
27
|
+
END;
|
|
28
|
+
SQL
|
|
29
|
+
|
|
30
|
+
node = parse(:start, body.strip.downcase)
|
|
31
|
+
node.count { |e| e.sql? }.should == 4
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -26,7 +26,7 @@ module Piggly
|
|
|
26
26
|
|
|
27
27
|
COMMENTS = ["abc defghi", "abc -- abc", "quote's", "a 'str'"]
|
|
28
28
|
|
|
29
|
-
SQLWORDS = %w[select insert update delete drop alter commit set start]
|
|
29
|
+
SQLWORDS = %w[select insert update delete merge drop alter commit set start]
|
|
30
30
|
|
|
31
31
|
KEYWORDS = %w[as := = alias begin by constant continue
|
|
32
32
|
cursor debug declare diagnostics else elsif elseif
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: piggly-nsd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.3.
|
|
4
|
+
version: 2.3.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kvle Putnam
|
|
@@ -37,6 +37,20 @@ dependencies:
|
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: 0.18.4
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: csv
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.3'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.3'
|
|
40
54
|
description: PostgreSQL PL/pgSQL stored procedure code coverage (NSD fork)
|
|
41
55
|
email: putnam.kvle@gmail.com
|
|
42
56
|
executables:
|
|
@@ -80,6 +94,7 @@ files:
|
|
|
80
94
|
- lib/piggly/reporter/resources/highlight.js
|
|
81
95
|
- lib/piggly/reporter/resources/piggly.css
|
|
82
96
|
- lib/piggly/reporter/resources/sortable.js
|
|
97
|
+
- lib/piggly/reporter/schema_csv.rb
|
|
83
98
|
- lib/piggly/reporter/sonar.rb
|
|
84
99
|
- lib/piggly/tags.rb
|
|
85
100
|
- lib/piggly/task.rb
|
|
@@ -88,6 +103,7 @@ files:
|
|
|
88
103
|
- lib/piggly/util/cacheable.rb
|
|
89
104
|
- lib/piggly/util/enumerable.rb
|
|
90
105
|
- lib/piggly/util/file.rb
|
|
106
|
+
- lib/piggly/util/line_numbers.rb
|
|
91
107
|
- lib/piggly/util/process_queue.rb
|
|
92
108
|
- lib/piggly/util/thunk.rb
|
|
93
109
|
- lib/piggly/version.rb
|
|
@@ -123,12 +139,14 @@ files:
|
|
|
123
139
|
- spec/examples/reporter/html/dsl_spec.rb
|
|
124
140
|
- spec/examples/reporter/html/index_spec.rb
|
|
125
141
|
- spec/examples/reporter/html_spec.rb
|
|
142
|
+
- spec/examples/reporter/schema_csv_spec.rb
|
|
126
143
|
- spec/examples/reporter_spec.rb
|
|
127
144
|
- spec/examples/tags_spec.rb
|
|
128
145
|
- spec/examples/task_spec.rb
|
|
129
146
|
- spec/examples/util/cacheable_spec.rb
|
|
130
147
|
- spec/examples/util/enumerable_spec.rb
|
|
131
148
|
- spec/examples/util/file_spec.rb
|
|
149
|
+
- spec/examples/util/line_numbers_spec.rb
|
|
132
150
|
- spec/examples/util/process_queue_spec.rb
|
|
133
151
|
- spec/examples/util/thunk_spec.rb
|
|
134
152
|
- spec/examples/version_spec.rb
|
|
@@ -138,9 +156,12 @@ files:
|
|
|
138
156
|
- spec/issues/028_spec.rb
|
|
139
157
|
- spec/issues/032_spec.rb
|
|
140
158
|
- spec/issues/036_spec.rb
|
|
159
|
+
- spec/issues/037_spec.rb
|
|
160
|
+
- spec/issues/case_in_condition_spec.rb
|
|
161
|
+
- spec/issues/commit_spec.rb
|
|
141
162
|
- spec/spec_helper.rb
|
|
142
163
|
- spec/spec_suite.rb
|
|
143
|
-
homepage: https://github.com/
|
|
164
|
+
homepage: https://github.com/NSDDeveloper/piggly
|
|
144
165
|
licenses:
|
|
145
166
|
- BSD-2-Clause
|
|
146
167
|
metadata: {}
|