dbd-sqlite 0.1

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.
@@ -0,0 +1,120 @@
1
+ #
2
+ # See DBI::BaseStatement.
3
+ #
4
+ class DBI::DBD::SQLite::Statement < DBI::BaseStatement
5
+ DBI_TYPE_MAP = [
6
+ [ /^INT(EGER)?$/i, DBI::SQL_INTEGER ],
7
+ [ /^(OID|ROWID|_ROWID_)$/i, DBI::SQL_OTHER ],
8
+ [ /^FLOAT$/i, DBI::SQL_FLOAT ],
9
+ [ /^REAL$/i, DBI::SQL_REAL ],
10
+ [ /^DOUBLE$/i, DBI::SQL_DOUBLE ],
11
+ [ /^DECIMAL/i, DBI::SQL_DECIMAL ],
12
+ [ /^(BOOL|BOOLEAN)$/i, DBI::SQL_BOOLEAN ],
13
+ [ /^TIME$/i, DBI::SQL_TIME ],
14
+ [ /^DATE$/i, DBI::SQL_DATE ],
15
+ [ /^TIMESTAMP$/i, DBI::SQL_TIMESTAMP ],
16
+ [ /^(VARCHAR|TEXT)/i, DBI::SQL_VARCHAR ],
17
+ [ /^CHAR$/i, DBI::SQL_CHAR ],
18
+ ]
19
+
20
+ def initialize(stmt, dbh)
21
+ @dbh = dbh
22
+ @statement = DBI::SQL::PreparedStatement.new(@dbh, stmt)
23
+ @attr = { }
24
+ @params = [ ]
25
+ @rows = [ ]
26
+ @result_set = nil
27
+ @dbh.open_handles += 1
28
+ end
29
+
30
+ #
31
+ # See DBI::BaseStatement#bind_param. This method will also raise
32
+ # DBI::InterfaceError if +param+ is not a Fixnum, to prevent incorrect
33
+ # binding.
34
+ #
35
+ def bind_param(param, value, attributes=nil)
36
+ unless param.kind_of? Fixnum
37
+ raise DBI::InterfaceError, "Only numeric parameters are supported"
38
+ end
39
+
40
+ @params[param-1] = value
41
+
42
+ # FIXME what to do with attributes? are they important in SQLite?
43
+ end
44
+
45
+ #
46
+ # See DBI::BaseStatement#execute.
47
+ #
48
+ # In the event AutoCommit is off and no transaction is currently executing,
49
+ # one will be opened at this point. It is your responsibility to #finish,
50
+ # #cancel, #rollback, or #commit.
51
+ #
52
+ def execute
53
+ sql = @statement.bind(@params)
54
+ # XXX sqlite re-escapes this for us automatically, it's causing trouble with everything else.
55
+ # this will probably break in a horrible manner and I will be forced to "fix" it again.
56
+ sql.gsub!(/\\\\/) { '\\' }
57
+ DBI::DBD::SQLite.check_sql(sql)
58
+
59
+ begin
60
+ unless @dbh.db.transaction_active?
61
+ @dbh.db.transaction
62
+ end
63
+ @result_set = @dbh.db.query(sql)
64
+ @dbh.commit if @dbh["AutoCommit"]
65
+ rescue Exception => e
66
+ raise DBI::DatabaseError, e.message
67
+ end
68
+ end
69
+
70
+ alias :finish :cancel
71
+
72
+ def finish
73
+ # nil out the result set
74
+ @result_set.close if @result_set
75
+ @result_set = nil
76
+ @rows = nil
77
+ @dbh.open_handles -= 1
78
+ end
79
+
80
+ def fetch
81
+ return nil if @result_set.eof?
82
+
83
+ row = @result_set.next
84
+ return nil unless row
85
+
86
+ return row
87
+ end
88
+
89
+ def column_info
90
+ columns = [ ]
91
+
92
+ # FIXME this shit should *really* be abstracted into DBI
93
+ # FIXME this still doesn't handle nullable/unique/default stuff.
94
+ @result_set.columns.each_with_index do |name, i|
95
+ columns[i] = { } unless columns[i]
96
+ columns[i]["name"] = name
97
+ type_name = @result_set.types[i]
98
+
99
+ if type_name
100
+ m = DBI::DBD::SQLite.parse_type(type_name)
101
+
102
+ columns[i]["type_name"] = m[1]
103
+ columns[i]["precision"] = m[3].to_i if m[3]
104
+ columns[i]["scale"] = m[5].to_i if m[5]
105
+ DBI_TYPE_MAP.each do |map|
106
+ if columns[i]["type_name"] =~ map[0]
107
+ columns[i]["sql_type"] = map[1]
108
+ break
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ return columns
115
+ end
116
+
117
+ def rows
118
+ return @dbh.db.changes
119
+ end
120
+ end
data/test/DBD_TESTS ADDED
@@ -0,0 +1,48 @@
1
+ ================================================================================
2
+ Using DBD tests
3
+ ================================================================================
4
+
5
+ Create a YAML file named .ruby-dbi.test-config.yaml in your home directory.
6
+
7
+ This file is a hash of keys that determine what you want to test and how you
8
+ access the databases related to those tests.
9
+
10
+ The key 'dbtypes' is an array which determines what tests you want to run. They
11
+ *do not* correspond to the driver names, they correspond to the test
12
+ directories that were made for them.
13
+
14
+ Each 'dbtype' has a key that contains a hash of values:
15
+ - username: the username of your account
16
+ - password: the password for your account
17
+ - dbname: the name of the database to connect to
18
+
19
+ NOTE that tests expect to connect to a database on localhost currently. This
20
+ may be fixed in the future, especially when we start writing Oracle and
21
+ SQLServer tests.
22
+
23
+ Each DBD test relies on database semantics which may not match up entirely with
24
+ this configuration. For instance, the postgresql tests expect you to be able to
25
+ work with the database directly via the `psql' client. This is something which
26
+ will eventually be remedied as time and ability allows.
27
+
28
+ Here is a sample configuration to get you started with the postgresql tests:
29
+
30
+ ################################################################################
31
+
32
+ ---
33
+ dbtypes:
34
+ - postgresql
35
+ postgresql:
36
+ username: erikh
37
+ password: monkeys
38
+ dbname: rubytest
39
+
40
+ ################################################################################
41
+
42
+ NOTE the --- is part of the file and is not a separator.
43
+
44
+ ================================================================================
45
+ Writing DBD tests
46
+ ================================================================================
47
+
48
+ Coming soon.
@@ -0,0 +1,157 @@
1
+ @class = Class.new(DBDConfig.testbase(DBDConfig.current_dbtype)) do
2
+ def test_ping
3
+ assert @dbh.ping
4
+ # XXX if it isn't obvious, this should be tested better. Not sure what
5
+ # good behavior is yet.
6
+ end
7
+
8
+ def test_columns
9
+ assert_nothing_raised do
10
+ cols = @dbh.columns("precision_test")
11
+
12
+ assert(cols)
13
+ assert_kind_of(Array, cols)
14
+ assert_equal(2, cols.length)
15
+
16
+ # the first column should always be "text_field" and have the following
17
+ # properties:
18
+ assert_equal("text_field", cols[0]["name"])
19
+ assert(!cols[0]["nullable"])
20
+
21
+ assert_equal(20, cols[0]["precision"])
22
+ # scale can be either nil or 0 for character types.
23
+ case cols[0]["scale"]
24
+ when nil
25
+ assert_equal(nil, cols[0]["scale"])
26
+ when 0
27
+ assert_equal(0, cols[0]["scale"])
28
+ else
29
+ flunk "scale can be either 0 or nil for character types"
30
+ end
31
+
32
+ assert_equal(
33
+ DBI::Type::Varchar,
34
+ DBI::TypeUtil.type_name_to_module(cols[0]["type_name"])
35
+ )
36
+
37
+ # the second column should always be "integer_field" and have the following
38
+ # properties:
39
+ assert_equal("integer_field", cols[1]["name"])
40
+ assert(cols[1]["nullable"])
41
+ assert_equal(1, cols[1]["scale"])
42
+ assert_equal(2, cols[1]["precision"])
43
+ assert_equal(
44
+ DBI::Type::Decimal,
45
+ DBI::TypeUtil.type_name_to_module(cols[1]["type_name"])
46
+ )
47
+
48
+ # finally, we ensure that every column in the array is a ColumnInfo
49
+ # object
50
+ cols.each { |col| assert_kind_of(DBI::ColumnInfo, col) }
51
+ end
52
+ end
53
+
54
+ def test_prepare
55
+ @sth = @dbh.prepare('select * from names')
56
+
57
+ assert @sth
58
+ assert_kind_of DBI::StatementHandle, @sth
59
+
60
+ @sth.finish
61
+ end
62
+
63
+ def test_do
64
+ assert_equal 1, @dbh.do("insert into names (name, age) values (?, ?)", "Billy", 21)
65
+ @sth = @dbh.prepare("select * from names where name = ?")
66
+ @sth.execute("Billy")
67
+ assert_equal ["Billy", 21], @sth.fetch
68
+ @sth.finish
69
+ end
70
+
71
+ def test_tables
72
+ tables = @dbh.tables.sort
73
+
74
+ # since this is a general test, let's prune the system tables
75
+ # FIXME not so sure if this should be a general test anymore.
76
+ if dbtype == "odbc"
77
+ tables -= [
78
+ "administrable_role_authorizations",
79
+ "applicable_roles",
80
+ "attributes",
81
+ "check_constraint_routine_usage",
82
+ "check_constraints",
83
+ "column_domain_usage",
84
+ "column_privileges",
85
+ "column_udt_usage",
86
+ "columns",
87
+ "constraint_column_usage",
88
+ "constraint_table_usage",
89
+ "data_type_privileges",
90
+ "domain_constraints",
91
+ "domain_udt_usage",
92
+ "domains",
93
+ "element_types",
94
+ "enabled_roles",
95
+ "information_schema_catalog_name",
96
+ "key_column_usage",
97
+ "parameters",
98
+ "referential_constraints",
99
+ "role_column_grants",
100
+ "role_routine_grants",
101
+ "role_table_grants",
102
+ "role_usage_grants",
103
+ "routine_privileges",
104
+ "routines",
105
+ "schemata",
106
+ "sequences",
107
+ "sql_features",
108
+ "sql_implementation_info",
109
+ "sql_languages",
110
+ "sql_packages",
111
+ "sql_parts",
112
+ "sql_sizing",
113
+ "sql_sizing_profiles",
114
+ "table_constraints",
115
+ "table_privileges",
116
+ "tables",
117
+ "triggered_update_columns",
118
+ "triggers",
119
+ "usage_privileges",
120
+ "view_column_usage",
121
+ "view_routine_usage",
122
+ "view_table_usage",
123
+ "views"
124
+ ]
125
+ end
126
+
127
+ case dbtype
128
+ when "postgresql"
129
+ tables.reject! { |x| x =~ /^pg_/ }
130
+ assert_equal %w(array_test bit_test blob_test boolean_test bytea_test field_types_test names precision_test time_test timestamp_test view_names), tables
131
+ else
132
+ assert_equal %w(bit_test blob_test boolean_test field_types_test names precision_test time_test timestamp_test view_names), tables
133
+ end
134
+ end
135
+
136
+ def test_attrs
137
+ # test defaults
138
+ assert @dbh["AutoCommit"] # should be true
139
+
140
+ # test setting
141
+ assert !(@dbh["AutoCommit"] = false)
142
+ assert !@dbh["AutoCommit"]
143
+
144
+ # test committing an outstanding transaction
145
+
146
+ @sth = @dbh.prepare("insert into names (name, age) values (?, ?)")
147
+ @sth.execute("Billy", 22)
148
+ @sth.finish
149
+
150
+ assert @dbh["AutoCommit"] = true # should commit at this point
151
+
152
+ @sth = @dbh.prepare("select * from names where name = ?")
153
+ @sth.execute("Billy")
154
+ assert_equal [ "Billy", 22 ], @sth.fetch
155
+ @sth.finish
156
+ end
157
+ end
@@ -0,0 +1,240 @@
1
+ @class = Class.new(DBDConfig.testbase(DBDConfig.current_dbtype)) do
2
+ def test_quoting # FIXME breaks sqlite-ruby to a segfault - research
3
+ @sth = nil
4
+
5
+ assert_nothing_raised do
6
+ if dbtype == "postgresql"
7
+ @sth = @dbh.prepare('select E\'\\\\\'')
8
+ else
9
+ @sth = @dbh.prepare('select \'\\\\\'')
10
+ end
11
+ @sth.execute
12
+ row = @sth.fetch
13
+ assert_equal ['\\'], row
14
+ @sth.finish
15
+ end
16
+ end
17
+
18
+ def test_duplicate_columns
19
+ assert_nothing_raised do
20
+ @sth = @dbh.prepare("select name, name from names where name = ?")
21
+ @sth.execute("Bob")
22
+ assert_equal [["Bob", "Bob"]], @sth.fetch_all
23
+ @sth.finish
24
+ end
25
+ end
26
+
27
+ def test_columninfo
28
+ @sth = nil
29
+
30
+ assert_nothing_raised do
31
+ @sth = @dbh.prepare("select * from precision_test")
32
+ @sth.execute
33
+
34
+ cols = @sth.column_info
35
+
36
+ assert(cols)
37
+ assert_kind_of(Array, cols)
38
+ assert_equal(2, cols.length)
39
+
40
+ # the first column should always be "text_field" and have the following
41
+ # properties:
42
+ assert_equal("text_field", cols[0]["name"])
43
+ assert_equal(20, cols[0]["precision"])
44
+ # scale can be either nil or 0 for character types.
45
+ case cols[0]["scale"]
46
+ when nil
47
+ assert_equal(nil, cols[0]["scale"])
48
+ when 0
49
+ assert_equal(0, cols[0]["scale"])
50
+ else
51
+ flunk "scale can be either 0 or nil for character types"
52
+ end
53
+
54
+ assert_equal(
55
+ DBI::Type::Varchar,
56
+ DBI::TypeUtil.type_name_to_module(cols[0]["type_name"])
57
+ )
58
+
59
+ # the second column should always be "integer_field" and have the following
60
+ # properties:
61
+ assert_equal("integer_field", cols[1]["name"])
62
+ assert_equal(1, cols[1]["scale"])
63
+ assert_equal(2, cols[1]["precision"])
64
+ assert_equal(
65
+ DBI::Type::Decimal,
66
+ DBI::TypeUtil.type_name_to_module(cols[1]["type_name"])
67
+ )
68
+
69
+ cols.each { |col| assert_kind_of(DBI::ColumnInfo, col) }
70
+ @sth.finish
71
+ end
72
+ end
73
+
74
+ def test_duplicate_columns
75
+ assert_nothing_raised do
76
+ @sth = @dbh.prepare("select name, name from names where name = ?")
77
+ @sth.execute("Bob")
78
+ assert_equal [["Bob", "Bob"]], @sth.fetch_all
79
+ @sth.finish
80
+ end
81
+ end
82
+
83
+ def test_rows
84
+ @sth = nil
85
+
86
+ assert_nothing_raised do
87
+ @sth = @dbh.prepare("insert into names (name, age) values (?, ?)")
88
+ @sth.execute("Bill", 22);
89
+ end
90
+
91
+ assert 1, @sth.rows
92
+
93
+ @sth.finish
94
+ @sth = nil
95
+
96
+ assert_nothing_raised do
97
+ @sth = @dbh.prepare("delete from names where name = ?")
98
+ @sth.execute("Bill");
99
+ end
100
+
101
+ assert 1, @sth.rows
102
+
103
+ @sth.finish
104
+
105
+ assert_nothing_raised do
106
+ @sth = @dbh.prepare("select * from names")
107
+ @sth.execute
108
+ end
109
+
110
+ assert_equal 0, @sth.rows
111
+ assert @sth.fetchable?
112
+ assert @sth.any?
113
+ assert @sth.rows.zero?
114
+ @sth.finish
115
+ end
116
+
117
+ def test_execute
118
+ assert_nothing_raised do
119
+ @sth = @dbh.prepare("select * from names")
120
+ @sth.execute
121
+ @sth.finish
122
+ end
123
+
124
+ assert_nothing_raised do
125
+ @sth = @dbh.prepare("select * from names where name = ?")
126
+ @sth.execute("Bob")
127
+ @sth.finish
128
+ end
129
+
130
+ assert_nothing_raised do
131
+ @sth = @dbh.prepare("insert into names (name, age) values (?, ?)")
132
+ @sth.execute("Bill", 22);
133
+ @sth.finish
134
+ end
135
+ end
136
+
137
+ def test_execute_with_transactions
138
+ @dbh["AutoCommit"] = false
139
+ config = DBDConfig.get_config['sqlite3']
140
+
141
+ # rollback 1 (the right way)
142
+ @sth = nil
143
+ @sth2 = nil
144
+
145
+ assert_nothing_raised do
146
+ @sth = @dbh.prepare("insert into names (name, age) values (?, ?)")
147
+ @sth.execute("Billy", 23)
148
+ @sth2 = @dbh.prepare("select * from names where name = ?")
149
+ @sth2.execute("Billy")
150
+ end
151
+ assert_equal ["Billy", 23 ], @sth2.fetch
152
+ @sth2.finish
153
+ @sth.finish
154
+ assert_nothing_raised { @dbh.rollback }
155
+
156
+ @sth = @dbh.prepare("select * from names where name = ?")
157
+ @sth.execute("Billy")
158
+ assert_nil @sth.fetch
159
+ @sth.finish
160
+
161
+ # rollback 2 (without closing statements first)
162
+
163
+ @sth = nil
164
+ @sth2 = nil
165
+
166
+ assert_nothing_raised do
167
+ @sth = @dbh.prepare("insert into names (name, age) values (?, ?)")
168
+ @sth.execute("Billy", 23)
169
+ @sth2 = @dbh.prepare("select * from names where name = ?")
170
+ @sth2.execute("Billy")
171
+ end
172
+
173
+ assert_equal ["Billy", 23], @sth2.fetch
174
+
175
+ # FIXME some throw here, some don't. we should probably normalize this
176
+ @dbh.rollback rescue true
177
+
178
+ @sth2.finish
179
+ @sth.finish
180
+ assert_nothing_raised { @dbh.rollback }
181
+
182
+ @sth = @dbh.prepare("select * from names where name = ?")
183
+ @sth.execute("Billy")
184
+ assert_nil @sth.fetch
185
+ @sth.finish
186
+
187
+ # commit
188
+
189
+ @sth = nil
190
+ @sth2 = nil
191
+
192
+ assert_nothing_raised do
193
+ @sth = @dbh.prepare("insert into names (name, age) values (?, ?)")
194
+ @sth.execute("Billy", 23)
195
+ @sth2 = @dbh.prepare("select * from names where name = ?")
196
+ @sth2.execute("Billy")
197
+ end
198
+ assert_equal ["Billy", 23 ], @sth2.fetch
199
+ @sth2.finish
200
+ @sth.finish
201
+ assert_nothing_raised { @dbh.commit }
202
+
203
+ @sth = @dbh.prepare("select * from names where name = ?")
204
+ @sth.execute("Billy")
205
+ assert_equal ["Billy", 23 ], @sth.fetch
206
+ @sth.finish
207
+ end
208
+
209
+ def test_fetch
210
+ @sth = nil
211
+ assert_nothing_raised do
212
+ @sth = @dbh.prepare("select * from names order by age")
213
+ @sth.execute
214
+ end
215
+
216
+ # this tests that we're getting the rows in the right order,
217
+ # and that the types are being converted.
218
+ assert_equal ["Joe", 19], @sth.fetch
219
+ assert_equal ["Bob", 21], @sth.fetch
220
+ assert_equal ["Jim", 30], @sth.fetch
221
+ assert_nil @sth.fetch
222
+
223
+ @sth.finish
224
+ end
225
+
226
+ def test_transaction_block
227
+ @dbh["AutoCommit"] = false
228
+ # this transaction should not fail because it called return early
229
+ @dbh.transaction do |dbh|
230
+ dbh.do('INSERT INTO names (name, age) VALUES (?, ?)', "Cooter", 69)
231
+ return 42
232
+ end
233
+ @sth = @dbh.prepare("select * from names where name = ?")
234
+ @sth.execute("Cooter")
235
+ row = @sth.fetch
236
+ assert row
237
+ assert_equal ["Cooter", 69], row
238
+ @sth.finish
239
+ end
240
+ end