do_sqlite3 0.2.5 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
data/ext/extconf.rb CHANGED
@@ -1,11 +1,35 @@
1
- ENV["RC_ARCHS"] = `uname -m`.chomp if `uname -sr` =~ /^Darwin/
1
+ # ENV["RC_ARCHS"] = `uname -m`.chomp if `uname -sr` =~ /^Darwin/
2
+ #
3
+ # require 'mkmf'
4
+ #
5
+ # SWIG_WRAP = "sqlite3_api_wrap.c"
6
+ #
7
+ # dir_config( "sqlite3", "/usr/local" )
8
+ #
9
+ # if have_header( "sqlite3.h" ) && have_library( "sqlite3", "sqlite3_open" )
10
+ # create_makefile( "sqlite3_c" )
11
+ # end
2
12
 
13
+ if RUBY_PLATFORM =~ /darwin/
14
+ ENV["RC_ARCHS"] = `uname -m`.chomp if `uname -sr` =~ /^Darwin/
15
+
16
+ # On PowerPC the defaults are fine
17
+ ENV["RC_ARCHS"] = '' if `uname -m` =~ /^Power Macintosh/
18
+ end
19
+
20
+ # Loads mkmf which is used to make makefiles for Ruby extensions
3
21
  require 'mkmf'
4
22
 
5
- SWIG_WRAP = "sqlite3_api_wrap.c"
23
+ # Give it a name
24
+ extension_name = 'do_sqlite3_ext'
25
+
26
+ dir_config("sqlite3")
6
27
 
7
- dir_config( "sqlite3", "/usr/local" )
28
+ # NOTE: use GCC flags unless Visual C compiler is used
29
+ $CFLAGS << ' -Wall ' unless RUBY_PLATFORM =~ /mswin/
8
30
 
31
+ # Do the work
32
+ # create_makefile(extension_name)
9
33
  if have_header( "sqlite3.h" ) && have_library( "sqlite3", "sqlite3_open" )
10
- create_makefile( "sqlite3_c" )
34
+ create_makefile(extension_name)
11
35
  end
data/lib/do_sqlite3.rb CHANGED
@@ -1,210 +1,5 @@
1
- require 'sqlite3_c'
2
- require 'data_objects'
3
-
4
- module DataObject
5
- module Sqlite3
6
-
7
- QUOTE_STRING = "\""
8
- QUOTE_COLUMN = "'"
9
-
10
- class Connection < DataObject::Connection
11
-
12
- attr_reader :db
13
-
14
- def initialize(connection_string)
15
- @state = STATE_CLOSED
16
- @connection_string = connection_string
17
- @conn = Hash[*connection_string.split(" ").map {|x| x.split("=")}.flatten]["dbname"]
18
- end
19
-
20
- def open
21
- r, d = Sqlite3_c.sqlite3_open(@conn)
22
- unless r == Sqlite3_c::SQLITE_OK
23
- raise ConnectionFailed, "Unable to connect to database with provided connection string. \n#{Sqlite3_c.sqlite3_errmsg(d)}"
24
- else
25
- @db = d
26
- end
27
- @state = STATE_OPEN
28
- true
29
- end
30
-
31
- def close
32
- Sqlite3_c.sqlite3_close(@db)
33
- @state = STATE_CLOSED
34
- true
35
- end
36
-
37
- def create_command(text)
38
- Command.new(self, text)
39
- end
40
-
41
- def begin_transaction
42
- Transaction.new(self)
43
- end
44
-
45
- end
46
-
47
- class Transaction < DataObject::Transaction
48
-
49
- attr_reader :connection
50
-
51
- def initialize(conn)
52
- @connection = conn
53
- exec_sql("BEGIN")
54
- end
55
1
 
