piggly 1.2.1 → 2.0.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 +7 -0
- data/README.md +163 -0
- data/Rakefile +29 -15
- data/bin/piggly +4 -244
- data/lib/piggly.rb +19 -17
- data/lib/piggly/command.rb +9 -0
- data/lib/piggly/command/base.rb +148 -0
- data/lib/piggly/command/report.rb +162 -0
- data/lib/piggly/command/test.rb +157 -0
- data/lib/piggly/command/trace.rb +90 -0
- data/lib/piggly/command/untrace.rb +78 -0
- data/lib/piggly/compiler.rb +7 -5
- data/lib/piggly/compiler/cache_dir.rb +119 -0
- data/lib/piggly/compiler/coverage_report.rb +63 -0
- data/lib/piggly/compiler/trace_compiler.rb +105 -0
- data/lib/piggly/config.rb +47 -22
- data/lib/piggly/dumper.rb +9 -0
- data/lib/piggly/dumper/index.rb +121 -0
- data/lib/piggly/dumper/qualified_name.rb +36 -0
- data/lib/piggly/dumper/qualified_type.rb +81 -0
- data/lib/piggly/dumper/reified_procedure.rb +142 -0
- data/lib/piggly/dumper/skeleton_procedure.rb +102 -0
- data/lib/piggly/installer.rb +84 -42
- data/lib/piggly/parser.rb +43 -49
- data/lib/piggly/parser/grammar.tt +289 -313
- data/lib/piggly/parser/nodes.rb +270 -211
- data/lib/piggly/parser/traversal.rb +35 -33
- data/lib/piggly/parser/treetop_ruby19_patch.rb +1 -1
- data/lib/piggly/profile.rb +81 -60
- data/lib/piggly/reporter.rb +5 -18
- data/lib/piggly/reporter/base.rb +103 -0
- data/lib/piggly/reporter/html_dsl.rb +63 -0
- data/lib/piggly/reporter/index.rb +108 -0
- data/lib/piggly/reporter/procedure.rb +104 -0
- data/lib/piggly/reporter/resources/highlight.js +21 -0
- data/lib/piggly/reporter/{piggly.css → resources/piggly.css} +52 -12
- data/lib/piggly/reporter/{sortable.js → resources/sortable.js} +0 -0
- data/lib/piggly/tags.rb +280 -0
- data/lib/piggly/task.rb +191 -40
- data/lib/piggly/util.rb +8 -27
- data/lib/piggly/util/blankslate.rb +114 -0
- data/lib/piggly/util/cacheable.rb +19 -0
- data/lib/piggly/util/enumerable.rb +44 -0
- data/lib/piggly/util/file.rb +17 -0
- data/lib/piggly/util/process_queue.rb +96 -0
- data/lib/piggly/util/thunk.rb +39 -0
- data/lib/piggly/version.rb +8 -8
- data/spec/examples/compiler/cacheable_spec.rb +190 -0
- data/spec/examples/compiler/report_spec.rb +25 -0
- data/spec/{compiler → examples/compiler}/trace_spec.rb +7 -57
- data/spec/examples/config_spec.rb +61 -0
- data/spec/examples/dumper/index_spec.rb +197 -0
- data/spec/examples/dumper/procedure_spec.rb +116 -0
- data/spec/{grammar → examples/grammar}/expression_spec.rb +60 -60
- data/spec/{grammar → examples/grammar}/statements/assignment_spec.rb +15 -15
- data/spec/examples/grammar/statements/declaration_spec.rb +21 -0
- data/spec/{grammar → examples/grammar}/statements/exception_spec.rb +10 -10
- data/spec/{grammar → examples/grammar}/statements/if_spec.rb +47 -34
- data/spec/{grammar → examples/grammar}/statements/loop_spec.rb +5 -5
- data/spec/{grammar → examples/grammar}/statements/sql_spec.rb +11 -11
- data/spec/{grammar → examples/grammar}/tokens/comment_spec.rb +11 -11
- data/spec/{grammar → examples/grammar}/tokens/datatype_spec.rb +14 -8
- data/spec/{grammar → examples/grammar}/tokens/identifier_spec.rb +26 -10
- data/spec/{grammar → examples/grammar}/tokens/keyword_spec.rb +5 -5
- data/spec/{grammar → examples/grammar}/tokens/label_spec.rb +7 -7
- data/spec/{grammar → examples/grammar}/tokens/literal_spec.rb +1 -1
- data/spec/examples/grammar/tokens/lval_spec.rb +50 -0
- data/spec/{grammar → examples/grammar}/tokens/number_spec.rb +1 -1
- data/spec/{grammar → examples/grammar}/tokens/sqlkeywords_spec.rb +1 -1
- data/spec/{grammar → examples/grammar}/tokens/string_spec.rb +9 -9
- data/spec/{grammar → examples/grammar}/tokens/whitespace_spec.rb +1 -1
- data/spec/examples/installer_spec.rb +59 -0
- data/spec/examples/parser/nodes_spec.rb +73 -0
- data/spec/examples/parser/traversal_spec.rb +14 -0
- data/spec/examples/parser_spec.rb +115 -0
- data/spec/examples/profile_spec.rb +153 -0
- data/spec/{reporter/html_spec.rb → examples/reporter/html/dsl_spec.rb} +0 -0
- data/spec/examples/reporter/html/index_spec.rb +0 -0
- data/spec/examples/reporter/html_spec.rb +1 -0
- data/spec/examples/reporter_spec.rb +0 -0
- data/spec/{compiler → examples}/tags_spec.rb +10 -10
- data/spec/examples/task_spec.rb +0 -0
- data/spec/examples/util/cacheable_spec.rb +41 -0
- data/spec/examples/util/enumerable_spec.rb +64 -0
- data/spec/examples/util/file_spec.rb +40 -0
- data/spec/examples/util/process_queue_spec.rb +16 -0
- data/spec/examples/util/thunk_spec.rb +58 -0
- data/spec/examples/version_spec.rb +0 -0
- data/spec/issues/007_spec.rb +25 -0
- data/spec/issues/008_spec.rb +73 -0
- data/spec/issues/018_spec.rb +25 -0
- data/spec/spec_helper.rb +253 -9
- metadata +136 -93
- data/README.markdown +0 -116
- data/lib/piggly/compiler/cache.rb +0 -151
- data/lib/piggly/compiler/pretty.rb +0 -67
- data/lib/piggly/compiler/queue.rb +0 -46
- data/lib/piggly/compiler/tags.rb +0 -244
- data/lib/piggly/compiler/trace.rb +0 -91
- data/lib/piggly/filecache.rb +0 -40
- data/lib/piggly/parser/parser.rb +0 -11794
- data/lib/piggly/reporter/html.rb +0 -207
- data/spec/compiler/cache_spec.rb +0 -9
- data/spec/compiler/pretty_spec.rb +0 -9
- data/spec/compiler/queue_spec.rb +0 -3
- data/spec/compiler/rewrite_spec.rb +0 -3
- data/spec/config_spec.rb +0 -58
- data/spec/filecache_spec.rb +0 -70
- data/spec/fixtures/snippets.sql +0 -158
- data/spec/grammar/tokens/lval_spec.rb +0 -50
- data/spec/parser_spec.rb +0 -8
- data/spec/profile_spec.rb +0 -5
data/lib/piggly/config.rb
CHANGED
@@ -1,34 +1,19 @@
|
|
1
1
|
module Piggly
|
2
2
|
class Config
|
3
|
+
end
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
self.class.send(:define_method, "#{name}=") do |value|
|
10
|
-
instance_variable_set("@#{name}", value)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
config_accessor :cache_root => File.expand_path(File.join(Dir.pwd, 'piggly', 'cache')),
|
16
|
-
:report_root => File.expand_path(File.join(Dir.pwd, 'piggly', 'reports')),
|
17
|
-
:piggly_root => PIGGLY_ROOT,
|
18
|
-
:trace_prefix => 'PIGGLY',
|
19
|
-
:aggregate => false
|
20
|
-
|
21
|
-
def self.path(root, file=nil)
|
22
|
-
if file
|
5
|
+
class << Config
|
6
|
+
def path(root, file=nil)
|
7
|
+
if file.nil?
|
8
|
+
root
|
9
|
+
else
|
23
10
|
file[%r{^\.\.|^\/|^(?:[A-Z]:)?/}i] ?
|
24
11
|
file : # ../path, /path, or D:\path that isn't relative to root
|
25
12
|
File.join(root, file)
|
26
|
-
else
|
27
|
-
root
|
28
13
|
end
|
29
14
|
end
|
30
15
|
|
31
|
-
def
|
16
|
+
def mkpath(root, file=nil)
|
32
17
|
if file.nil?
|
33
18
|
FileUtils.makedirs(root)
|
34
19
|
root
|
@@ -39,5 +24,45 @@ module Piggly
|
|
39
24
|
end
|
40
25
|
end
|
41
26
|
|
27
|
+
private
|
28
|
+
|
29
|
+
def config_accessor(hash)
|
30
|
+
hash = hash.clone
|
31
|
+
hash.keys.each do |name|
|
32
|
+
define_method(name) do
|
33
|
+
instance_variable_get("@#{name}") || hash[name]
|
34
|
+
end
|
35
|
+
|
36
|
+
define_method("#{name}?") do
|
37
|
+
instance_variable_get("@#{name}") || hash[name]
|
38
|
+
end
|
39
|
+
|
40
|
+
define_method("#{name}=") do |value|
|
41
|
+
instance_variable_set("@#{name}", value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Config
|
48
|
+
config_accessor \
|
49
|
+
:cache_root => File.expand_path("#{Dir.pwd}/piggly/cache"),
|
50
|
+
:report_root => File.expand_path("#{Dir.pwd}/piggly/reports"),
|
51
|
+
:database_yml => nil,
|
52
|
+
:connection_name => "piggly",
|
53
|
+
:trace_prefix => "PIGGLY",
|
54
|
+
:accumulate => false,
|
55
|
+
:dry_run => false,
|
56
|
+
:filters => []
|
57
|
+
|
58
|
+
alias accumulate? accumulate
|
59
|
+
|
60
|
+
def path(*args)
|
61
|
+
self.class.path(*args)
|
62
|
+
end
|
63
|
+
|
64
|
+
def mkpath(*args)
|
65
|
+
self.class.mkpath(*args)
|
66
|
+
end
|
42
67
|
end
|
43
68
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Piggly
|
2
|
+
module Dumper
|
3
|
+
autoload :Index, "piggly/dumper/index"
|
4
|
+
autoload :QualifiedName, "piggly/dumper/qualified_name"
|
5
|
+
autoload :QualifiedType, "piggly/dumper/qualified_type"
|
6
|
+
autoload :ReifiedProcedure, "piggly/dumper/reified_procedure"
|
7
|
+
autoload :SkeletonProcedure, "piggly/dumper/skeleton_procedure"
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Piggly
|
2
|
+
module Dumper
|
3
|
+
|
4
|
+
#
|
5
|
+
# The index file stores metadata about every procedure, but the source
|
6
|
+
# code is stored in a separate file for each procedure.
|
7
|
+
#
|
8
|
+
class Index
|
9
|
+
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
def path
|
16
|
+
@config.mkpath("#{@config.cache_root}/Dumper", "index.yml")
|
17
|
+
end
|
18
|
+
|
19
|
+
# Updates the index with the given list of Procedure values
|
20
|
+
# @return [void]
|
21
|
+
def update(procedures)
|
22
|
+
newest = Util::Enumerable.index_by(procedures){|x| x.identifier }
|
23
|
+
|
24
|
+
removed = index.values.reject{|p| newest.include?(p.identifier) }
|
25
|
+
removed.each{|p| p.purge_source(@config) }
|
26
|
+
|
27
|
+
added = procedures.reject{|p| index.include?(p.identifier) }
|
28
|
+
added.each{|p| p.store_source(@config) }
|
29
|
+
|
30
|
+
changed = procedures.select do |p|
|
31
|
+
if mine = index[p.identifier]
|
32
|
+
# If both are skeletons, they will have the same source because they
|
33
|
+
# are read from the same file, so don't bother checking that case
|
34
|
+
not (mine.skeleton? and p.skeleton?) and
|
35
|
+
mine.source(@config) != p.source(@config)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
changed.each{|p| p.store_source(@config) }
|
39
|
+
|
40
|
+
@index = newest
|
41
|
+
store_index
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns a list of Procedure values from the index
|
45
|
+
def procedures
|
46
|
+
index.values
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the Procedure with the given identifier
|
50
|
+
def [](identifier)
|
51
|
+
p = index[identifier]
|
52
|
+
p.dup if p
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the shortest human-readable label that distinctly identifies
|
56
|
+
# the given procedure from the other procedures in the index
|
57
|
+
def label(procedure)
|
58
|
+
others =
|
59
|
+
procedures.reject{|p| p.oid == procedure.oid }
|
60
|
+
|
61
|
+
same =
|
62
|
+
others.all?{|p| p.name.schema == procedure.name.schema }
|
63
|
+
|
64
|
+
name =
|
65
|
+
if same
|
66
|
+
procedure.name.name
|
67
|
+
else
|
68
|
+
procedure.name.to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
samenames =
|
72
|
+
others.select{|p| p.name == procedure.name }
|
73
|
+
|
74
|
+
if samenames.empty?
|
75
|
+
# Name is unique enough
|
76
|
+
name.to_s
|
77
|
+
else
|
78
|
+
sameargs =
|
79
|
+
samenames.select{|p| p.arg_types == procedure.arg_types }
|
80
|
+
|
81
|
+
if sameargs.empty?
|
82
|
+
# Name and arg types are unique enough
|
83
|
+
"#{name}(#{procedure.arg_types.join(", ")})"
|
84
|
+
else
|
85
|
+
samemodes =
|
86
|
+
sameargs.select{|p| p.arg_modes == procedure.arg_modes }
|
87
|
+
|
88
|
+
if samemodes.empty?
|
89
|
+
# Name, arg types, and arg modes are unique enough
|
90
|
+
"#{name}(#{procedure.arg_modes.zip(procedure.arg_types).map{|a,b| "#{a} #{b}" }.join(", ")})"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def index
|
99
|
+
@index ||= load_index
|
100
|
+
end
|
101
|
+
|
102
|
+
# Load the index from disk
|
103
|
+
def load_index
|
104
|
+
contents =
|
105
|
+
unless File.exists?(path)
|
106
|
+
[]
|
107
|
+
else
|
108
|
+
YAML.load(File.read(path))
|
109
|
+
end
|
110
|
+
|
111
|
+
Util::Enumerable.index_by(contents){|x| x.identifier }
|
112
|
+
end
|
113
|
+
|
114
|
+
# Write the index to disk
|
115
|
+
def store_index
|
116
|
+
File.open(path, "wb"){|io| YAML.dump(procedures.map{|p| p.skeleton }, io) }
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Piggly
|
2
|
+
module Dumper
|
3
|
+
|
4
|
+
class QualifiedName
|
5
|
+
attr_reader :schema, :name
|
6
|
+
|
7
|
+
def initialize(schema, name)
|
8
|
+
@schema, @name = schema, name
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [String]
|
12
|
+
def quote
|
13
|
+
if @schema
|
14
|
+
'"' + @schema + '"."' + @name + '"'
|
15
|
+
else
|
16
|
+
'"' + @name + '"'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String]
|
21
|
+
def to_s
|
22
|
+
if @schema
|
23
|
+
@schema + "." + @name
|
24
|
+
else
|
25
|
+
@name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean]
|
30
|
+
def ==(other)
|
31
|
+
self.to_s == other.to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Piggly
|
2
|
+
module Dumper
|
3
|
+
|
4
|
+
class QualifiedType
|
5
|
+
attr_reader :schema, :name
|
6
|
+
|
7
|
+
def initialize(schema, name)
|
8
|
+
@schema, @name = schema, normalize(name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def shorten
|
12
|
+
self.class.new(nil, @name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def quote
|
16
|
+
if @schema
|
17
|
+
'"' + @schema + '"."' + @name + '"'
|
18
|
+
else
|
19
|
+
'"' + @name + '"'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
if @schema and !%w[public pg_catalog].include?(@schema)
|
25
|
+
@schema + "." + readable(@name)
|
26
|
+
else
|
27
|
+
readable(@name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def ==(other)
|
32
|
+
self.to_s == other.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def normalize(name)
|
38
|
+
# select format_type(ret.oid, null), ret.typname
|
39
|
+
# from pg_type as ret
|
40
|
+
# where ret.typname <> format_type(ret.oid, null)
|
41
|
+
# and ret.typname not like '\\_%'
|
42
|
+
# group by ret.typname, format_type(ret.oid, null)
|
43
|
+
# order by format_type(ret.oid, null);
|
44
|
+
case name
|
45
|
+
when /(.*)\[\]/ then "_#{normalize($1)}"
|
46
|
+
when '"any"' then "any"
|
47
|
+
when "bigint" then "int8"
|
48
|
+
when "bit varying" then "varbit"
|
49
|
+
when "boolean" then "bool"
|
50
|
+
when '"char"' then "char"
|
51
|
+
when "character" then "bpchar"
|
52
|
+
when "character varying" then "varchar"
|
53
|
+
when "double precision" then "float8"
|
54
|
+
when "information_schema\.(.*)" then $1
|
55
|
+
when "integer" then "int4"
|
56
|
+
when "real" then "float4"
|
57
|
+
when "smallint" then "int2"
|
58
|
+
when "timestamp without time zone" then "timestamp"
|
59
|
+
when "timestamp with time zone" then "timestamptz"
|
60
|
+
when "time without time zone" then "time"
|
61
|
+
when "time with time zone" then "timetz"
|
62
|
+
else name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def readable(name)
|
67
|
+
case name
|
68
|
+
when /^_(.*)/ then "#{readable($1)}[]"
|
69
|
+
when "bpchar" then "char"
|
70
|
+
when /^float4(.*)/ then "real#{$1}"
|
71
|
+
when /^int2(.*)/ then "smallint#{$1}"
|
72
|
+
when /^int4(.*)/ then "int#{$1}"
|
73
|
+
when /^int8(.*)/ then "bigint#{$1}"
|
74
|
+
when /^serial4(.*)/ then "serial#{$1}"
|
75
|
+
else name
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Piggly
|
2
|
+
module Dumper
|
3
|
+
|
4
|
+
#
|
5
|
+
# Differs from SkeletonProcedure in that the procedure source code is stored
|
6
|
+
# as an instance variable.
|
7
|
+
#
|
8
|
+
class ReifiedProcedure < SkeletonProcedure
|
9
|
+
|
10
|
+
def initialize(source, *args)
|
11
|
+
super(*args)
|
12
|
+
@source = source.strip
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [String]
|
16
|
+
def source(config)
|
17
|
+
@source
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [void]
|
21
|
+
def store_source(config)
|
22
|
+
if @source.include?("$PIGGLY$")
|
23
|
+
raise "Procedure `#{@name}' is already instrumented. " +
|
24
|
+
"This means the original source wasn't restored after the " +
|
25
|
+
"last coverage run. You must restore the source manually."
|
26
|
+
end
|
27
|
+
|
28
|
+
File.open(source_path(config), "wb"){|io| io.write(@source) }
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [SkeletonProcedure]
|
32
|
+
def skeleton
|
33
|
+
SkeletonProcedure.new(@oid, @name, @strict, @secdef, @setof, @type,
|
34
|
+
@volatility, @arg_modes, @arg_names, @arg_types,
|
35
|
+
@arg_defaults)
|
36
|
+
end
|
37
|
+
|
38
|
+
def skeleton?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class << ReifiedProcedure
|
44
|
+
# Rewrite "i", "o", and "b", otherwise pass-through
|
45
|
+
MODES = Hash.new{|h,k| k }.update \
|
46
|
+
"i" => "in",
|
47
|
+
"o" => "out",
|
48
|
+
"b" => "inout",
|
49
|
+
"v" => "variadic"
|
50
|
+
|
51
|
+
# Rewrite "i", "v", and "s", otherwise pass-through
|
52
|
+
VOLATILITY = Hash.new{|h,k| k }.update \
|
53
|
+
"i" => "immutable",
|
54
|
+
"v" => "volatile",
|
55
|
+
"s" => "stable"
|
56
|
+
|
57
|
+
def mode(mode)
|
58
|
+
MODES[mode]
|
59
|
+
end
|
60
|
+
|
61
|
+
def volatility(mode)
|
62
|
+
VOLATILITY[mode]
|
63
|
+
end
|
64
|
+
|
65
|
+
def defaults(exprs, count, total)
|
66
|
+
exprs = exprs.split(", ")
|
67
|
+
nreqd = total - count
|
68
|
+
|
69
|
+
if nreqd > 0 and exprs.length == count
|
70
|
+
Array.new(nreqd) + exprs
|
71
|
+
else
|
72
|
+
raise "Couldn't parse default arguments"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns a list of all PL/pgSQL stored procedures in the current database
|
77
|
+
#
|
78
|
+
# @return [Array<ReifiedProcedure>]
|
79
|
+
def all(connection)
|
80
|
+
connection.query(<<-SQL).map{|x| from_hash(x) }
|
81
|
+
select
|
82
|
+
pro.oid,
|
83
|
+
nschema.nspname as nschema,
|
84
|
+
pro.proname as name,
|
85
|
+
pro.proisstrict as strict,
|
86
|
+
pro.prosecdef as secdef,
|
87
|
+
pro.provolatile as volatility,
|
88
|
+
pro.proretset as setof,
|
89
|
+
rschema.nspname as tschema,
|
90
|
+
ret.typname as type,
|
91
|
+
pro.prosrc as source,
|
92
|
+
pro.pronargs as arg_count,
|
93
|
+
array_to_string(pro.proargmodes, ',') as arg_modes,
|
94
|
+
array_to_string(pro.proargnames, ',') as arg_names,
|
95
|
+
case when proallargtypes is not null then
|
96
|
+
-- use proalltypes array if its non-null
|
97
|
+
array_to_string(array(select format_type(proallargtypes[k], null)
|
98
|
+
from generate_series(array_lower(proallargtypes, 1),
|
99
|
+
array_upper(proallargtypes, 1)) as k), ',')
|
100
|
+
else
|
101
|
+
-- fallback to oidvector proargtypes
|
102
|
+
oidvectortypes(pro.proargtypes)
|
103
|
+
end as arg_types,
|
104
|
+
pro.pronargdefaults as arg_defaults_count,
|
105
|
+
coalesce(pg_get_expr(pro.proargdefaults, 0), '') as arg_defaults
|
106
|
+
from pg_proc as pro,
|
107
|
+
pg_type as ret,
|
108
|
+
pg_namespace as nschema,
|
109
|
+
pg_namespace as rschema
|
110
|
+
where pro.pronamespace = nschema.oid
|
111
|
+
and ret.typnamespace = rschema.oid
|
112
|
+
and pro.proname not like 'piggly_%'
|
113
|
+
and pro.prorettype = ret.oid
|
114
|
+
and pro.prolang = (select oid from pg_language where lanname = 'plpgsql')
|
115
|
+
and pro.pronamespace not in (select oid
|
116
|
+
from pg_namespace
|
117
|
+
where nspname like 'pg_%'
|
118
|
+
or nspname like 'information_schema')
|
119
|
+
SQL
|
120
|
+
end
|
121
|
+
|
122
|
+
# Construct a ReifiedProcedure from a result row (Hash)
|
123
|
+
#
|
124
|
+
# @return [ReifiedProcedure]
|
125
|
+
def from_hash(hash)
|
126
|
+
new(hash["source"],
|
127
|
+
hash["oid"],
|
128
|
+
QualifiedName.new(hash["nschema"], hash["name"]),
|
129
|
+
hash["strict"] == "t",
|
130
|
+
hash["secdef"] == "t",
|
131
|
+
hash["setof"] == "t",
|
132
|
+
QualifiedType.new(hash["tschema"], hash["type"]),
|
133
|
+
volatility(hash["volatility"]),
|
134
|
+
hash["arg_modes"].to_s.split(",").map{|x| mode(x.strip) },
|
135
|
+
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
|
+
defaults(hash["arg_defaults"], hash["arg_defaults_count"].to_i, hash["arg_count"].to_i))
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|