ydbi 0.5.2 → 0.5.7

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.
Files changed (93) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +35 -0
  3. data/.gitignore +8 -0
  4. data/.travis.yml +15 -0
  5. data/ChangeLog +339 -314
  6. data/Gemfile +5 -0
  7. data/Rakefile +10 -0
  8. data/TODO +44 -0
  9. data/bench/bench.rb +79 -0
  10. data/build/rake_task_lib.rb +186 -0
  11. data/doc/DBD_SPEC.rdoc +88 -0
  12. data/doc/DBI_SPEC.rdoc +157 -0
  13. data/doc/homepage/contact.html +62 -0
  14. data/doc/homepage/development.html +124 -0
  15. data/doc/homepage/index.html +83 -0
  16. data/doc/homepage/ruby-dbi.css +91 -0
  17. data/lib/dbd/Mysql.rb +137 -0
  18. data/lib/dbd/ODBC.rb +89 -0
  19. data/lib/dbd/Pg.rb +188 -0
  20. data/lib/dbd/SQLite.rb +97 -0
  21. data/lib/dbd/SQLite3.rb +124 -0
  22. data/lib/dbd/mysql/database.rb +405 -0
  23. data/lib/dbd/mysql/driver.rb +125 -0
  24. data/lib/dbd/mysql/statement.rb +188 -0
  25. data/lib/dbd/odbc/database.rb +128 -0
  26. data/lib/dbd/odbc/driver.rb +38 -0
  27. data/lib/dbd/odbc/statement.rb +137 -0
  28. data/lib/dbd/pg/database.rb +504 -0
  29. data/lib/dbd/pg/exec.rb +47 -0
  30. data/lib/dbd/pg/statement.rb +160 -0
  31. data/lib/dbd/pg/tuples.rb +121 -0
  32. data/lib/dbd/pg/type.rb +209 -0
  33. data/lib/dbd/sqlite/database.rb +151 -0
  34. data/lib/dbd/sqlite/statement.rb +125 -0
  35. data/lib/dbd/sqlite3/database.rb +201 -0
  36. data/lib/dbd/sqlite3/statement.rb +78 -0
  37. data/lib/dbi.rb +14 -17
  38. data/lib/dbi/utils/date.rb +7 -3
  39. data/lib/dbi/version.rb +1 -1
  40. data/prototypes/types2.rb +237 -0
  41. data/readme.md +15 -0
  42. data/setup.rb +1585 -0
  43. data/test/DBD_TESTS +50 -0
  44. data/test/TESTING +16 -0
  45. data/test/dbd/general/test_database.rb +206 -0
  46. data/test/dbd/general/test_statement.rb +326 -0
  47. data/test/dbd/general/test_types.rb +296 -0
  48. data/test/dbd/mysql/base.rb +26 -0
  49. data/test/dbd/mysql/down.sql +19 -0
  50. data/test/dbd/mysql/test_blob.rb +18 -0
  51. data/test/dbd/mysql/test_new_methods.rb +7 -0
  52. data/test/dbd/mysql/test_patches.rb +111 -0
  53. data/test/dbd/mysql/up.sql +28 -0
  54. data/test/dbd/odbc/base.rb +30 -0
  55. data/test/dbd/odbc/down.sql +19 -0
  56. data/test/dbd/odbc/test_new_methods.rb +12 -0
  57. data/test/dbd/odbc/test_ping.rb +10 -0
  58. data/test/dbd/odbc/test_statement.rb +44 -0
  59. data/test/dbd/odbc/test_transactions.rb +58 -0
  60. data/test/dbd/odbc/up.sql +33 -0
  61. data/test/dbd/postgresql/base.rb +31 -0
  62. data/test/dbd/postgresql/down.sql +31 -0
  63. data/test/dbd/postgresql/test_arrays.rb +179 -0
  64. data/test/dbd/postgresql/test_async.rb +121 -0
  65. data/test/dbd/postgresql/test_blob.rb +36 -0
  66. data/test/dbd/postgresql/test_bytea.rb +87 -0
  67. data/test/dbd/postgresql/test_ping.rb +10 -0
  68. data/test/dbd/postgresql/test_timestamp.rb +77 -0
  69. data/test/dbd/postgresql/test_transactions.rb +58 -0
  70. data/test/dbd/postgresql/testdbipg.rb +307 -0
  71. data/test/dbd/postgresql/up.sql +60 -0
  72. data/test/dbd/sqlite/base.rb +32 -0
  73. data/test/dbd/sqlite/test_database.rb +30 -0
  74. data/test/dbd/sqlite/test_driver.rb +68 -0
  75. data/test/dbd/sqlite/test_statement.rb +112 -0
  76. data/test/dbd/sqlite/up.sql +25 -0
  77. data/test/dbd/sqlite3/base.rb +32 -0
  78. data/test/dbd/sqlite3/test_database.rb +77 -0
  79. data/test/dbd/sqlite3/test_driver.rb +67 -0
  80. data/test/dbd/sqlite3/test_statement.rb +88 -0
  81. data/test/dbd/sqlite3/up.sql +33 -0
  82. data/test/dbi/tc_columninfo.rb +4 -9
  83. data/test/dbi/tc_date.rb +2 -9
  84. data/test/dbi/tc_dbi.rb +3 -9
  85. data/test/dbi/tc_row.rb +17 -23
  86. data/test/dbi/tc_sqlbind.rb +6 -7
  87. data/test/dbi/tc_statementhandle.rb +3 -4
  88. data/test/dbi/tc_time.rb +2 -8
  89. data/test/dbi/tc_timestamp.rb +2 -16
  90. data/test/dbi/tc_types.rb +5 -11
  91. data/test/ts_dbd.rb +131 -0
  92. data/ydbi.gemspec +23 -0
  93. metadata +128 -10
