pg_funcall 0.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 +15 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/.travis.yml +15 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +78 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/Rakefile +10 -0
- data/config/database.yml +37 -0
- data/config/database.yml.example +17 -0
- data/gemfiles/rails40.gemfile +8 -0
- data/gemfiles/rails40.gemfile.lock +79 -0
- data/gemfiles/rails41.gemfile +7 -0
- data/gemfiles/rails41.gemfile.lock +78 -0
- data/gemfiles/rails42.gemfile +7 -0
- data/gemfiles/rails42.gemfile.lock +78 -0
- data/lib/pg_funcall.rb +389 -0
- data/lib/pg_funcall/ipaddr_monkeys.rb +23 -0
- data/lib/pg_funcall/type_info.rb +149 -0
- data/lib/pg_funcall/type_map.rb +198 -0
- data/lib/pg_funcall/version.rb +3 -0
- data/pg_funcall.gemspec +34 -0
- data/script/shell +31 -0
- data/spec/lib/pg_funcall_spec.rb +371 -0
- data/spec/lib/type_info_spec.rb +147 -0
- data/spec/lib/type_map_spec.rb +110 -0
- data/spec/spec_helper.rb +20 -0
- metadata +201 -0
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pg_funcall/type_info'
|
3
|
+
require 'pg_funcall/type_map'
|
4
|
+
|
5
|
+
#
|
6
|
+
# Test the utility class for calling database functions
|
7
|
+
#
|
8
|
+
|
9
|
+
describe PgFuncall::TypeInfo do
|
10
|
+
let :int4row do
|
11
|
+
{
|
12
|
+
"oid"=>"23",
|
13
|
+
"nspname"=>"pg_catalog",
|
14
|
+
"typname"=>"int4",
|
15
|
+
"typnamespace"=>"11",
|
16
|
+
"typowner"=>"10",
|
17
|
+
"typlen"=>"4",
|
18
|
+
"typbyval"=>"t",
|
19
|
+
"typtype"=>"b",
|
20
|
+
"typcategory"=>"N",
|
21
|
+
"typispreferred"=>"f",
|
22
|
+
"typisdefined"=>"t",
|
23
|
+
"typdelim"=>",",
|
24
|
+
"typrelid"=>"0",
|
25
|
+
"typelem"=>"0",
|
26
|
+
"typarray"=>"1007",
|
27
|
+
"typinput"=>"int4in",
|
28
|
+
"typoutput"=>"int4out",
|
29
|
+
"typreceive"=>"int4recv",
|
30
|
+
"typsend"=>"int4send",
|
31
|
+
"typmodin"=>"-",
|
32
|
+
"typmodout"=>"-",
|
33
|
+
"typanalyze"=>"-",
|
34
|
+
"typalign"=>"i",
|
35
|
+
"typstorage"=>"p",
|
36
|
+
"typnotnull"=>"f",
|
37
|
+
"typbasetype"=>"0",
|
38
|
+
"typtypmod"=>"-1",
|
39
|
+
"typndims"=>"0",
|
40
|
+
"typcollation"=>"0",
|
41
|
+
"typdefaultbin"=>nil,
|
42
|
+
"typdefault"=>nil,
|
43
|
+
"typacl"=>nil,
|
44
|
+
"nspowner"=>"10",
|
45
|
+
"nspacl"=>"{robertsanders=UC/robertsanders,=U/robertsanders}"
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
let :pg_connection do
|
50
|
+
ActiveRecord::Base.connection.raw_connection
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Array of string->string hashes representing known Postgres types
|
55
|
+
#
|
56
|
+
let :all_type_rows do
|
57
|
+
pg_connection.query(<<-SQL).to_a
|
58
|
+
SELECT pgt.oid, ns.nspname, *
|
59
|
+
FROM pg_type as pgt
|
60
|
+
JOIN pg_namespace as ns on pgt.typnamespace = ns.oid;
|
61
|
+
SQL
|
62
|
+
end
|
63
|
+
|
64
|
+
let :types_by_oid do
|
65
|
+
{}.tap do |hash|
|
66
|
+
all_type_rows.each do |row|
|
67
|
+
hash[row['oid'].to_i] = row
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
let :types_by_name do
|
73
|
+
{}.tap do |hash|
|
74
|
+
all_type_rows.each do |row|
|
75
|
+
raise "Entry for #{row['oid']} - #{row['typname']} already defined!" if hash.has_key?(row['typname'])
|
76
|
+
hash[row['typname']] = row
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
let :type_map do
|
82
|
+
PgFuncall::TypeMap.fetch(ActiveRecord::Base.connection)
|
83
|
+
end
|
84
|
+
|
85
|
+
let :ar_type do
|
86
|
+
type_map.lookup_ar_by_oid(row['oid'].to_i)
|
87
|
+
end
|
88
|
+
|
89
|
+
# currently inspected row; int4 by default
|
90
|
+
let :row do
|
91
|
+
int4row
|
92
|
+
end
|
93
|
+
|
94
|
+
subject do
|
95
|
+
described_class.new(row, ar_type)
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'for int4 hardcoded row' do
|
99
|
+
it 'should return the correct OID' do
|
100
|
+
subject.oid.should == 23
|
101
|
+
end
|
102
|
+
it 'should return the simple name' do
|
103
|
+
subject.name.should == 'int4'
|
104
|
+
end
|
105
|
+
it 'should return a simple fqname' do
|
106
|
+
subject.fqname.should == 'int4'
|
107
|
+
end
|
108
|
+
it { should be_numeric }
|
109
|
+
it { should_not be_temporal }
|
110
|
+
it { should_not be_array }
|
111
|
+
it 'should have an array type' do
|
112
|
+
subject.array_type_oid.should_not be_nil
|
113
|
+
end
|
114
|
+
it 'should cast a string to integer when leaving DB' do
|
115
|
+
subject.cast_from_database('3211').should == 3211
|
116
|
+
end
|
117
|
+
it 'should cast from an integer to string form entering DB' do
|
118
|
+
subject.cast_to_database(3211).should == '3211'
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'parsed from all_type_rows' do
|
123
|
+
let(:row) { types_by_name['_int4'] }
|
124
|
+
|
125
|
+
it 'should return the correct OID' do
|
126
|
+
subject.oid.should == 1007
|
127
|
+
end
|
128
|
+
it 'should return the simple name' do
|
129
|
+
subject.name.should == '_int4'
|
130
|
+
end
|
131
|
+
it 'should return a simple fqname' do
|
132
|
+
subject.fqname.should == '_int4'
|
133
|
+
end
|
134
|
+
it { should_not be_numeric }
|
135
|
+
it { should_not be_temporal }
|
136
|
+
it { should be_array }
|
137
|
+
it 'should have an element type' do
|
138
|
+
subject.element_type_oid.should == 23
|
139
|
+
end
|
140
|
+
it 'should cast from string form to array of integers' do
|
141
|
+
subject.cast_from_database('{1,1,2,3,5,8}').should == [1,1,2,3,5,8]
|
142
|
+
end
|
143
|
+
it 'should cast from string form to array of integers' do
|
144
|
+
subject.cast_to_database([1,1,2,3,5,8]).should == '{1,1,2,3,5,8}'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pg_funcall/type_map'
|
3
|
+
|
4
|
+
#
|
5
|
+
# Test the utility class for calling database functions
|
6
|
+
#
|
7
|
+
|
8
|
+
describe PgFuncall::TypeMap do
|
9
|
+
subject do
|
10
|
+
PgFuncall::TypeMap.fetch(ActiveRecord::Base.connection)
|
11
|
+
end
|
12
|
+
|
13
|
+
before(:all) do
|
14
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
15
|
+
CREATE OR REPLACE FUNCTION public.dbfspec_textfunc(arg1 text, arg2 text)
|
16
|
+
RETURNS text
|
17
|
+
LANGUAGE plpgsql
|
18
|
+
AS $function$
|
19
|
+
BEGIN
|
20
|
+
RETURN arg1 || ',' || arg2;
|
21
|
+
END;
|
22
|
+
$function$;
|
23
|
+
SQL
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
after(:all) do
|
28
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
29
|
+
DROP FUNCTION IF EXISTS public.dbfspec_textfunc(text, text);
|
30
|
+
SQL
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
context 'creation' do
|
35
|
+
it { should be_a(PgFuncall::TypeMap) }
|
36
|
+
end
|
37
|
+
|
38
|
+
context '#resolve of TypeInfo' do
|
39
|
+
context 'by name' do
|
40
|
+
it 'should return the appropriate type for int4' do
|
41
|
+
subject.resolve('int4').should be_a(PgFuncall::TypeInfo)
|
42
|
+
subject.resolve('int4').name.should == 'int4'
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should contain reference to ar_type for Integer' do
|
46
|
+
subject.resolve('int4').ar_type.should be_a(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::Integer)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'by oid' do
|
51
|
+
it 'should return the appropriate array type for 17 (bytea)' do
|
52
|
+
subject.resolve(17).should be_a(PgFuncall::TypeInfo)
|
53
|
+
subject.resolve(17).name.should == 'bytea'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should return the appropriate array type for 1007 (int4 array)' do
|
57
|
+
typobj = subject.resolve(1007)
|
58
|
+
typobj.should be_array
|
59
|
+
typobj.element_type_oid.should == 23
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should contain reference to ar_type for intarray' do
|
63
|
+
subject.resolve(1007).ar_type.should be_a(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::Array)
|
64
|
+
subject.resolve(1007).ar_type.subtype.should be_a(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::Integer)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'lookup of AR types' do
|
70
|
+
context 'by name' do
|
71
|
+
it 'should return the appropriate type for int4' do
|
72
|
+
subject.lookup_ar_by_name('int4').should be_a(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::Integer)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'by oid' do
|
77
|
+
it 'should return the appropriate array type for 17 (bytea)' do
|
78
|
+
subject.lookup_ar_by_oid(17).should be_a(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::Bytea)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should return the appropriate array type for 1007 (int4 array)' do
|
82
|
+
typobj = subject.lookup_ar_by_oid(1007)
|
83
|
+
typobj.should be_a(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::Array)
|
84
|
+
typobj.subtype.should be_a(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::Integer)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'introspection' do
|
90
|
+
subject { PgFuncall.new(ActiveRecord::Base.connection).type_map }
|
91
|
+
|
92
|
+
context '#function_types' do
|
93
|
+
it 'returns expected types for qualified name' do
|
94
|
+
types = subject.function_types('public.dbfspec_textfunc')
|
95
|
+
types.ret_type.should == 25
|
96
|
+
types.arg_sigs.should include([25, 25])
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'returns same types for unqualified name' do
|
100
|
+
subject.function_types('public.dbfspec_textfunc').
|
101
|
+
should == subject.function_types('dbfspec_textfunc', PgFuncall.default_instance.search_path)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'throws if non-namespaced function is specified without a search_path' do
|
105
|
+
expect { subject.function_types('dbfspec_textfunc', []) }.
|
106
|
+
to raise_error
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
if ENV['COVERAGE'] == 'true'
|
5
|
+
require 'simplecov'
|
6
|
+
SimpleCov.start
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'yaml'
|
10
|
+
require 'active_record'
|
11
|
+
configs = YAML.load(File.read("config/database.yml.example"))
|
12
|
+
ActiveRecord::Base.establish_connection(configs['test'])
|
13
|
+
|
14
|
+
require 'pg_funcall'
|
15
|
+
|
16
|
+
begin
|
17
|
+
require 'pry'
|
18
|
+
rescue LoadError
|
19
|
+
#
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pg_funcall
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Sanders
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pg
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.17.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.17.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 4.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 4.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: uuid
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: ipaddr_extensions
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.7'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '10.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '10.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 2.14.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.14.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: wwtd
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ! '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ! '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description:
|
140
|
+
email:
|
141
|
+
- robert@curioussquid.com
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- .gitignore
|
147
|
+
- .rspec
|
148
|
+
- .travis.yml
|
149
|
+
- Gemfile
|
150
|
+
- Gemfile.lock
|
151
|
+
- LICENSE.txt
|
152
|
+
- README.md
|
153
|
+
- Rakefile
|
154
|
+
- config/database.yml
|
155
|
+
- config/database.yml.example
|
156
|
+
- gemfiles/rails40.gemfile
|
157
|
+
- gemfiles/rails40.gemfile.lock
|
158
|
+
- gemfiles/rails41.gemfile
|
159
|
+
- gemfiles/rails41.gemfile.lock
|
160
|
+
- gemfiles/rails42.gemfile
|
161
|
+
- gemfiles/rails42.gemfile.lock
|
162
|
+
- lib/pg_funcall.rb
|
163
|
+
- lib/pg_funcall/ipaddr_monkeys.rb
|
164
|
+
- lib/pg_funcall/type_info.rb
|
165
|
+
- lib/pg_funcall/type_map.rb
|
166
|
+
- lib/pg_funcall/version.rb
|
167
|
+
- pg_funcall.gemspec
|
168
|
+
- script/shell
|
169
|
+
- spec/lib/pg_funcall_spec.rb
|
170
|
+
- spec/lib/type_info_spec.rb
|
171
|
+
- spec/lib/type_map_spec.rb
|
172
|
+
- spec/spec_helper.rb
|
173
|
+
homepage: http://github.com/rsanders/pg_funcall
|
174
|
+
licenses:
|
175
|
+
- MIT
|
176
|
+
metadata: {}
|
177
|
+
post_install_message:
|
178
|
+
rdoc_options: []
|
179
|
+
require_paths:
|
180
|
+
- lib
|
181
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - ! '>='
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '0'
|
186
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - ! '>='
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '0'
|
191
|
+
requirements: []
|
192
|
+
rubyforge_project:
|
193
|
+
rubygems_version: 2.4.5
|
194
|
+
signing_key:
|
195
|
+
specification_version: 4
|
196
|
+
summary: Utility class for calling functions defined in a PostgreSQL database.
|
197
|
+
test_files:
|
198
|
+
- spec/lib/pg_funcall_spec.rb
|
199
|
+
- spec/lib/type_info_spec.rb
|
200
|
+
- spec/lib/type_map_spec.rb
|
201
|
+
- spec/spec_helper.rb
|