56
- # Commits the transaction
57
- def commit
58
- exec_sql("COMMIT")
59
- end
60
-
61
- # Rolls back the transaction
62
- def rollback(savepoint = nil)
63
- raise NotImplementedError, "SQLite3 does not support savepoints" if savepoint
64
- exec_sql("ROLLBACK")
65
- end
66
-
67
- # Creates a savepoint for rolling back later (not commonly supported)
68
- def save(name)
69
- raise NotImplementedError, "SQLite3 does not support savepoints"
70
- end
71
-
72
- def create_command(*args)
73
- @connection.create_command(*args)
74
- end
75
-
76
- protected
77
-
78
- def exec_sql(sql)
79
- @connection.logger.debug { sql }
80
- result, reader = Sqlite3_c.sqlite3_prepare_v2(@connection.db, sql, sql.size + 1)
81
- exec_result = Sqlite3_c.sqlite3_step(reader)
82
- Sqlite3_c.sqlite3_finalize(reader)
83
- exec_result
84
- end
85
-
86
- end
87
-
88
- class Reader < DataObject::Reader
89
-
90
- def initialize(db, reader)
91
- @reader = reader
92
- result = Sqlite3_c.sqlite3_step(reader)
93
- rows_affected, field_count = Sqlite3_c.sqlite3_changes(db), Sqlite3_c.sqlite3_column_count(reader)
94
- if field_count == 0
95
- @records_affected = rows_affected
96
- close
97
- else
98
- @field_count = field_count
99
- @fields, @field_types = [], []
100
- i = 0
101
- while(i < @field_count)
102
- @field_types.push(Sqlite3_c.sqlite3_column_type(reader, i))
103
- @fields.push(Sqlite3_c.sqlite3_column_name(reader, i))
104
- i += 1
105
- end
106
- case result
107
- when Sqlite3_c::SQLITE_BUSY, Sqlite3_c::SQLITE_ERROR, Sqlite3_c::SQLITE_MISUSE
108
- raise ReaderError, "An error occurred while trying to get the next row\n#{Sqlite3_c.sqlite3_errmsg(db)}"
109
- else
110
- @has_rows = result == Sqlite3_c::SQLITE_ROW
111
- @state = STATE_OPEN
112
- close unless @has_rows
113
- end
114
- end
115
- end
116
-
117
- def real_close
118
- Sqlite3_c.sqlite3_finalize(@reader)
119
- end
120
-
121
- def name(idx)
122
- super
123
- @fields[idx]
124
- end
125
-
126
- def get_index(name)
127
- super
128
- @fields.index(name)
129
- end
130
-
131
- def null?(idx)
132
- super
133
- item(idx).nil?
134
- end
135
-
136
- def item(idx)
137
- super
138
- case @field_types[idx]
139
- when 1 # SQLITE_INTEGER
140
- Sqlite3_c.sqlite3_column_int(@reader, idx).to_i
141
- when 2 # SQLITE_FLOAT
142
- Sqlite3_c.sqlite3_column_double(@reader, idx)
143
- else
144
- Sqlite3_c.sqlite3_column_text(@reader, idx)
145
- end
146
- end
147
-
148
- def each
149
- return unless has_rows?
150
-
151
- while(true) do
152
- yield
153
- break unless Sqlite3_c.sqlite3_step(@reader) == Sqlite3_c::SQLITE_ROW
154
- end
155
- end
156
-
157
- end
158
-
159
- class Command < DataObject::Command
160
-
161
- def execute_reader(*args)
162
- super
163
- sql = escape_sql(args)
164
- @connection.logger.debug { sql }
165
- result, ptr = Sqlite3_c.sqlite3_prepare_v2(@connection.db, sql, sql.size + 1)
166
- unless result == Sqlite3_c::SQLITE_OK
167
- raise QueryError, "Your query failed.\n#{Sqlite3_c.sqlite3_errmsg(@connection.db)}\nQUERY: \"#{sql}\""
168
- else
169
- reader = Reader.new(@connection.db, ptr)
170
-
171
- if block_given?
172
- return_value = yield(reader)
173
- reader.close
174
- return_value
175
- else
176
- reader
177
- end
178
- end
179
- end
180
-
181
- def execute_non_query(*args)
182
- super
183
- sql = escape_sql(args)
184
- @connection.logger.debug { sql }
185
- result, reader = Sqlite3_c.sqlite3_prepare_v2(@connection.db, sql, -1)
186
- unless result == Sqlite3_c::SQLITE_OK
187
- Sqlite3_c.sqlite3_finalize(reader)
188
- raise QueryError, "Your query failed.\n#{Sqlite3_c.sqlite3_errmsg(@connection.db)}\nQUERY: \"#{sql}\""
189
- else
190
- exec_result = Sqlite3_c.sqlite3_step(reader)
191
- Sqlite3_c.sqlite3_finalize(reader)
192
- if exec_result == Sqlite3_c::SQLITE_DONE
193
- ResultData.new(@connection, Sqlite3_c.sqlite3_changes(@connection.db), Sqlite3_c.sqlite3_last_insert_rowid(@connection.db))
194
- else
195
- raise QueryError, "Your query failed or you tried to execute a SELECT query through execute_non_reader\n#{Sqlite3_c.sqlite3_errmsg(@connection.db)}\nQUERY: \"#{@text}\""
196
- end
197
- end
198
- end
199
-
200
- def quote_symbol(value)
201
- value.to_s
202
- end
203
-
204
- def quote_boolean(value)
205
- value ? '1' : '0'
206
- end
207
- end
208
-
209
- end
210
- end
2
+ require 'rubygems'
3
+ require 'data_objects'
4
+ require 'do_sqlite3_ext'
5
+ require 'do_sqlite3/transaction'
@@ -0,0 +1,36 @@
1
+
2
+ module DataObjects
3
+
4
+ module Sqlite3
5
+
6
+ class Transaction < DataObjects::Transaction
7
+
8
+ def begin
9
+ cmd = "BEGIN"
10
+ connection.create_command(cmd).execute_non_query
11
+ end
12
+
13
+ def commit
14
+ cmd = "COMMIT"
15
+ connection.create_command(cmd).execute_non_query
16
+ end
17
+
18
+ def rollback
19
+ cmd = "ROLLBACK"
20
+ connection.create_command(cmd).execute_non_query
21
+ end
22
+
23
+ def rollback_prepared
24
+ cmd = "ROLLBACK"
25
+ connection.create_command(cmd).execute_non_query
26
+ end
27
+
28
+ def prepare
29
+ # Eek, I don't know how to do this. Lets hope a commit arrives soon...
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,244 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ describe "DataObjects::Sqlite3" do
5
+ include Sqlite3SpecHelpers
6
+
7
+ it "should raise error on bad connection string" do
8
+ lambda { DataObjects::Connection.new("sqlite3:///ac0d9iopalmsdcasd/asdc9pomasd/test.db") }.should raise_error("unable to open database file")
9
+ end
10
+ end
11
+
12
+ NOW = DateTime.now
13
+
14
+ describe "DataObjects::Sqlite3::Result" do
15
+ include Sqlite3SpecHelpers
16
+
17
+ before(:all) do
18
+ @connection = DataObjects::Connection.new("sqlite3://#{File.expand_path(File.dirname(__FILE__))}/test.db")
19
+ end
20
+
21
+ it "should raise an error for a bad query" do
22
+ command = @connection.create_command("INSER INTO table_which_doesnt_exist (id) VALUES (1)")
23
+ lambda { command.execute_non_query }.should raise_error('near "INSER": syntax error')
24
+
25
+ command = @connection.create_command("INSERT INTO table_which_doesnt_exist (id) VALUES (1)")
26
+ lambda { command.execute_non_query }.should raise_error("no such table: table_which_doesnt_exist")
27
+
28
+ command = @connection.create_command("SELECT * FROM table_which_doesnt_exist")
29
+ lambda { command.execute_reader }.should raise_error("no such table: table_which_doesnt_exist")
30
+ end
31
+
32
+ it "should return the affected rows and insert_id" do
33
+ command = @connection.create_command("DROP TABLE users")
34
+ command.execute_non_query rescue nil
35
+ command = @connection.create_command("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, type TEXT, age INTEGER, created_at DATETIME, balance DECIMAL default '0.00')")
36
+ result = command.execute_non_query
37
+ command = @connection.create_command("INSERT INTO users (name) VALUES ('test')")
38
+ result = command.execute_non_query
39
+ result.insert_id.should == 1
40
+ result.to_i.should == 1
41
+ end
42
+
43
+ it "should do a reader query" do
44
+ command = @connection.create_command("SELECT * FROM users")
45
+ reader = command.execute_reader
46
+
47
+ lambda { reader.values }.should raise_error
48
+
49
+ while ( reader.next! )
50
+ lambda { reader.values }.should_not raise_error
51
+ reader.values.should be_a_kind_of(Array)
52
+ end
53
+
54
+ lambda { reader.values }.should raise_error
55
+
56
+ reader.close
57
+ end
58
+
59
+ it "should do a paramaterized reader query" do
60
+ command = @connection.create_command("SELECT * FROM users WHERE id = ?")
61
+ reader = command.execute_reader(1)
62
+ reader.next!
63
+
64
+ reader.values[0].should == 1
65
+
66
+ reader.next!
67
+
68
+ lambda { reader.values }.should raise_error
69
+
70
+ reader.close
71
+ end
72
+
73
+ it "should do a custom typecast reader" do
74
+ command = @connection.create_command("SELECT name, id FROM users")
75
+ command.set_types [String, String]
76
+ reader = command.execute_reader
77
+
78
+ while ( reader.next! )
79
+ reader.fields.should == ["name", "id"]
80
+ reader.values.each { |v| v.should be_a_kind_of(String) }
81
+ end
82
+
83
+ reader.close
84
+
85
+ end
86
+
87
+ it "should handle a null value" do
88
+ id = insert("INSERT INTO users (name) VALUES (NULL)")
89
+ select("SELECT name from users WHERE name is null") do |reader|
90
+ reader.values[0].should == nil
91
+ end
92
+ end
93
+
94
+ it "should not convert empty strings to null" do
95
+ id = insert("INSERT INTO users (name) VALUES ('')")
96
+ select("SELECT name FROM users WHERE id = ?", [String], id) do |reader|
97
+ reader.values.first.should == ''
98
+ end
99
+ end
100
+
101
+ it "should raise an error when you pass too many or too few types for the expected result set" do
102
+ lambda { select("SELECT name, id FROM users", [String, Integer, String]) }.should raise_error(Sqlite3Error)
103
+ end
104
+
105
+ it "should do a custom typecast reader with Class" do
106
+ class Person; end
107
+
108
+ id = insert("INSERT INTO users (name, age, type) VALUES (?, ?, ?)", 'Sam', 30, Person)
109
+
110
+ select("SELECT name, age, type FROM users WHERE id = ?", [String, Integer, Class], id) do |reader|
111
+ reader.fields.should == ["name", "age", "type"]
112
+ reader.values.should == ["Sam", 30, Person]
113
+ end
114
+
115
+ exec("DELETE FROM users WHERE id = ?", id)
116
+ end
117
+
118
+ [
119
+ NOW.strftime('%Y-%m-%dT%H:%M:%S'),
120
+ NOW.strftime('%Y-%m-%d %H:%M:%S')
121
+ ].each do |raw_value|
122
+ it "should return #{NOW.to_s} using the LOCAL timezone when typecasting '#{raw_value}'" do
123
+
124
+ # Insert a timezone-less DateTime into the DB
125
+ id = insert("INSERT INTO users (name, age, type, created_at) VALUES (?, ?, ?, ?)", 'Sam', 30, Person, raw_value)
126
+
127
+ select("SELECT created_at FROM users WHERE id = ?", [DateTime], id) do |reader|
128
+ reader.values.last.to_s.should == NOW.to_s
129
+ end
130
+
131
+ exec("DELETE FROM users WHERE id = ?", id)
132
+ end
133
+ end
134
+
135
+ it "should return DateTimes using the same timezone that was used to insert it" do
136
+ pending "improved support for timezone checking"
137
+
138
+ dates = [
139
+ NOW,
140
+ NOW.new_offset( (-11 * 3600).to_r / 86400), # GMT -11:00
141
+ NOW.new_offset( (-9 * 3600 + 10 * 60).to_r / 86400), # GMT -9:10
142
+ NOW.new_offset( (-8 * 3600).to_r / 86400), # GMT -08:00
143
+ NOW.new_offset( (+3 * 3600).to_r / 86400), # GMT +03:00
144
+ NOW.new_offset( (+5 * 3600 + 30 * 60).to_r / 86400) # GMT +05:30 (New Delhi)
145
+ ]
146
+
147
+ dates.each do |date|
148
+ id = insert("INSERT INTO users (name, age, type, created_at) VALUES (?, ?, ?, ?)", 'Sam', 30, Person, date)
149
+
150
+ select("SELECT created_at FROM users WHERE id = ?", [DateTime], id) do |reader|
151
+ reader.values.last.year.should == date.year
152
+ reader.values.last.month.should == date.month
153
+ reader.values.last.day.should == date.day
154
+ reader.values.last.hour.should == date.hour
155
+ reader.values.last.min.should == date.min
156
+ reader.values.last.sec.should == date.sec
157
+ reader.values.last.zone.should == date.zone
158
+ end
159
+
160
+ exec("DELETE FROM users WHERE id = ?", id)
161
+ end
162
+ end
163
+
164
+ it "should return a BigDecimal" do
165
+ balance = BigDecimal.new('10000000000.00')
166
+
167
+ id = insert("INSERT INTO users (name, age, type, created_at, balance) VALUES (?, ?, ?, ?, ?)", 'Scott', 27, Person, DateTime.now, balance)
168
+
169
+ select("SELECT balance FROM users WHERE id = ?", [BigDecimal], id) do |reader|
170
+ reader.values.last.should == balance
171
+ end
172
+ end
173
+
174
+ describe "quoting" do
175
+
176
+ before do
177
+ @connection.create_command("DROP TABLE IF EXISTS sail_boats").execute_non_query
178
+ @connection.create_command("CREATE TABLE sail_boats ( id INTEGER PRIMARY KEY, name VARCHAR(50), port VARCHAR(50), notes VARCHAR(50), vintage BOOLEAN )").execute_non_query
179
+ command = @connection.create_command("INSERT INTO sail_boats (id, name, port, name, vintage) VALUES (?, ?, ?, ?, ?)")
180
+ command.execute_non_query(1, "A", "C", "Fortune Pig!", false)
181
+ command.execute_non_query(2, "B", "B", "Happy Cow!", true)
182
+ command.execute_non_query(3, "C", "A", "Spoon", true)
183
+ end
184
+
185
+ it "should quote a String" do
186
+ command = @connection.create_command("INSERT INTO users (name) VALUES (?)")
187
+ result = command.execute_non_query("John Doe")
188
+ result.to_i.should == 1
189
+ end
190
+
191
+ it "should quote multiple values" do
192
+ command = @connection.create_command("INSERT INTO users (name, age) VALUES (?, ?)")
193
+ result = command.execute_non_query("Sam Smoot", 1)
194
+ result.to_i.should == 1
195
+ end
196
+
197
+
198
+ it "should handle boolean columns gracefully" do
199
+ command = @connection.create_command("INSERT INTO sail_boats (id, name, port, name, vintage) VALUES (?, ?, ?, ?, ?)")
200
+ result = command.execute_non_query(4, "Scooner", "Port au Prince", "This is one gangster boat!", true)
201
+ result.to_i.should == 1
202
+ end
203
+
204
+ it "should quote an Array" do
205
+ command = @connection.create_command("SELECT id, notes FROM sail_boats WHERE (id IN ?)")
206
+ reader = command.execute_reader([1, 2, 3])
207
+
208
+ i = 1
209
+ while(reader.next!)
210
+ reader.values[0].should == i
211
+ i += 1
212
+ end
213
+ end
214
+
215
+ it "should quote an Array with NULL values returned" do
216
+ command = @connection.create_command("SELECT id, NULL AS notes FROM sail_boats WHERE (id IN ?)")
217
+ reader = command.execute_reader([1, 2, 3])
218
+
219
+ i = 1
220
+ while(reader.next!)
221
+ reader.values[0].should == i
222
+ i += 1
223
+ end
224
+ end
225
+
226
+ it "should quote an Array with NULL values returned AND set_types called" do
227
+ command = @connection.create_command("SELECT id, NULL AS notes FROM sail_boats WHERE (id IN ?)")
228
+ command.set_types [ Integer, String ]
229
+
230
+ reader = command.execute_reader([1, 2, 3])
231
+
232
+ i = 1
233
+ while(reader.next!)
234
+ reader.values[0].should == i
235
+ i += 1
236
+ end
237
+ end
238
+
239
+ after do
240
+ @connection.create_command("DROP TABLE sail_boats").execute_non_query
241
+ end
242
+
243
+ end # describe "quoting"
244
+ end