@@ -0,0 +1,121 @@
1
+ class TestPostgresAsync < DBDConfig.testbase(:postgresql)
2
+ def get_dbh
3
+ config = DBDConfig.get_config['postgresql']
4
+ DBI.connect("dbi:Pg:#{config['dbname']}", config['username'], config['password'])
5
+ end
6
+
7
+ def get_db_info
8
+ config = DBDConfig.get_config['postgresql']
9
+ dsn = "dbi:Pg:database=#{config['dbname']}"
10
+ user = config['username']
11
+ pass = config['password']
12
+
13
+ return [dsn, user, pass]
14
+ end
15
+
16
+ def test_async_default
17
+ dsn, user, pass = get_db_info
18
+
19
+ DBI.connect(dsn, user, pass) do |dbh|
20
+ assert_equal false, dbh['pg_async']
21
+ assert_equal false, dbh['NonBlocking']
22
+ dbh.prepare('SELECT 1') do |sth| # Statement inherits
23
+ assert_equal false, sth['pg_async']
24
+ assert_equal false, sth['NonBlocking']
25
+ end
26
+ end
27
+ end
28
+
29
+ def test_async_dsn_enable
30
+ dsn, user, pass = get_db_info
31
+
32
+ for enable in ['true', 'TRUE', 'tRuE']
33
+ DBI.connect(dsn + ";pg_async=#{enable}", user, pass) do |dbh|
34
+ assert_equal true, dbh['pg_async']
35
+ assert_equal true, dbh['NonBlocking']
36
+ dbh.prepare('SELECT 1') do |sth| # Statement inherits
37
+ assert_equal true, sth['pg_async']
38
+ assert_equal true, sth['NonBlocking']
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def test_async_attr_enable
45
+ dsn, user, pass = get_db_info
46
+
47
+ for enable in ['true', 'TRUE', 'tRuE']
48
+ DBI.connect(dsn, user, pass, { 'pg_async' => enable } ) do |dbh|
49
+ assert_equal true, dbh['pg_async']
50
+ assert_equal true, dbh['NonBlocking']
51
+ dbh.prepare('SELECT 1') do |sth| # Statement inherits
52
+ assert_equal true, sth['pg_async']
53
+ assert_equal true, sth['NonBlocking']
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def test_async_dsn_disable
60
+ dsn, user, pass = get_db_info
61
+
62
+ for disable in ['false', 'FALSE', 'fAlSe']
63
+ DBI.connect(dsn + ";pg_async=#{disable}", user, pass) do |dbh|
64
+ assert_equal false, dbh['pg_async']
65
+ assert_equal false, dbh['NonBlocking']
66
+ dbh.prepare('SELECT 1') do |sth| # Statement inherits
67
+ assert_equal false, sth['pg_async']
68
+ assert_equal false, sth['NonBlocking']
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ def test_async_attr_disable
75
+ dsn, user, pass = get_db_info
76
+
77
+ for disable in ['false', 'FALSE', 'fAlSe']
78
+ DBI.connect(dsn, user, pass, { 'pg_async' => disable }) do |dbh|
79
+ assert_equal false, dbh['pg_async']
80
+ assert_equal false, dbh['NonBlocking']
81
+ dbh.prepare('SELECT 1') do |sth| # Statement inherits
82
+ assert_equal false, sth['pg_async']
83
+ assert_equal false, sth['NonBlocking']
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ def test_manual_enable
90
+ dsn, user, pass = get_db_info
91
+
92
+ DBI.connect(dsn, user, pass) do |dbh|
93
+ dbh['pg_async'] = true
94
+ assert_equal true, dbh['pg_async']
95
+ assert_equal true, dbh['NonBlocking']
96
+ dbh.prepare('SELECT 1') do |sth| # Statement inherits
97
+ assert_equal true, sth['pg_async']
98
+ assert_equal true, sth['NonBlocking']
99
+ end
100
+ end
101
+ end
102
+
103
+ def test_async_commands
104
+ dsn, user, pass = get_db_info
105
+
106
+ DBI.connect(dsn + ";pg_async=true", user, pass) do |dbh|
107
+ assert_equal true, dbh['pg_async']
108
+ assert_equal true, dbh['NonBlocking']
109
+ ret = dbh.select_all('SELECT 1')
110
+ assert_equal [[1]], ret
111
+
112
+ ret = dbh.select_all(%q{SELECT 1 WHERE 'foo' = ?}, 'bar')
113
+ assert_equal [], ret
114
+
115
+ dbh.prepare(%q{SELECT 1 WHERE 'foo' = ?}) do |sth|
116
+ sth.execute('bar')
117
+ assert_equal [], sth.fetch_all
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,36 @@
1
+ class TestPostgresBlob < DBDConfig.testbase(:postgresql)
2
+ DATA = "this is my new binary object"
3
+
4
+ def test_insert
5
+ assert @dbh
6
+ assert @dbh.ping
7
+
8
+ # test with DBI::Binary
9
+ assert_equal 1, @dbh.do("INSERT INTO blob_test (name, data) VALUES (?,?)", "test", DBI::Binary.new(DATA))
10
+
11
+ # test with blob_create directly
12
+ blob = @dbh.func(:blob_create, PGconn::INV_WRITE)
13
+ assert blob
14
+ assert @dbh.func(:blob_write, blob, DATA)
15
+ assert_equal 1, @dbh.do("INSERT INTO blob_test (name, data) VALUES (?,?)", "test (2)", blob)
16
+
17
+ # test with blob_import directly
18
+ File.open('/tmp/pg_dbi_import_test', 'w') { |f| f << DATA }
19
+ blob = @dbh.func(:blob_import, '/tmp/pg_dbi_import_test')
20
+ assert blob
21
+ assert_equal 1, @dbh.do("INSERT INTO blob_test (name, data) VALUES (?,?)", "test (2)", blob)
22
+
23
+ index = 0
24
+ @dbh.select_all("SELECT name, data FROM blob_test") do |name, data|
25
+ index += 1
26
+ assert_equal DATA, @dbh.func(:blob_read, data, DATA.length)
27
+ @dbh.func(:blob_export, data, '/tmp/pg_dbi_read_test')
28
+ assert_equal DATA, File.read('/tmp/pg_dbi_read_test')
29
+ end
30
+
31
+ assert_equal 3, index
32
+
33
+ File.unlink("/tmp/pg_dbi_read_test")
34
+ File.unlink("/tmp/pg_dbi_import_test")
35
+ end
36
+ end
@@ -0,0 +1,87 @@
1
+ require 'dbd/Pg'
2
+
3
+ begin
4
+ require 'rubygems'
5
+ gem 'pg'
6
+ rescue Exception => e
7
+ end
8
+
9
+ require 'pg'
10
+
11
+ LEN = 50
12
+
13
+ class TestPostgresByteA < DBDConfig.testbase(:postgresql)
14
+ # FIXME the 'pg' module is broken and doesn't encode/decode properly.
15
+ # this test should prove that the 'pg' module works so we can back out our
16
+ # hacks.
17
+ def skip_underlying_driver
18
+ str = generate_random_string
19
+
20
+ encoded = PGconn.escape_bytea(str.dup)
21
+ decoded = PGconn.unescape_bytea(encoded)
22
+
23
+ assert_equal str, decoded
24
+ end
25
+
26
+ def test_encode_decode
27
+ bytea = DBI::DBD::Pg::Type::ByteA
28
+
29
+ # some specific cases that were failing intermittenly
30
+ # poor \\ handling
31
+ str = "\236\000\257\202G<\371\035TPEO\211\005*AH'H\3136\360\004\245\261\037\340u\003s\\772X\231\002\200\n\327\202\217\353\177r\317o\341\237\341"
32
+ encoded = bytea.escape_bytea(str)
33
+ decoded = bytea.parse(encoded)
34
+
35
+ assert_equal str, decoded
36
+
37
+ # the split hack not working
38
+ str = "\343\336e\260\337\373\314\026\323#\237i\035\0302\024\346X\274\016\324\371\206\036\230\374\206#rA\n\214\272\316\330\025\374\000\2663\244M\255x\360\002\266q\336\231"
39
+
40
+ encoded = bytea.escape_bytea(str)
41
+ decoded = bytea.parse(encoded)
42
+
43
+ assert_equal str, decoded
44
+
45
+ # delimiter at the end
46
+ str = "\343\336e\260\337\373\314\026\323#\237i\035\0302\024\346X\274\016\324\371\206\036\230\374\206#rA\n\214\272\316\330\025\374\000\2663\244M\255x\360\002\266q\336\231\\\\\\\\"
47
+
48
+ encoded = bytea.escape_bytea(str)
49
+ decoded = bytea.parse(encoded)
50
+
51
+ assert_equal str, decoded
52
+
53
+ # a huge test to weed out all the stragglers
54
+ 5_000.times do
55
+ str = generate_random_string
56
+
57
+ encoded = bytea.escape_bytea(str)
58
+ decoded = bytea.parse(encoded)
59
+
60
+ assert_equal str, decoded
61
+ end
62
+ end
63
+
64
+ def test_insert_retrieve
65
+ assert_nothing_raised do
66
+ str = generate_random_string
67
+ sth = @dbh.prepare('insert into bytea_test (foo) values (?)')
68
+ sth.execute(DBI::DBD::Pg::Type::ByteA.new(str))
69
+ sth.finish
70
+
71
+ sth = @dbh.prepare('select * from bytea_test')
72
+ sth.execute
73
+ assert_equal([str], sth.fetch)
74
+ sth.finish
75
+ end
76
+ end
77
+
78
+ def generate_random_string
79
+ # random string test
80
+ str = " " * LEN
81
+ for i in 0...LEN
82
+ str[i] = (rand * 256).to_i.chr
83
+ end
84
+
85
+ return str
86
+ end
87
+ end
@@ -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_raises(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,307 @@
1
+ require 'dbd/Pg'
2
+
3
+ module DBI
4
+ class ForcedError < ::DBI::Error
5
+ end
6
+ end
7
+
8
+ ######################################################################
9
+ # Test the PostgreSql DBD driver. This test exercises options
10
+ # difficult to test through the standard DBI interface.
11
+ #
12
+ class TestDbdPostgres < DBDConfig.testbase(:postgresql)
13
+
14
+ # FIXME this is a feature that should be there, but currently isn't.
15
+ # def test_connect
16
+ # dbd = get_dbd
17
+ # assert_not_nil dbd.connection
18
+ # assert_equal 'localhost', dbd.connection.host
19
+ # assert_equal 'erikh', dbd.connection.user
20
+ # assert_equal 'rubytest', dbd.connection.db
21
+ # assert_equal 5432, dbd.connection.port
22
+ # ensure
23
+ # dbd.disconnect if dbd
24
+ # end
25
+
26
+ # this monkeypatch is used for the following test... NEVER integrate this into DBI proper.
27
+ class DBI::StatementHandle < DBI::Handle
28
+ def stmt_name
29
+ @handle.instance_variable_get(:@stmt_name)
30
+ end
31
+ end
32
+
33
+ def test_database_name
34
+ assert_nothing_raised do
35
+ assert_equal DBDConfig.get_config[dbtype]['dbname'], @dbh.database_name
36
+ end
37
+ end
38
+
39
+ def test_enum_type
40
+ assert_nothing_raised do
41
+ assert(@dbh.convert_types)
42
+ @sth = @dbh.prepare("insert into enum_type_test values (?)")
43
+ @sth.execute("one")
44
+ @sth.finish
45
+
46
+ @sth = @dbh.prepare("select foo from enum_type_test")
47
+ @sth.execute
48
+ assert_equal(@sth.fetch, ['one'])
49
+ @sth.finish
50
+ end
51
+ end
52
+
53
+ def test_statement_finish_deallocates_sth
54
+ assert_nothing_raised do
55
+ @sth = @dbh.prepare("select * from names")
56
+ @sth.execute
57
+ sth_internal_name = @sth.stmt_name
58
+ assert(sth_internal_name)
59
+ assert(!sth_internal_name.empty?)
60
+ @sth.finish
61
+
62
+ # at this point, the statement name should no longer exist
63
+ #
64
+ # XXX this is a potentially horrible way of doing it since it'll
65
+ # create another prepared statement, but *at this time*, I don't
66
+ # see any drawbacks and the alternative is considerably uglier.
67
+
68
+ @sth = @dbh.prepare("select count(*) from pg_prepared_statements where name = ?")
69
+ @sth.execute(sth_internal_name)
70
+ assert_equal([0], @sth.fetch)
71
+ @sth.finish
72
+ end
73
+ end
74
+
75
+ def test_binding
76
+ assert(@dbh["pg_native_binding"])
77
+
78
+ assert_raises(DBI::ProgrammingError) do
79
+ @sth = @dbh.prepare("select * from names where age IS NOT ?")
80
+ @sth.execute("NULL")
81
+ @sth.finish
82
+ end
83
+
84
+ assert_nothing_raised do
85
+ @dbh["pg_native_binding"] = false
86
+ @sth = @dbh.prepare("select * from names where age IS NOT ? order by age")
87
+ @sth.execute("NULL")
88
+ assert_equal(
89
+ [
90
+ ["Joe", 19],
91
+ ["Bob", 21],
92
+ ["Jim", 30],
93
+ ],
94
+ @sth.fetch_all
95
+ )
96
+
97
+ @sth.finish
98
+
99
+ @sth = @dbh.prepare("select * from names where age = ?")
100
+ @sth.execute(19)
101
+ assert_equal(
102
+ [
103
+ ["Joe", 19]
104
+ ],
105
+ @sth.fetch_all
106
+ )
107
+
108
+ @sth.finish
109
+ end
110
+ end
111
+
112
+ def test_function_multiple_return_values
113
+ @sth = @dbh.prepare("SELECT age, select_subproperty(age, NULL), select_subproperty(age, 1) FROM names WHERE age = 19")
114
+ @sth.execute
115
+ assert_equal([[19, nil, 19]], @sth.fetch_all)
116
+ @sth.finish
117
+ end
118
+
119
+ def test_columns
120
+ assert_equal(
121
+ [
122
+ {
123
+ :name =>"age",
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
+ :name =>"name",
137
+ :default =>nil,
138
+ :primary =>nil,
139
+ :scale =>nil,
140
+ :sql_type =>12,
141
+ :nullable =>true,
142
+ :indexed =>false,
143
+ :precision =>255,
144
+ :type_name =>"character varying",
145
+ :unique =>nil,
146
+ :array_of_type =>nil
147
+ }
148
+ ], @dbh.columns("names").sort_by { |x| x["name"] })
149
+
150
+ assert_equal(2, @dbh.columns("names").size) # make sure this works before the search path change
151
+
152
+ assert_equal(0, @dbh.columns("tbl").size) # tbl doesn't exist in public
153
+
154
+ @dbh.do('SET search_path TO schema1,schema2,"$user",public')
155
+
156
+ assert_equal(1, @dbh.columns('tbl').size);
157
+ assert_equal(
158
+ [
159
+ {
160
+ :name =>"foo",
161
+ :default =>nil,
162
+ :primary =>nil,
163
+ :scale =>nil,
164
+ :sql_type =>4,
165
+ :nullable =>true,
166
+ :indexed =>false,
167
+ :precision =>4,
168
+ :type_name =>"integer",
169
+ :unique =>nil,
170
+ :array_of_type =>nil
171
+
172
+ }
173
+ ],
174
+ @dbh.columns('tbl')
175
+ )
176
+
177
+ end
178
+
179
+ def test_statement_name_uniqueness
180
+ 5000.times do
181
+ begin
182
+ @dbh.prepare('SELECT 1').execute()
183
+ raise DBI::ForcedError
184
+ sth.finish # never reached
185
+ rescue DBI::ProgrammingError => e
186
+ # ERROR: prepared statement "ruby-dbi:Pg:-604926268" already exists
187
+ # This should never happen
188
+ raise e
189
+ rescue DBI::ForcedError
190
+ # no-op
191
+ end
192
+ end
193
+ end
194
+
195
+ def test_connect_errors
196
+ dbd = nil
197
+ ex = assert_raises(DBI::OperationalError) {
198
+ dbd = DBI::DBD::Pg::Database.new('rubytest:1234', 'jim', nil, {})
199
+ }
200
+ ex = assert_raises(DBI::OperationalError) {
201
+ dbd = DBI::DBD::Pg::Database.new('bad_db_name', 'jim', nil, {})
202
+ }
203
+
204
+ # this corresponds to the test_parse_url_expected_errors test in tc_dbi.rb
205
+ assert_raises(DBI::InterfaceError) do
206
+ DBI.connect("dbi:Pg").disconnect
207
+ end
208
+
209
+ ensure
210
+ dbd.disconnect if dbd
211
+ end
212
+
213
+ def skip_test_type_map
214
+ dbd = get_dbd
215
+ def dbd.type_map
216
+ @type_map
217
+ end
218
+ assert dbd.type_map
219
+ assert_equal 21, dbd.convert("21", 23)
220
+ assert_equal "21", dbd.convert("21", 1043)
221
+ assert_equal 21.5, dbd.convert("21.5", 701)
222
+ end
223
+
224
+ def test_simple_command
225
+ dbd = get_dbd
226
+ res = dbd.do("INSERT INTO names (name, age) VALUES('Dan', 16)")
227
+ assert_equal 1, res
228
+
229
+ @sth = get_dbi.prepare("SELECT name FROM names WHERE age=16")
230
+ @sth.execute
231
+ assert @sth.fetchable?
232
+ # XXX FIXME This is a bug in the DBD. #rows should equal 1 for select statements.
233
+ assert_equal 0, @sth.rows
234
+ ensure
235
+ dbd.do("DELETE FROM names WHERE age < 20")
236
+ dbd.disconnect if dbd
237
+ end
238
+
239
+ def test_bad_command
240
+ dbd = get_dbd
241
+ assert_raises(DBI::ProgrammingError) {
242
+ dbd.do("INSERT INTO bad_table (name, age) VALUES('Dave', 12)")
243
+ }
244
+ ensure
245
+ dbd.disconnect if dbd
246
+ end
247
+
248
+ def test_query_single
249
+ dbd = get_dbi
250
+ res = dbd.prepare("SELECT name, age FROM names WHERE age=21;")
251
+ assert res
252
+ res.execute
253
+ fields = res.column_info
254
+ assert_equal 2, fields.length
255
+ assert_equal 'name', fields[0]['name']
256
+ assert_equal 'varchar', fields[0]['type_name']
257
+ assert_equal 'age', fields[1]['name']
258
+ assert_equal 'int4', fields[1]['type_name']
259
+
260
+ row = res.fetch
261
+
262
+ assert_equal 'Bob', row[0]
263
+ assert_equal 21, row[1]
264
+
265
+ row = res.fetch
266
+ assert_nil row
267
+
268
+ res.finish
269
+ ensure
270
+ dbd.disconnect if dbd
271
+ end
272
+
273
+ def test_query_multi
274
+ dbd = get_dbd
275
+ res = dbd.prepare("SELECT name, age FROM names WHERE age > 20;")
276
+
277
+ expected_list = ['Jim', 'Bob', 'Charlie']
278
+ res.execute
279
+ while row=res.fetch
280
+ expected = expected_list.shift
281
+ assert_equal expected, row[0]
282
+ end
283
+
284
+ res.finish
285
+ ensure
286
+ dbd.disconnect if dbd
287
+ end
288
+
289
+ def test_tables_call
290
+ # per bug #1082, views do not show up in tables listing.
291
+ assert get_dbi.tables.include?("view_names")
292
+ end
293
+
294
+ def get_dbi
295
+ config = DBDConfig.get_config
296
+ DBI.connect("dbi:Pg:#{config['postgresql']['dbname']}", config['postgresql']['username'], config['postgresql']['password'])
297
+ end
298
+
299
+ def get_dbd
300
+ config = DBDConfig.get_config['postgresql']
301
+ result = DBI::DBD::Pg::Database.new(config['dbname'], config['username'], config['password'], {})
302
+ result['AutoCommit'] = true
303
+ result
304
+ end
305
+ end
306
+
307
+ # --------------------------------------------------------------------