rdbi-driver-postgresql 0.9.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.
data/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT Licence
2
+
3
+ Copyright (c) 2010 Pistos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT Licence
2
+
3
+ Copyright (c) 2010 Pistos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,17 @@
1
+ = rdbi-dbd-postgresql
2
+
3
+ The PostgreSQL database driver for RDBI.
4
+
5
+ == Requirements
6
+
7
+ * Ruby 1.8.7+
8
+ * "pg" gem, version 0.9.0
9
+ * "rdbi" gem
10
+
11
+ == Copyright
12
+
13
+ Copyright (c) 2010 Pistos. See LICENCE for details.
14
+
15
+ == Acknowledgements
16
+
17
+ Thanks to Erik Hollensbe for sharing in-depth knowledge about RDBI and helping debug.
data/Rakefile ADDED
@@ -0,0 +1,62 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rdbi-driver-postgresql"
8
+ gem.summary = %Q{PostgreSQL driver for RDBI}
9
+ gem.description = %Q{PostgreSQL driver for RDBI}
10
+ gem.email = "rdbi@pistos.oib.com"
11
+ gem.homepage = "http://github.com/Pistos/rdbi-dbd-postgresql"
12
+ gem.authors = [ "Pistos", "Erik Hollensbe" ]
13
+
14
+ gem.add_development_dependency 'test-unit'
15
+ gem.add_development_dependency 'rdoc'
16
+ gem.add_development_dependency 'rdbi-dbrc'
17
+
18
+ gem.add_dependency 'rdbi'
19
+ gem.add_dependency 'pg', '= 0.9.0'
20
+ gem.add_dependency 'methlab'
21
+ gem.add_dependency 'epoxy', '>= 0.3.1'
22
+
23
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
24
+ end
25
+ Jeweler::GemcutterTasks.new
26
+ rescue LoadError
27
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
28
+ end
29
+
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'lib' << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+
37
+ begin
38
+ require 'rcov/rcovtask'
39
+ Rcov::RcovTask.new do |test|
40
+ test.libs << 'test'
41
+ test.pattern = 'test/**/test_*.rb'
42
+ test.verbose = true
43
+ end
44
+ rescue LoadError
45
+ task :rcov do
46
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
47
+ end
48
+ end
49
+
50
+ task :test => :check_dependencies
51
+
52
+ task :default => :test
53
+
54
+ require 'rdoc/task'
55
+ RDoc::Task.new do |rdoc|
56
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
57
+
58
+ rdoc.rdoc_dir = 'rdoc'
59
+ rdoc.title = "rdbi-dbd-postgresql #{version}"
60
+ rdoc.rdoc_files.include('README*')
61
+ rdoc.rdoc_files.include('lib/**/*.rb')
62
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.9.0
@@ -0,0 +1,314 @@
1
+ require 'rdbi'
2
+ require 'epoxy'
3
+ require 'methlab'
4
+ gem 'pg', '= 0.9.0'
5
+ require 'pg'
6
+
7
+ class RDBI::Driver::PostgreSQL < RDBI::Driver
8
+ def initialize( *args )
9
+ super( Database, *args )
10
+ end
11
+ end
12
+
13
+ class RDBI::Driver::PostgreSQL < RDBI::Driver
14
+ class Database < RDBI::Database
15
+ extend MethLab
16
+
17
+ attr_accessor :pg_conn
18
+
19
+ def initialize( *args )
20
+ super( *args )
21
+ self.database_name = @connect_args[:dbname] || @connect_args[:database] || @connect_args[:db]
22
+ @pg_conn = PGconn.new(
23
+ @connect_args[:host] || @connect_args[:hostname],
24
+ @connect_args[:port],
25
+ @connect_args[:options],
26
+ @connect_args[:tty],
27
+ self.database_name,
28
+ @connect_args[:user] || @connect_args[:username],
29
+ @connect_args[:password] || @connect_args[:auth]
30
+ )
31
+
32
+ @preprocess_quoter = proc do |x, named, indexed|
33
+ @pg_conn.escape_string((named[x] || indexed[x]).to_s)
34
+ end
35
+ end
36
+
37
+ def disconnect
38
+ @pg_conn.close
39
+ super
40
+ end
41
+
42
+ def transaction( &block )
43
+ if in_transaction?
44
+ raise RDBI::TransactionError.new( "Already in transaction (not supported by PostgreSQL)" )
45
+ end
46
+ execute 'BEGIN'
47
+ super &block
48
+ end
49
+
50
+ def rollback
51
+ if ! in_transaction?
52
+ raise RDBI::TransactionError.new( "Cannot rollback when not in a transaction" )
53
+ end
54
+ execute 'ROLLBACK'
55
+ super
56
+ end
57
+ def commit
58
+ if ! in_transaction?
59
+ raise RDBI::TransactionError.new( "Cannot commit when not in a transaction" )
60
+ end
61
+ execute 'COMMIT'
62
+ super
63
+ end
64
+
65
+ def new_statement( query )
66
+ Statement.new( query, self )
67
+ end
68
+
69
+ def table_schema( table_name, pg_schema = 'public' )
70
+ info_row = execute(
71
+ "SELECT table_type FROM information_schema.tables WHERE table_schema = ? AND table_name = ?",
72
+ pg_schema,
73
+ table_name
74
+ ).fetch( :all )[ 0 ]
75
+ if info_row.nil?
76
+ return nil
77
+ end
78
+
79
+ sch = RDBI::Schema.new( [], [] )
80
+ sch.tables << table_name.to_sym
81
+
82
+ case info_row[ 0 ]
83
+ when 'BASE TABLE'
84
+ sch.type = :table
85
+ when 'VIEW'
86
+ sch.type = :view
87
+ end
88
+
89
+ execute( "SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_schema = ? AND table_name = ?",
90
+ pg_schema,
91
+ table_name
92
+ ).fetch( :all ).each do |row|
93
+ col = RDBI::Column.new
94
+ col.name = row[0].to_sym
95
+ col.type = row[1].to_sym
96
+ # TODO: ensure this ruby_type is solid, especially re: dates and times
97
+ col.ruby_type = row[1].to_sym
98
+ col.nullable = row[2] == "YES"
99
+ sch.columns << col
100
+ end
101
+
102
+ sch
103
+ end
104
+
105
+ def schema( pg_schema = 'public' )
106
+ schemata = []
107
+ execute( "SELECT table_name FROM information_schema.tables WHERE table_schema = '#{pg_schema}';" ).fetch( :all ).each do |row|
108
+ schemata << table_schema( row[0], pg_schema )
109
+ end
110
+ schemata
111
+ end
112
+
113
+ def ping
114
+ start = Time.now
115
+ rows = begin
116
+ execute("SELECT 1").result_count
117
+ rescue PGError => e
118
+ # XXX Sorry this sucks. PGconn is completely useless without a
119
+ # connection... like asking it if it's connected.
120
+ raise RDBI::DisconnectedError.new(e.message)
121
+ end
122
+
123
+ stop = Time.now
124
+
125
+ if rows > 0
126
+ stop.to_i - start.to_i
127
+ else
128
+ raise RDBI::DisconnectedError, "disconnected during ping"
129
+ end
130
+ end
131
+ end
132
+
133
+ class Cursor < RDBI::Cursor
134
+ def initialize(handle, schema)
135
+ super(handle)
136
+ @index = 0
137
+ @stub_datetime = DateTime.now.strftime( " %z" )
138
+ @schema = schema
139
+ end
140
+
141
+ def fetch(count=1)
142
+ return [] if last_row?
143
+ a = []
144
+ count.times { a.push(next_row) }
145
+ return a
146
+ end
147
+
148
+ def next_row
149
+ val = @handle[@index].values
150
+ @index += 1
151
+ fix_dates(val)
152
+ end
153
+
154
+ def result_count
155
+ @handle.num_tuples
156
+ end
157
+
158
+ def affected_count
159
+ @handle.cmd_tuples
160
+ end
161
+
162
+ def first
163
+ fix_dates(@handle[0].values)
164
+ end
165
+
166
+ def last
167
+ fix_dates(@handle[-1].values)
168
+ end
169
+
170
+ def rest
171
+ oindex, @index = @index, result_count-1
172
+ fix_dates(fetch_range(oindex, @index))
173
+ end
174
+
175
+ def all
176
+ fix_dates(fetch_range(0, result_count-1))
177
+ end
178
+
179
+ def [](index)
180
+ fix_dates(@handle[index].values)
181
+ end
182
+
183
+ def last_row?
184
+ @index == result_count
185
+ end
186
+
187
+ def rewind
188
+ @index = 0
189
+ end
190
+
191
+ def empty?
192
+ result_count == 0
193
+ end
194
+
195
+ def finish
196
+ @handle.clear
197
+ end
198
+
199
+ protected
200
+
201
+ def fetch_range(start, stop)
202
+ # XXX when did PGresult get so stupid?
203
+ ary = []
204
+ (start..stop).to_a.each do |i|
205
+ row = []
206
+ @handle.num_fields.times do |j|
207
+ row[ j ] = @handle.getvalue( i, j )
208
+ end
209
+
210
+ ary.push row
211
+ end
212
+ # XXX end stupid rectifier.
213
+
214
+ return ary
215
+ end
216
+
217
+ def fix_dates(values)
218
+ # probably a better way to do this, but at least it's happening on fetch.
219
+ retval = []
220
+ values.each_with_index do |val, x|
221
+ if val.kind_of?(Array)
222
+ newval = []
223
+ val.each_with_index do |col, i|
224
+ if !col.nil? and @schema.columns[i].type == 'timestamp without time zone'
225
+ col << @stub_datetime
226
+ end
227
+
228
+ newval.push(col)
229
+ end
230
+ retval.push(newval)
231
+ else
232
+ if !val.nil? and @schema.columns[x].type == 'timestamp without time zone'
233
+ val << @stub_datetime
234
+ end
235
+ retval.push(val)
236
+ end
237
+ end
238
+ return retval
239
+ end
240
+ end
241
+
242
+ class Statement < RDBI::Statement
243
+ extend MethLab
244
+
245
+ attr_accessor :pg_result
246
+ attr_threaded_accessor :stmt_name
247
+
248
+ def initialize( query, dbh )
249
+ super( query, dbh )
250
+ @stmt_name = Time.now.to_f.to_s
251
+
252
+ ep = Epoxy.new( query )
253
+ @index_map = ep.indexed_binds
254
+ query = ep.quote(@index_map.compact.inject({}) { |x,y| x.merge({ y => nil }) }) do |x|
255
+ case x
256
+ when Integer
257
+ "$#{x+1}"
258
+ when Symbol
259
+ num = @index_map.index(x)
260
+ "$#{num+1}"
261
+ end
262
+ end
263
+
264
+ @pg_result = dbh.pg_conn.prepare(
265
+ @stmt_name,
266
+ query
267
+ )
268
+
269
+ # @input_type_map initialized in superclass
270
+ @output_type_map = RDBI::Type.create_type_hash( RDBI::Type::Out )
271
+ @output_type_map[ :bigint ] = RDBI::Type.filterlist( RDBI::Type::Filters::STR_TO_INT )
272
+ end
273
+
274
+ # Returns an Array of things used to fill out the parameters to RDBI::Result.new
275
+ def new_execution( *binds )
276
+ # FIXME move to RDBI::Util or something.
277
+ hashes, binds = binds.partition { |x| x.kind_of?(Hash) }
278
+ hash = hashes.inject({}) { |x, y| x.merge(y) }
279
+ hash.keys.each do |key|
280
+ if index = @index_map.index(key)
281
+ binds.insert(index, hash[key])
282
+ end
283
+ end
284
+
285
+ pg_result = @dbh.pg_conn.exec_prepared( @stmt_name, binds )
286
+
287
+ columns = []
288
+ stub_datetime = DateTime.now.strftime( " %z" )
289
+ (0...pg_result.num_fields).each do |i|
290
+ c = RDBI::Column.new
291
+ c.name = pg_result.fname( i ).to_sym
292
+ c.type = @dbh.pg_conn.exec(
293
+ "SELECT format_type( #{ pg_result.ftype(i) }, #{ pg_result.fmod(i) } )"
294
+ )[ 0 ].values[ 0 ]
295
+ if c.type.start_with? 'timestamp'
296
+ c.ruby_type = 'timestamp'.to_sym
297
+ else
298
+ c.ruby_type = c.type.to_sym
299
+ end
300
+ columns << c
301
+ end
302
+
303
+ this_schema = RDBI::Schema.new
304
+ this_schema.columns = columns
305
+
306
+ [ Cursor.new(pg_result, this_schema), this_schema, @output_type_map ]
307
+ end
308
+
309
+ def finish
310
+ @pg_result.clear
311
+ super
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rdbi-driver-postgresql}
8
+ s.version = "0.9.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Pistos", "Erik Hollensbe"]
12
+ s.date = %q{2010-08-21}
13
+ s.description = %q{PostgreSQL driver for RDBI}
14
+ s.email = %q{rdbi@pistos.oib.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ "LICENCE",
21
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/rdbi/driver/postgresql.rb",
26
+ "rdbi-driver-postgresql.gemspec",
27
+ "test/helper.rb",
28
+ "test/test_database.rb"
29
+ ]
30
+ s.homepage = %q{http://github.com/Pistos/rdbi-dbd-postgresql}
31
+ s.rdoc_options = ["--charset=UTF-8"]
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = %q{1.3.7}
34
+ s.summary = %q{PostgreSQL driver for RDBI}
35
+ s.test_files = [
36
+ "test/helper.rb",
37
+ "test/test_database.rb"
38
+ ]
39
+
40
+ if s.respond_to? :specification_version then
41
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_development_dependency(%q<test-unit>, [">= 0"])
46
+ s.add_development_dependency(%q<rdoc>, [">= 0"])
47
+ s.add_development_dependency(%q<rdbi-dbrc>, [">= 0"])
48
+ s.add_runtime_dependency(%q<rdbi>, [">= 0"])
49
+ s.add_runtime_dependency(%q<pg>, ["= 0.9.0"])
50
+ s.add_runtime_dependency(%q<methlab>, [">= 0"])
51
+ s.add_runtime_dependency(%q<epoxy>, [">= 0.3.1"])
52
+ else
53
+ s.add_dependency(%q<test-unit>, [">= 0"])
54
+ s.add_dependency(%q<rdoc>, [">= 0"])
55
+ s.add_dependency(%q<rdbi-dbrc>, [">= 0"])
56
+ s.add_dependency(%q<rdbi>, [">= 0"])
57
+ s.add_dependency(%q<pg>, ["= 0.9.0"])
58
+ s.add_dependency(%q<methlab>, [">= 0"])
59
+ s.add_dependency(%q<epoxy>, [">= 0.3.1"])
60
+ end
61
+ else
62
+ s.add_dependency(%q<test-unit>, [">= 0"])
63
+ s.add_dependency(%q<rdoc>, [">= 0"])
64
+ s.add_dependency(%q<rdbi-dbrc>, [">= 0"])
65
+ s.add_dependency(%q<rdbi>, [">= 0"])
66
+ s.add_dependency(%q<pg>, ["= 0.9.0"])
67
+ s.add_dependency(%q<methlab>, [">= 0"])
68
+ s.add_dependency(%q<epoxy>, [">= 0.3.1"])
69
+ end
70
+ end
71
+
data/test/helper.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ gem 'test-unit'
3
+ require 'test/unit'
4
+ require 'fileutils'
5
+
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
8
+ gem 'rdbi-dbrc'
9
+ require 'rdbi-dbrc'
10
+ require 'rdbi/driver/postgresql'
11
+
12
+ class Test::Unit::TestCase
13
+
14
+ SQL = [
15
+ 'DROP TABLE IF EXISTS foo',
16
+ 'DROP TABLE IF EXISTS bar',
17
+ 'DROP TABLE IF EXISTS time_test',
18
+ 'DROP TABLE IF EXISTS ordinals',
19
+ 'create table foo (bar integer)',
20
+ 'create table bar (foo varchar, bar integer)',
21
+ 'create table time_test (my_date timestamp)',
22
+ 'CREATE TABLE ordinals ( id SERIAL PRIMARY KEY, cardinal INTEGER, s VARCHAR )',
23
+ "INSERT INTO ordinals ( cardinal, s ) VALUES ( 1, 'first' )",
24
+ "INSERT INTO ordinals ( cardinal, s ) VALUES ( 2, 'second' )",
25
+ "INSERT INTO ordinals ( cardinal, s ) VALUES ( 3, 'third' )",
26
+ ]
27
+
28
+ def new_database
29
+ RDBI::DBRC.connect(:postgresql_test)
30
+ end
31
+
32
+ def init_database
33
+ dbh = new_database
34
+ SQL.each { |query| dbh.execute(query) }
35
+ return dbh
36
+ end
37
+
38
+ def role
39
+ RDBI::DBRC.roles[:postgresql_test]
40
+ end
41
+ end
@@ -0,0 +1,316 @@
1
+ require 'helper'
2
+
3
+ class TestDatabase < Test::Unit::TestCase
4
+
5
+ attr_accessor :dbh
6
+
7
+ def teardown
8
+ @dbh.disconnect if @dbh && @dbh.connected?
9
+ end
10
+
11
+ def test_01_connect
12
+ self.dbh = new_database
13
+ assert dbh
14
+ assert_kind_of( RDBI::Driver::PostgreSQL::Database, dbh )
15
+ assert_kind_of( RDBI::Database, dbh )
16
+ assert_equal( dbh.database_name, role[:database] )
17
+ dbh.disconnect
18
+ assert ! dbh.connected?
19
+ end
20
+
21
+ def test_02_ping
22
+ self.dbh = init_database
23
+
24
+ my_role = role.dup
25
+ driver = my_role.delete(:driver)
26
+
27
+ assert_kind_of(Numeric, dbh.ping)
28
+ assert_kind_of(Numeric, RDBI.ping(driver, my_role))
29
+ dbh.disconnect
30
+
31
+ assert_raises(RDBI::DisconnectedError.new("not connected")) do
32
+ dbh.ping
33
+ end
34
+
35
+ # XXX This should still work because it connects. Obviously, testing a
36
+ # downed database is gonna be pretty hard.
37
+ assert_kind_of(Numeric, RDBI.ping(driver, my_role))
38
+ end
39
+
40
+ def test_03_execute
41
+ self.dbh = init_database
42
+ res = dbh.execute( "insert into foo (bar) values (?)", 1 )
43
+ assert res
44
+ assert_kind_of( RDBI::Result, res )
45
+ assert_equal( 1, res.affected_count )
46
+
47
+ res = dbh.execute( "select * from foo" )
48
+ assert res
49
+ assert_kind_of( RDBI::Result, res )
50
+ assert_equal( [[1]], res.fetch(:all) )
51
+
52
+ rows = res.as( :Struct ).fetch( :all )
53
+ row = rows[ 0 ]
54
+ assert_equal( 1, row.bar )
55
+
56
+ res = dbh.execute( "select count(*) from foo" )
57
+ assert res
58
+ assert_kind_of( RDBI::Result, res )
59
+ assert_equal( [[1]], res.fetch(:all) )
60
+ row = res.as( :Array ).fetch( :first )
61
+ assert_equal 1, row[ 0 ]
62
+
63
+ res = dbh.execute( "SELECT 5" )
64
+ assert res
65
+ assert_kind_of( RDBI::Result, res )
66
+ row = res.as( :Array ).fetch( :first )
67
+ assert_equal 5, row[ 0 ]
68
+
69
+ time_str = DateTime.now.strftime( "%Y-%m-%d %H:%M:%S %z" )
70
+ res = dbh.execute( "SELECT 5, TRUE, 'hello', '#{time_str}'::TIMESTAMP" )
71
+ assert res
72
+ assert_kind_of( RDBI::Result, res )
73
+ row = res.fetch( :all )[ 0 ]
74
+ assert_equal 5, row[ 0 ]
75
+ assert_equal true, row[ 1 ]
76
+ assert_equal 'hello', row[ 2 ]
77
+ assert_equal DateTime.parse( time_str ), row[ 3 ]
78
+ end
79
+
80
+ def test_04_prepare
81
+ self.dbh = init_database
82
+
83
+ sth = dbh.prepare( "insert into foo (bar) values (?)" )
84
+ assert sth
85
+ assert_kind_of( RDBI::Statement, sth )
86
+ assert_respond_to( sth, :execute )
87
+
88
+ 5.times do
89
+ res = sth.execute( 1 )
90
+ assert_equal( 1, res.affected_count )
91
+ end
92
+
93
+ assert_equal( dbh.last_statement.object_id, sth.object_id )
94
+
95
+ sth2 = dbh.prepare( "select * from foo" )
96
+ assert sth
97
+ assert_kind_of( RDBI::Statement, sth )
98
+ assert_respond_to( sth, :execute )
99
+
100
+ res = sth2.execute
101
+ assert res
102
+ assert_kind_of( RDBI::Result, res )
103
+ assert_equal( [[1]] * 5, res.fetch(:all) )
104
+
105
+ sth.execute 1
106
+
107
+ res = sth2.execute
108
+ assert res
109
+ assert_kind_of( RDBI::Result, res )
110
+ assert_equal( [[1]] * 6, res.fetch(:all) )
111
+
112
+ sth.finish
113
+ sth2.finish
114
+ end
115
+
116
+ def test_05_transaction
117
+ self.dbh = init_database
118
+
119
+ dbh.transaction do
120
+ assert dbh.in_transaction?
121
+ 5.times { dbh.execute( "insert into foo (bar) values (?)", 1 ) }
122
+ dbh.rollback
123
+ assert ! dbh.in_transaction?
124
+ end
125
+
126
+ assert ! dbh.in_transaction?
127
+
128
+ assert_equal( [], dbh.execute("select * from foo").fetch(:all) )
129
+
130
+ dbh.transaction do
131
+ assert dbh.in_transaction?
132
+ 5.times { dbh.execute("insert into foo (bar) values (?)", 1) }
133
+ assert_equal( [[1]] * 5, dbh.execute("select * from foo").fetch(:all) )
134
+ dbh.commit
135
+ assert ! dbh.in_transaction?
136
+ end
137
+
138
+ assert ! dbh.in_transaction?
139
+
140
+ assert_equal( [[1]] * 5, dbh.execute("select * from foo").fetch(:all) )
141
+
142
+ dbh.transaction do
143
+ assert dbh.in_transaction?
144
+ assert_raises( RDBI::TransactionError ) do
145
+ dbh.transaction do
146
+ end
147
+ end
148
+ end
149
+
150
+ # Not in a transaction
151
+
152
+ assert_raises( RDBI::TransactionError ) do
153
+ dbh.rollback
154
+ end
155
+
156
+ assert_raises( RDBI::TransactionError ) do
157
+ dbh.commit
158
+ end
159
+ end
160
+
161
+ def test_06_preprocess_query
162
+ self.dbh = init_database
163
+ assert_equal(
164
+ "insert into foo (bar) values (1)",
165
+ dbh.preprocess_query( "insert into foo (bar) values (?)", 1 )
166
+ )
167
+ end
168
+
169
+ def test_07_schema
170
+ self.dbh = init_database
171
+
172
+ dbh.execute( "insert into bar (foo, bar) values (?, ?)", "foo", 1 )
173
+ res = dbh.execute( "select * from bar" )
174
+
175
+ assert res
176
+ assert res.schema
177
+ assert_kind_of( RDBI::Schema, res.schema )
178
+ assert res.schema.columns
179
+ res.schema.columns.each { |x| assert_kind_of(RDBI::Column, x) }
180
+ end
181
+
182
+ def test_08_datetime
183
+ self.dbh = init_database
184
+
185
+ dt = DateTime.now
186
+ dbh.execute( 'insert into time_test (my_date) values (?)', dt )
187
+ dt2 = dbh.execute( 'select * from time_test limit 1' ).fetch(1)[0][0]
188
+
189
+ assert_kind_of( DateTime, dt2 )
190
+ assert_equal( dt2.to_s, dt.to_s )
191
+
192
+ dbh.execute 'INSERT INTO time_test ( my_date ) VALUES ( NULL )'
193
+ dt3 = "not nil"
194
+ assert_nothing_raised do
195
+ dt3 = dbh.execute( 'SELECT * FROM time_test WHERE my_date IS NULL LIMIT 1' ).fetch( 1 )[0][0]
196
+ end
197
+ assert_nil dt3
198
+ end
199
+
200
+ def test_09_basic_schema
201
+ self.dbh = init_database
202
+ assert_respond_to( dbh, :schema )
203
+ schema = dbh.schema.sort_by { |x| x.tables[0].to_s }
204
+
205
+ tables = [ :bar, :foo, :ordinals, :time_test ]
206
+ columns = {
207
+ :bar => { :foo => 'character varying'.to_sym, :bar => :integer },
208
+ :foo => { :bar => :integer },
209
+ :time_test => { :my_date => 'timestamp without time zone'.to_sym },
210
+ :ordinals => {
211
+ :id => :integer,
212
+ :cardinal => :integer,
213
+ :s => 'character varying'.to_sym,
214
+ },
215
+ }
216
+
217
+ schema.each_with_index do |sch, x|
218
+ assert_kind_of( RDBI::Schema, sch )
219
+ assert_equal( sch.tables[0], tables[x] )
220
+
221
+ sch.columns.each do |col|
222
+ assert_kind_of( RDBI::Column, col )
223
+ assert_equal( columns[ tables[x] ][ col.name ], col.type )
224
+ end
225
+ end
226
+
227
+ result = dbh.execute( "SELECT id, cardinal FROM ordinals ORDER BY id" )
228
+ rows = result.fetch( :all, RDBI::Result::Driver::Array )
229
+ assert_kind_of( Fixnum, rows[ 0 ][ 0 ] )
230
+ assert_kind_of( Fixnum, rows[ 0 ][ 1 ] )
231
+ rows = result.fetch( :all, RDBI::Result::Driver::Struct )
232
+ assert_kind_of( Fixnum, rows[ 0 ][ :id ] )
233
+ assert_kind_of( Fixnum, rows[ 0 ][ :cardinal ] )
234
+ end
235
+
236
+ def test_10_table_schema
237
+ self.dbh = init_database
238
+ assert_respond_to( dbh, :table_schema )
239
+
240
+ schema = dbh.table_schema( :foo )
241
+ columns = schema.columns
242
+ assert_equal columns.size, 1
243
+ c = columns[ 0 ]
244
+ assert_equal c.name, :bar
245
+ assert_equal c.type, :integer
246
+
247
+ schema = dbh.table_schema( :bar )
248
+ columns = schema.columns
249
+ assert_equal columns.size, 2
250
+ columns.each do |c|
251
+ case c.name
252
+ when :foo
253
+ assert_equal c.type, 'character varying'.to_sym
254
+ when :bar
255
+ assert_equal c.type, :integer
256
+ end
257
+ end
258
+
259
+ assert_nil dbh.table_schema( :non_existent )
260
+ end
261
+
262
+ def test_11_named_binds
263
+ self.dbh = init_database
264
+
265
+ res = dbh.execute("select id, cardinal, s from ordinals where id = ?id", { :id => 1 })
266
+ assert(res)
267
+ assert_equal([[1, 1, 'first']], res.fetch(:all))
268
+
269
+ res = dbh.execute("select id, cardinal, s from ordinals where id = ?id and cardinal = ?", { :id => 1 }, 1)
270
+ assert(res)
271
+ assert_equal([[1, 1, 'first']], res.fetch(:all))
272
+
273
+ res = dbh.execute("select id, cardinal, s from ordinals where id = ?id and cardinal = ?", 1, { :id => 1 })
274
+ assert(res)
275
+ assert_equal([[1, 1, 'first']], res.fetch(:all))
276
+ end
277
+
278
+ def test_12_struct_select
279
+ self.dbh = init_database
280
+
281
+ results = dbh.execute("select id, cardinal, s from ordinals order by id").as(:Struct).fetch(:all)
282
+
283
+ assert(results)
284
+ assert_kind_of(Array, results)
285
+ assert_equal(results[0].id, 1)
286
+ assert_equal(results[0].cardinal, 1)
287
+ assert_equal(results[0].s, 'first')
288
+ assert_equal(results[1].id, 2)
289
+ assert_equal(results[1].cardinal, 2)
290
+ assert_equal(results[1].s, 'second')
291
+ assert_equal(results[2].id, 3)
292
+ assert_equal(results[2].cardinal, 3)
293
+ assert_equal(results[2].s, 'third')
294
+
295
+ results = dbh.execute("select * from ordinals order by id").as(:Struct).fetch(:all)
296
+
297
+ assert(results)
298
+ assert_kind_of(Array, results)
299
+ assert_equal(results[0].id, 1)
300
+ assert_equal(results[0].cardinal, 1)
301
+ assert_equal(results[0].s, 'first')
302
+ assert_equal(results[1].id, 2)
303
+ assert_equal(results[1].cardinal, 2)
304
+ assert_equal(results[1].s, 'second')
305
+ assert_equal(results[2].id, 3)
306
+ assert_equal(results[2].cardinal, 3)
307
+ assert_equal(results[2].s, 'third')
308
+
309
+ result = dbh.execute("select * from ordinals order by id").as(:Struct).fetch(:first)
310
+ assert(result)
311
+ assert_kind_of(Struct, result)
312
+ assert_equal(result.id, 1)
313
+ assert_equal(result.cardinal, 1)
314
+ assert_equal(result.s, 'first')
315
+ end
316
+ end
metadata ADDED
@@ -0,0 +1,179 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rdbi-driver-postgresql
3
+ version: !ruby/object:Gem::Version
4
+ hash: 59
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 0
10
+ version: 0.9.0
11
+ platform: ruby
12
+ authors:
13
+ - Pistos
14
+ - Erik Hollensbe
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-08-21 00:00:00 -04:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: test-unit
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 3
31
+ segments:
32
+ - 0
33
+ version: "0"
34
+ type: :development
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rdoc
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rdbi-dbrc
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ type: :development
63
+ version_requirements: *id003
64
+ - !ruby/object:Gem::Dependency
65
+ name: rdbi
66
+ prerelease: false
67
+ requirement: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ type: :runtime
77
+ version_requirements: *id004
78
+ - !ruby/object:Gem::Dependency
79
+ name: pg
80
+ prerelease: false
81
+ requirement: &id005 !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - "="
85
+ - !ruby/object:Gem::Version
86
+ hash: 59
87
+ segments:
88
+ - 0
89
+ - 9
90
+ - 0
91
+ version: 0.9.0
92
+ type: :runtime
93
+ version_requirements: *id005
94
+ - !ruby/object:Gem::Dependency
95
+ name: methlab
96
+ prerelease: false
97
+ requirement: &id006 !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
105
+ version: "0"
106
+ type: :runtime
107
+ version_requirements: *id006
108
+ - !ruby/object:Gem::Dependency
109
+ name: epoxy
110
+ prerelease: false
111
+ requirement: &id007 !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ hash: 17
117
+ segments:
118
+ - 0
119
+ - 3
120
+ - 1
121
+ version: 0.3.1
122
+ type: :runtime
123
+ version_requirements: *id007
124
+ description: PostgreSQL driver for RDBI
125
+ email: rdbi@pistos.oib.com
126
+ executables: []
127
+
128
+ extensions: []
129
+
130
+ extra_rdoc_files:
131
+ - LICENSE
132
+ - README.rdoc
133
+ files:
134
+ - LICENCE
135
+ - LICENSE
136
+ - README.rdoc
137
+ - Rakefile
138
+ - VERSION
139
+ - lib/rdbi/driver/postgresql.rb
140
+ - rdbi-driver-postgresql.gemspec
141
+ - test/helper.rb
142
+ - test/test_database.rb
143
+ has_rdoc: true
144
+ homepage: http://github.com/Pistos/rdbi-dbd-postgresql
145
+ licenses: []
146
+
147
+ post_install_message:
148
+ rdoc_options:
149
+ - --charset=UTF-8
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ hash: 3
158
+ segments:
159
+ - 0
160
+ version: "0"
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ none: false
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ hash: 3
167
+ segments:
168
+ - 0
169
+ version: "0"
170
+ requirements: []
171
+
172
+ rubyforge_project:
173
+ rubygems_version: 1.3.7
174
+ signing_key:
175
+ specification_version: 3
176
+ summary: PostgreSQL driver for RDBI
177
+ test_files:
178
+ - test/helper.rb
179
+ - test/test_database.rb