do_sqlite3 0.2.5 → 0.9.2

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.
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