dbd-pg 0.3.3
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/ChangeLog +3694 -0
- data/LICENSE +25 -0
- data/README +271 -0
- data/lib/dbd/Pg.rb +186 -0
- data/lib/dbd/pg/database.rb +510 -0
- data/lib/dbd/pg/exec.rb +47 -0
- data/lib/dbd/pg/statement.rb +155 -0
- data/lib/dbd/pg/tuples.rb +120 -0
- data/lib/dbd/pg/type.rb +209 -0
- data/test/DBD_TESTS +48 -0
- data/test/dbd/general/test_database.rb +157 -0
- data/test/dbd/general/test_statement.rb +240 -0
- data/test/dbd/general/test_types.rb +253 -0
- data/test/dbd/postgresql/base.rb +32 -0
- data/test/dbd/postgresql/down.sql +25 -0
- data/test/dbd/postgresql/test_arrays.rb +179 -0
- data/test/dbd/postgresql/test_async.rb +121 -0
- data/test/dbd/postgresql/test_blob.rb +36 -0
- data/test/dbd/postgresql/test_bytea.rb +87 -0
- data/test/dbd/postgresql/test_ping.rb +10 -0
- data/test/dbd/postgresql/test_timestamp.rb +77 -0
- data/test/dbd/postgresql/test_transactions.rb +58 -0
- data/test/dbd/postgresql/testdbipg.rb +254 -0
- data/test/dbd/postgresql/up.sql +54 -0
- data/test/ts_dbd.rb +118 -0
- metadata +98 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
class TestPostgresPing < DBDConfig.testbase(:postgresql)
|
2
|
+
def test_ping
|
3
|
+
config = DBDConfig.get_config['postgresql']
|
4
|
+
dbh = DBI.connect("dbi:Pg:#{config['dbname']}", config['username'], config['password'])
|
5
|
+
assert dbh
|
6
|
+
assert dbh.ping
|
7
|
+
dbh.disconnect
|
8
|
+
assert_raise(DBI::InterfaceError) { dbh.ping }
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
class TestPostgresTimestamp < DBDConfig.testbase(:postgresql)
|
2
|
+
def get_tstamp
|
3
|
+
DateTime.parse("2008-03-08 10:39:01.012300")
|
4
|
+
end
|
5
|
+
|
6
|
+
# XXX DateTime is not allowed to modify the sec_fraction component, so this test is moot.
|
7
|
+
# perhaps we need to fix DateTime.
|
8
|
+
def skip_test_timestamp_altered_fraction
|
9
|
+
ts = nil
|
10
|
+
|
11
|
+
assert_nothing_raised do
|
12
|
+
@sth = @dbh.prepare("insert into timestamp_test (mytimestamp) values (?)")
|
13
|
+
ts = DateTime.parse(Time.now.to_s)
|
14
|
+
ts.sec_fraction = 22200000
|
15
|
+
@sth.execute(ts)
|
16
|
+
@sth.finish
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_nothing_raised do
|
20
|
+
@sth = @dbh.prepare("select * from timestamp_test")
|
21
|
+
@sth.execute
|
22
|
+
row = @sth.fetch
|
23
|
+
@sth.finish
|
24
|
+
assert_equal ts.sec_fraction, row[0].sec_fraction
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_current_timestamp
|
29
|
+
assert @dbh
|
30
|
+
# syntax db-specific (e.g., "from dual", "...timestamp()", etc.)
|
31
|
+
ts = @dbh.select_one("SELECT CURRENT_TIMESTAMP")[0]
|
32
|
+
assert_kind_of DateTime, ts
|
33
|
+
assert_not_nil ts.sec_fraction
|
34
|
+
end
|
35
|
+
|
36
|
+
# Just like the 'general' test, but checking for fractional seconds
|
37
|
+
def test_timestamp_fractional
|
38
|
+
assert @dbh
|
39
|
+
@sth = nil
|
40
|
+
t = get_tstamp
|
41
|
+
assert_nothing_raised do
|
42
|
+
@sth = @dbh.prepare("insert into timestamp_test (mytimestamp) values (?)")
|
43
|
+
@sth.execute(t)
|
44
|
+
@sth.finish
|
45
|
+
end
|
46
|
+
|
47
|
+
assert_nothing_raised do
|
48
|
+
@sth = @dbh.prepare("select * from timestamp_test")
|
49
|
+
@sth.execute
|
50
|
+
row = @sth.fetch
|
51
|
+
assert_kind_of DateTime, row[0]
|
52
|
+
assert_equal t.year, row[0].year
|
53
|
+
assert_equal t.month, row[0].month
|
54
|
+
assert_equal t.day, row[0].day
|
55
|
+
assert_equal t.hour, row[0].hour
|
56
|
+
assert_equal t.min, row[0].min
|
57
|
+
assert_equal t.sec, row[0].sec
|
58
|
+
assert_not_nil row[0].sec_fraction
|
59
|
+
assert_equal t.sec_fraction, row[0].sec_fraction
|
60
|
+
@sth.finish
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Is our DBI::Timestamp equivalent to its canonical string literal
|
65
|
+
# form cast appropriately?
|
66
|
+
def test_timestamp_from_cast
|
67
|
+
assert @dbh
|
68
|
+
sql_ts = "SELECT CAST('2008-03-08 10:39:01.012300' AS TIMESTAMP)"
|
69
|
+
|
70
|
+
row = @dbh.select_one(sql_ts)
|
71
|
+
assert_not_nil row
|
72
|
+
assert_equal 1, row.size
|
73
|
+
|
74
|
+
assert_kind_of DateTime, row[0]
|
75
|
+
assert_equal row[0], get_tstamp
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class TestPostgresTransaction < DBDConfig.testbase(:postgresql)
|
2
|
+
def test_rollback
|
3
|
+
dbh = get_dbh
|
4
|
+
dbh["AutoCommit"] = false
|
5
|
+
@sth = dbh.prepare('insert into names (name, age) values (?, ?)')
|
6
|
+
@sth.execute("Foo", 51)
|
7
|
+
dbh.rollback
|
8
|
+
assert_equal 1, @sth.rows
|
9
|
+
@sth.finish
|
10
|
+
|
11
|
+
|
12
|
+
@sth = dbh.prepare('select name, age from names where name=?')
|
13
|
+
@sth.execute("Foo")
|
14
|
+
assert !@sth.fetch
|
15
|
+
@sth.finish
|
16
|
+
dbh.disconnect
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_commit
|
20
|
+
dbh = get_dbh
|
21
|
+
dbh["AutoCommit"] = false
|
22
|
+
@sth = dbh.prepare('insert into names (name, age) values (?, ?)')
|
23
|
+
@sth.execute("Foo", 51)
|
24
|
+
dbh.commit
|
25
|
+
assert_equal 1, @sth.rows
|
26
|
+
@sth.finish
|
27
|
+
|
28
|
+
@sth = dbh.prepare('select name, age from names where name=?')
|
29
|
+
@sth.execute("Foo")
|
30
|
+
row = @sth.fetch
|
31
|
+
assert row
|
32
|
+
assert_equal "Foo", row[0]
|
33
|
+
assert_equal 51, row[1]
|
34
|
+
@sth.finish
|
35
|
+
dbh.disconnect
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_select_transaction
|
39
|
+
# per bug #10466
|
40
|
+
dbh = get_dbh
|
41
|
+
dbh["AutoCommit"] = false
|
42
|
+
@sth = dbh.prepare('select * from test_insert(?, ?)');
|
43
|
+
@sth.execute("Foo", 51)
|
44
|
+
dbh.rollback
|
45
|
+
@sth.finish
|
46
|
+
|
47
|
+
@sth = dbh.prepare('select name, age from names where name=?')
|
48
|
+
@sth.execute("Foo")
|
49
|
+
assert !@sth.fetch
|
50
|
+
@sth.finish
|
51
|
+
dbh.disconnect
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_dbh
|
55
|
+
config = DBDConfig.get_config
|
56
|
+
DBI.connect("dbi:Pg:#{config['postgresql']['dbname']}", config['postgresql']['username'], config['postgresql']['password'])
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
require 'dbd/Pg'
|
2
|
+
|
3
|
+
######################################################################
|
4
|
+
# Test the PostgreSql DBD driver. This test exercises options
|
5
|
+
# difficult to test through the standard DBI interface.
|
6
|
+
#
|
7
|
+
class TestDbdPostgres < DBDConfig.testbase(:postgresql)
|
8
|
+
|
9
|
+
# FIXME this is a feature that should be there, but currently isn't.
|
10
|
+
# def test_connect
|
11
|
+
# dbd = get_dbd
|
12
|
+
# assert_not_nil dbd.connection
|
13
|
+
# assert_equal 'localhost', dbd.connection.host
|
14
|
+
# assert_equal 'erikh', dbd.connection.user
|
15
|
+
# assert_equal 'rubytest', dbd.connection.db
|
16
|
+
# assert_equal 5432, dbd.connection.port
|
17
|
+
# ensure
|
18
|
+
# dbd.disconnect if dbd
|
19
|
+
# end
|
20
|
+
|
21
|
+
def test_binding
|
22
|
+
assert(@dbh["pg_native_binding"])
|
23
|
+
|
24
|
+
assert_raise(DBI::ProgrammingError) do
|
25
|
+
@sth = @dbh.prepare("select * from names where age IS NOT ?")
|
26
|
+
@sth.execute("NULL")
|
27
|
+
@sth.finish
|
28
|
+
end
|
29
|
+
|
30
|
+
assert_nothing_raised do
|
31
|
+
@dbh["pg_native_binding"] = false
|
32
|
+
@sth = @dbh.prepare("select * from names where age IS NOT ? order by age")
|
33
|
+
@sth.execute("NULL")
|
34
|
+
assert_equal(
|
35
|
+
[
|
36
|
+
["Joe", 19],
|
37
|
+
["Bob", 21],
|
38
|
+
["Jim", 30],
|
39
|
+
],
|
40
|
+
@sth.fetch_all
|
41
|
+
)
|
42
|
+
|
43
|
+
@sth.finish
|
44
|
+
|
45
|
+
@sth = @dbh.prepare("select * from names where age = ?")
|
46
|
+
@sth.execute(19)
|
47
|
+
assert_equal(
|
48
|
+
[
|
49
|
+
["Joe", 19]
|
50
|
+
],
|
51
|
+
@sth.fetch_all
|
52
|
+
)
|
53
|
+
|
54
|
+
@sth.finish
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_function_multiple_return_values
|
59
|
+
@sth = @dbh.prepare("SELECT age, select_subproperty(age, NULL), select_subproperty(age, 1) FROM names WHERE age = 19")
|
60
|
+
@sth.execute
|
61
|
+
assert_equal([[19, nil, 19]], @sth.fetch_all)
|
62
|
+
@sth.finish
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_tables
|
66
|
+
assert_equal(
|
67
|
+
[
|
68
|
+
"array_test",
|
69
|
+
"bit_test",
|
70
|
+
"blob_test",
|
71
|
+
"boolean_test",
|
72
|
+
"bytea_test",
|
73
|
+
"field_types_test",
|
74
|
+
"names",
|
75
|
+
"precision_test",
|
76
|
+
"time_test",
|
77
|
+
"timestamp_test",
|
78
|
+
"view_names"
|
79
|
+
], @dbh.tables.reject { |x| x =~ /^pg_/ }.sort)
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_columns
|
83
|
+
assert_equal(
|
84
|
+
[
|
85
|
+
{
|
86
|
+
:name =>"age",
|
87
|
+
:default =>nil,
|
88
|
+
:primary =>nil,
|
89
|
+
:scale =>nil,
|
90
|
+
:sql_type =>4,
|
91
|
+
:nullable =>true,
|
92
|
+
:indexed =>false,
|
93
|
+
:precision =>4,
|
94
|
+
:type_name =>"integer",
|
95
|
+
:unique =>nil,
|
96
|
+
:array_of_type =>nil
|
97
|
+
},
|
98
|
+
{
|
99
|
+
:name =>"name",
|
100
|
+
:default =>nil,
|
101
|
+
:primary =>nil,
|
102
|
+
:scale =>nil,
|
103
|
+
:sql_type =>12,
|
104
|
+
:nullable =>true,
|
105
|
+
:indexed =>false,
|
106
|
+
:precision =>255,
|
107
|
+
:type_name =>"character varying",
|
108
|
+
:unique =>nil,
|
109
|
+
:array_of_type =>nil
|
110
|
+
}
|
111
|
+
], @dbh.columns("names").sort_by { |x| x["name"] })
|
112
|
+
|
113
|
+
assert_equal(2, @dbh.columns("names").size) # make sure this works before the search path change
|
114
|
+
|
115
|
+
assert_equal(0, @dbh.columns("tbl").size) # tbl doesn't exist in public
|
116
|
+
|
117
|
+
@dbh.do('SET search_path TO schema1,schema2,"$user",public')
|
118
|
+
|
119
|
+
assert_equal(1, @dbh.columns('tbl').size);
|
120
|
+
assert_equal(
|
121
|
+
[
|
122
|
+
{
|
123
|
+
:name =>"foo",
|
124
|
+
:default =>nil,
|
125
|
+
:primary =>nil,
|
126
|
+
:scale =>nil,
|
127
|
+
:sql_type =>4,
|
128
|
+
:nullable =>true,
|
129
|
+
:indexed =>false,
|
130
|
+
:precision =>4,
|
131
|
+
:type_name =>"integer",
|
132
|
+
:unique =>nil,
|
133
|
+
:array_of_type =>nil
|
134
|
+
|
135
|
+
}
|
136
|
+
],
|
137
|
+
@dbh.columns('tbl')
|
138
|
+
)
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_connect_errors
|
143
|
+
dbd = nil
|
144
|
+
ex = assert_raise(DBI::OperationalError) {
|
145
|
+
dbd = DBI::DBD::Pg::Database.new('rubytest:1234', 'jim', nil, {})
|
146
|
+
}
|
147
|
+
ex = assert_raise(DBI::OperationalError) {
|
148
|
+
dbd = DBI::DBD::Pg::Database.new('bad_db_name', 'jim', nil, {})
|
149
|
+
}
|
150
|
+
|
151
|
+
# this corresponds to the test_parse_url_expected_errors test in tc_dbi.rb
|
152
|
+
assert_raise(DBI::InterfaceError) do
|
153
|
+
DBI.connect("dbi:Pg").disconnect
|
154
|
+
end
|
155
|
+
|
156
|
+
ensure
|
157
|
+
dbd.disconnect if dbd
|
158
|
+
end
|
159
|
+
|
160
|
+
def skip_test_type_map
|
161
|
+
dbd = get_dbd
|
162
|
+
def dbd.type_map
|
163
|
+
@type_map
|
164
|
+
end
|
165
|
+
assert dbd.type_map
|
166
|
+
assert_equal 21, dbd.convert("21", 23)
|
167
|
+
assert_equal "21", dbd.convert("21", 1043)
|
168
|
+
assert_equal 21.5, dbd.convert("21.5", 701)
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_simple_command
|
172
|
+
dbd = get_dbd
|
173
|
+
res = dbd.do("INSERT INTO names (name, age) VALUES('Dan', 16)")
|
174
|
+
assert_equal 1, res
|
175
|
+
|
176
|
+
@sth = get_dbi.prepare("SELECT name FROM names WHERE age=16")
|
177
|
+
@sth.execute
|
178
|
+
assert @sth.fetchable?
|
179
|
+
# XXX FIXME This is a bug in the DBD. #rows should equal 1 for select statements.
|
180
|
+
assert_equal 0, @sth.rows
|
181
|
+
ensure
|
182
|
+
dbd.do("DELETE FROM names WHERE age < 20")
|
183
|
+
dbd.disconnect if dbd
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_bad_command
|
187
|
+
dbd = get_dbd
|
188
|
+
assert_raise(DBI::ProgrammingError) {
|
189
|
+
dbd.do("INSERT INTO bad_table (name, age) VALUES('Dave', 12)")
|
190
|
+
}
|
191
|
+
ensure
|
192
|
+
dbd.disconnect if dbd
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_query_single
|
196
|
+
dbd = get_dbi
|
197
|
+
res = dbd.prepare("SELECT name, age FROM names WHERE age=21;")
|
198
|
+
assert res
|
199
|
+
res.execute
|
200
|
+
fields = res.column_info
|
201
|
+
assert_equal 2, fields.length
|
202
|
+
assert_equal 'name', fields[0]['name']
|
203
|
+
assert_equal 'varchar', fields[0]['type_name']
|
204
|
+
assert_equal 'age', fields[1]['name']
|
205
|
+
assert_equal 'int4', fields[1]['type_name']
|
206
|
+
|
207
|
+
row = res.fetch
|
208
|
+
|
209
|
+
assert_equal 'Bob', row[0]
|
210
|
+
assert_equal 21, row[1]
|
211
|
+
|
212
|
+
row = res.fetch
|
213
|
+
assert_nil row
|
214
|
+
|
215
|
+
res.finish
|
216
|
+
ensure
|
217
|
+
dbd.disconnect if dbd
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_query_multi
|
221
|
+
dbd = get_dbd
|
222
|
+
res = dbd.prepare("SELECT name, age FROM names WHERE age > 20;")
|
223
|
+
|
224
|
+
expected_list = ['Jim', 'Bob', 'Charlie']
|
225
|
+
res.execute
|
226
|
+
while row=res.fetch
|
227
|
+
expected = expected_list.shift
|
228
|
+
assert_equal expected, row[0]
|
229
|
+
end
|
230
|
+
|
231
|
+
res.finish
|
232
|
+
ensure
|
233
|
+
dbd.disconnect if dbd
|
234
|
+
end
|
235
|
+
|
236
|
+
def test_tables_call
|
237
|
+
# per bug #1082, views do not show up in tables listing.
|
238
|
+
assert get_dbi.tables.include?("view_names")
|
239
|
+
end
|
240
|
+
|
241
|
+
def get_dbi
|
242
|
+
config = DBDConfig.get_config
|
243
|
+
DBI.connect("dbi:Pg:#{config['postgresql']['dbname']}", config['postgresql']['username'], config['postgresql']['password'])
|
244
|
+
end
|
245
|
+
|
246
|
+
def get_dbd
|
247
|
+
config = DBDConfig.get_config['postgresql']
|
248
|
+
result = DBI::DBD::Pg::Database.new(config['dbname'], config['username'], config['password'], {})
|
249
|
+
result['AutoCommit'] = true
|
250
|
+
result
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# --------------------------------------------------------------------
|
@@ -0,0 +1,54 @@
|
|
1
|
+
create table names (
|
2
|
+
name varchar(255),
|
3
|
+
age integer
|
4
|
+
);
|
5
|
+
---
|
6
|
+
insert into names (name, age) values ('Joe', 19);
|
7
|
+
---
|
8
|
+
insert into names (name, age) values ('Jim', 30);
|
9
|
+
---
|
10
|
+
insert into names (name, age) values ('Bob', 21);
|
11
|
+
---
|
12
|
+
create table precision_test (text_field varchar(20) primary key not null, integer_field decimal(2,1));
|
13
|
+
---
|
14
|
+
CREATE TABLE blob_test (name VARCHAR(30), data OID);
|
15
|
+
---
|
16
|
+
create view view_names as select * from names;
|
17
|
+
---
|
18
|
+
create or replace function test_insert (varchar(255), integer)
|
19
|
+
returns integer
|
20
|
+
language sql
|
21
|
+
as 'insert into names (name, age) values ($1, $2); select age from names where name = $1';
|
22
|
+
---
|
23
|
+
create table boolean_test (num integer, mybool boolean);
|
24
|
+
---
|
25
|
+
create table time_test (mytime time);
|
26
|
+
---
|
27
|
+
create table timestamp_test (mytimestamp timestamp);
|
28
|
+
---
|
29
|
+
create table bit_test (mybit bit);
|
30
|
+
---
|
31
|
+
create table field_types_test (foo integer not null primary key default 1);
|
32
|
+
---
|
33
|
+
create table array_test (foo integer[], bar integer[3], baz integer[3][3], quux varchar[2]);
|
34
|
+
---
|
35
|
+
create table bytea_test (foo bytea);
|
36
|
+
---
|
37
|
+
create schema schema1;
|
38
|
+
---
|
39
|
+
create schema schema2;
|
40
|
+
---
|
41
|
+
create table schema1.tbl (foo integer);
|
42
|
+
---
|
43
|
+
create table schema2.tbl (bar integer);
|
44
|
+
---
|
45
|
+
create or replace function
|
46
|
+
select_subproperty(value names.age%TYPE, sub names.age%TYPE, out retval names.age%TYPE)
|
47
|
+
as $$
|
48
|
+
select
|
49
|
+
case
|
50
|
+
when $2 is not null
|
51
|
+
then $1
|
52
|
+
else null
|
53
|
+
end
|
54
|
+
$$ language sql;
|