ydbi 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/ChangeLog +3699 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/Rakefile +8 -0
  7. data/TODO +44 -0
  8. data/bench/bench.rb +79 -0
  9. data/bin/dbi +518 -0
  10. data/bin/test_broken_dbi +37 -0
  11. data/build/Rakefile.dbi.rb +60 -0
  12. data/build/rake_task_lib.rb +187 -0
  13. data/doc/DBD_SPEC.rdoc +88 -0
  14. data/doc/DBI_SPEC.rdoc +157 -0
  15. data/doc/homepage/contact.html +62 -0
  16. data/doc/homepage/development.html +124 -0
  17. data/doc/homepage/index.html +83 -0
  18. data/doc/homepage/ruby-dbi.css +91 -0
  19. data/examples/test1.pl +39 -0
  20. data/examples/test1.rb +20 -0
  21. data/examples/xmltest.rb +8 -0
  22. data/lib/dbd/Mysql.rb +137 -0
  23. data/lib/dbd/ODBC.rb +89 -0
  24. data/lib/dbd/Pg.rb +188 -0
  25. data/lib/dbd/SQLite.rb +97 -0
  26. data/lib/dbd/SQLite3.rb +124 -0
  27. data/lib/dbd/mysql/database.rb +405 -0
  28. data/lib/dbd/mysql/driver.rb +125 -0
  29. data/lib/dbd/mysql/statement.rb +188 -0
  30. data/lib/dbd/odbc/database.rb +128 -0
  31. data/lib/dbd/odbc/driver.rb +38 -0
  32. data/lib/dbd/odbc/statement.rb +137 -0
  33. data/lib/dbd/pg/database.rb +516 -0
  34. data/lib/dbd/pg/exec.rb +47 -0
  35. data/lib/dbd/pg/statement.rb +160 -0
  36. data/lib/dbd/pg/tuples.rb +121 -0
  37. data/lib/dbd/pg/type.rb +209 -0
  38. data/lib/dbd/sqlite/database.rb +151 -0
  39. data/lib/dbd/sqlite/statement.rb +125 -0
  40. data/lib/dbd/sqlite3/database.rb +201 -0
  41. data/lib/dbd/sqlite3/statement.rb +78 -0
  42. data/lib/dbi.rb +336 -0
  43. data/lib/dbi/base_classes.rb +12 -0
  44. data/lib/dbi/base_classes/database.rb +135 -0
  45. data/lib/dbi/base_classes/driver.rb +47 -0
  46. data/lib/dbi/base_classes/statement.rb +171 -0
  47. data/lib/dbi/binary.rb +25 -0
  48. data/lib/dbi/columninfo.rb +107 -0
  49. data/lib/dbi/exceptions.rb +65 -0
  50. data/lib/dbi/handles.rb +49 -0
  51. data/lib/dbi/handles/database.rb +241 -0
  52. data/lib/dbi/handles/driver.rb +60 -0
  53. data/lib/dbi/handles/statement.rb +408 -0
  54. data/lib/dbi/row.rb +269 -0
  55. data/lib/dbi/sql.rb +22 -0
  56. data/lib/dbi/sql/preparedstatement.rb +115 -0
  57. data/lib/dbi/sql_type_constants.rb +75 -0
  58. data/lib/dbi/trace.rb +91 -0
  59. data/lib/dbi/types.rb +218 -0
  60. data/lib/dbi/typeutil.rb +109 -0
  61. data/lib/dbi/utils.rb +60 -0
  62. data/lib/dbi/utils/date.rb +59 -0
  63. data/lib/dbi/utils/tableformatter.rb +112 -0
  64. data/lib/dbi/utils/time.rb +52 -0
  65. data/lib/dbi/utils/timestamp.rb +96 -0
  66. data/lib/dbi/utils/xmlformatter.rb +73 -0
  67. data/lib/dbi/version.rb +3 -0
  68. data/prototypes/types2.rb +237 -0
  69. data/readme.md +274 -0
  70. data/setup.rb +1585 -0
  71. data/test/DBD_TESTS +50 -0
  72. data/test/TESTING +16 -0
  73. data/test/dbd/general/test_database.rb +206 -0
  74. data/test/dbd/general/test_statement.rb +326 -0
  75. data/test/dbd/general/test_types.rb +296 -0
  76. data/test/dbd/mysql/base.rb +26 -0
  77. data/test/dbd/mysql/down.sql +19 -0
  78. data/test/dbd/mysql/test_blob.rb +18 -0
  79. data/test/dbd/mysql/test_new_methods.rb +7 -0
  80. data/test/dbd/mysql/test_patches.rb +111 -0
  81. data/test/dbd/mysql/up.sql +28 -0
  82. data/test/dbd/odbc/base.rb +30 -0
  83. data/test/dbd/odbc/down.sql +19 -0
  84. data/test/dbd/odbc/test_new_methods.rb +12 -0
  85. data/test/dbd/odbc/test_ping.rb +10 -0
  86. data/test/dbd/odbc/test_statement.rb +44 -0
  87. data/test/dbd/odbc/test_transactions.rb +58 -0
  88. data/test/dbd/odbc/up.sql +33 -0
  89. data/test/dbd/postgresql/base.rb +31 -0
  90. data/test/dbd/postgresql/down.sql +31 -0
  91. data/test/dbd/postgresql/test_arrays.rb +179 -0
  92. data/test/dbd/postgresql/test_async.rb +121 -0
  93. data/test/dbd/postgresql/test_blob.rb +36 -0
  94. data/test/dbd/postgresql/test_bytea.rb +87 -0
  95. data/test/dbd/postgresql/test_ping.rb +10 -0
  96. data/test/dbd/postgresql/test_timestamp.rb +77 -0
  97. data/test/dbd/postgresql/test_transactions.rb +58 -0
  98. data/test/dbd/postgresql/testdbipg.rb +307 -0
  99. data/test/dbd/postgresql/up.sql +60 -0
  100. data/test/dbd/sqlite/base.rb +32 -0
  101. data/test/dbd/sqlite/test_database.rb +30 -0
  102. data/test/dbd/sqlite/test_driver.rb +68 -0
  103. data/test/dbd/sqlite/test_statement.rb +112 -0
  104. data/test/dbd/sqlite/up.sql +25 -0
  105. data/test/dbd/sqlite3/base.rb +32 -0
  106. data/test/dbd/sqlite3/test_database.rb +77 -0
  107. data/test/dbd/sqlite3/test_driver.rb +67 -0
  108. data/test/dbd/sqlite3/test_statement.rb +88 -0
  109. data/test/dbd/sqlite3/up.sql +33 -0
  110. data/test/dbi/tc_columninfo.rb +94 -0
  111. data/test/dbi/tc_date.rb +88 -0
  112. data/test/dbi/tc_dbi.rb +184 -0
  113. data/test/dbi/tc_row.rb +256 -0
  114. data/test/dbi/tc_sqlbind.rb +168 -0
  115. data/test/dbi/tc_statementhandle.rb +29 -0
  116. data/test/dbi/tc_time.rb +77 -0
  117. data/test/dbi/tc_timestamp.rb +142 -0
  118. data/test/dbi/tc_types.rb +268 -0
  119. data/test/ts_dbd.rb +131 -0
  120. data/test/ts_dbi.rb +16 -0
  121. data/ydbi.gemspec +24 -0
  122. metadata +224 -0
@@ -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
+ # --------------------------------------------------------------------