dbd-sqlite 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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