sqlite3 0.1.1 → 1.3.3.beta.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.
Files changed (55) hide show
  1. data/API_CHANGES.rdoc +50 -0
  2. data/CHANGELOG.rdoc +177 -0
  3. data/ChangeLog.cvs +88 -0
  4. data/LICENSE +24 -17
  5. data/Manifest.txt +50 -0
  6. data/README.rdoc +100 -4
  7. data/Rakefile +10 -0
  8. data/ext/sqlite3/backup.c +164 -0
  9. data/ext/sqlite3/backup.h +15 -0
  10. data/ext/sqlite3/database.c +762 -0
  11. data/ext/sqlite3/database.h +15 -0
  12. data/ext/sqlite3/exception.c +94 -0
  13. data/ext/sqlite3/exception.h +8 -0
  14. data/ext/sqlite3/extconf.rb +47 -0
  15. data/ext/sqlite3/sqlite3.c +36 -0
  16. data/ext/sqlite3/sqlite3_ruby.h +44 -0
  17. data/ext/sqlite3/statement.c +419 -0
  18. data/ext/sqlite3/statement.h +16 -0
  19. data/faq/faq.rb +145 -0
  20. data/faq/faq.yml +426 -0
  21. data/lib/sqlite3.rb +9 -12
  22. data/lib/sqlite3/constants.rb +46 -72
  23. data/lib/sqlite3/database.rb +415 -140
  24. data/lib/sqlite3/errors.rb +1 -56
  25. data/lib/sqlite3/pragmas.rb +246 -63
  26. data/lib/sqlite3/resultset.rb +30 -112
  27. data/lib/sqlite3/statement.rb +42 -108
  28. data/lib/sqlite3/translator.rb +118 -0
  29. data/lib/sqlite3/value.rb +57 -0
  30. data/lib/sqlite3/version.rb +23 -1
  31. data/setup.rb +1333 -0
  32. data/tasks/faq.rake +9 -0
  33. data/tasks/gem.rake +31 -0
  34. data/tasks/native.rake +61 -0
  35. data/tasks/vendor_sqlite3.rake +104 -0
  36. data/test/helper.rb +3 -0
  37. data/test/test_backup.rb +33 -0
  38. data/test/test_collation.rb +82 -0
  39. data/test/test_database.rb +312 -0
  40. data/test/test_database_readonly.rb +29 -0
  41. data/test/test_deprecated.rb +37 -0
  42. data/test/test_encoding.rb +119 -0
  43. data/test/test_integration.rb +544 -0
  44. data/test/test_integration_open_close.rb +30 -0
  45. data/test/test_integration_pending.rb +115 -0
  46. data/test/test_integration_resultset.rb +156 -0
  47. data/test/test_integration_statement.rb +194 -0
  48. data/test/test_sqlite3.rb +9 -0
  49. data/test/test_statement.rb +207 -0
  50. data/test/test_statement_execute.rb +35 -0
  51. metadata +124 -67
  52. data/lib/sqlite3/api.rb +0 -52
  53. data/lib/sqlite3/driver.rb +0 -138
  54. data/lib/sqlite3/encoding.rb +0 -43
  55. data/lib/sqlite3/extensions.rb +0 -27
@@ -1,13 +1,10 @@
1
- require "ffi"
1
+ # support multiple ruby version (fat binaries under windows)
2
+ begin
3
+ RUBY_VERSION =~ /(\d+.\d+)/
4
+ require "sqlite3/#{$1}/sqlite3_native"
5
+ rescue LoadError
6
+ require 'sqlite3/sqlite3_native'
7
+ end
2
8
 
3
- require "sqlite3/api"
4
- require "sqlite3/driver"
5
- require "sqlite3/constants"
6
- require "sqlite3/extensions"
7
- require "sqlite3/errors"
8
- require "sqlite3/pragmas"
9
- require "sqlite3/statement"
10
- require "sqlite3/resultset"
11
- require "sqlite3/encoding"
12
- require "sqlite3/database"
13
- require "sqlite3/version"
9
+ require 'sqlite3/database'
10
+ require 'sqlite3/version'
@@ -1,75 +1,49 @@
1
- # Copyright (c) 2004, Jamis Buck (jamis@jamisbuck.org)
2
- # All rights reserved.
3
-
4
- # Redistribution and use in source and binary forms, with or without
5
- # modification, are permitted provided that the following conditions
6
- # are met:
7
-
8
- # * Redistributions of source code must retain the above copyright
9
- # notice, this list of conditions and the following disclaimer.
10
-
11
- # * Redistributions in binary form must reproduce the above copyright
12
- # notice, this list of conditions and the following disclaimer in
13
- # the documentation and/or other materials provided with the
14
- # distribution.
15
-
16
- # * The names of its contributors may not be used to endorse or
17
- # promote products derived from this software without specific prior
18
- # written permission.
19
-
20
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23
- # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24
- # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25
- # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
- # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
- # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30
- # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
- # POSSIBILITY OF SUCH DAMAGE.
32
-
33
- module SQLite3
34
- module Constants
35
-
36
- module ColumnType
37
- INTEGER = 1
38
- FLOAT = 2
39
- TEXT = 3
40
- BLOB = 4
41
- NULL = 5
42
- end
43
-
44
- module ErrorCode
45
- OK = 0 # Successful result
46
- ERROR = 1 # SQL error or missing database
47
- INTERNAL = 2 # An internal logic error in SQLite
48
- PERM = 3 # Access permission denied
49
- ABORT = 4 # Callback routine requested an abort
50
- BUSY = 5 # The database file is locked
51
- LOCKED = 6 # A table in the database is locked
52
- NOMEM = 7 # A malloc() failed
53
- READONLY = 8 # Attempt to write a readonly database
54
- INTERRUPT = 9 # Operation terminated by sqlite_interrupt()
55
- IOERR = 10 # Some kind of disk I/O error occurred
56
- CORRUPT = 11 # The database disk image is malformed
57
- NOTFOUND = 12 # (Internal Only) Table or record not found
58
- FULL = 13 # Insertion failed because database is full
59
- CANTOPEN = 14 # Unable to open the database file
60
- PROTOCOL = 15 # Database lock protocol error
61
- EMPTY = 16 # (Internal Only) Database table is empty
62
- SCHEMA = 17 # The database schema changed
63
- TOOBIG = 18 # Too much data for one row of a table
64
- CONSTRAINT = 19 # Abort due to contraint violation
65
- MISMATCH = 20 # Data type mismatch
66
- MISUSE = 21 # Library used incorrectly
67
- NOLFS = 22 # Uses OS features not supported on host
68
- AUTH = 23 # Authorization denied
1
+ module SQLite3 ; module Constants
2
+
3
+ module TextRep
4
+ UTF8 = 1
5
+ UTF16LE = 2
6
+ UTF16BE = 3
7
+ UTF16 = 4
8
+ ANY = 5
9
+ end
69
10
 
70
- ROW = 100 # sqlite_step() has another row ready
71
- DONE = 101 # sqlite_step() has finished executing
72
- end
11
+ module ColumnType
12
+ INTEGER = 1
13
+ FLOAT = 2
14
+ TEXT = 3
15
+ BLOB = 4
16
+ NULL = 5
17
+ end
73
18
 
19
+ module ErrorCode
20
+ OK = 0 # Successful result
21
+ ERROR = 1 # SQL error or missing database
22
+ INTERNAL = 2 # An internal logic error in SQLite
23
+ PERM = 3 # Access permission denied
24
+ ABORT = 4 # Callback routine requested an abort
25
+ BUSY = 5 # The database file is locked
26
+ LOCKED = 6 # A table in the database is locked
27
+ NOMEM = 7 # A malloc() failed
28
+ READONLY = 8 # Attempt to write a readonly database
29
+ INTERRUPT = 9 # Operation terminated by sqlite_interrupt()
30
+ IOERR = 10 # Some kind of disk I/O error occurred
31
+ CORRUPT = 11 # The database disk image is malformed
32
+ NOTFOUND = 12 # (Internal Only) Table or record not found
33
+ FULL = 13 # Insertion failed because database is full
34
+ CANTOPEN = 14 # Unable to open the database file
35
+ PROTOCOL = 15 # Database lock protocol error
36
+ EMPTY = 16 # (Internal Only) Database table is empty
37
+ SCHEMA = 17 # The database schema changed
38
+ TOOBIG = 18 # Too much data for one row of a table
39
+ CONSTRAINT = 19 # Abort due to contraint violation
40
+ MISMATCH = 20 # Data type mismatch
41
+ MISUSE = 21 # Library used incorrectly
42
+ NOLFS = 22 # Uses OS features not supported on host
43
+ AUTH = 23 # Authorization denied
44
+
45
+ ROW = 100 # sqlite_step() has another row ready
46
+ DONE = 101 # sqlite_step() has finished executing
74
47
  end
75
- end
48
+
49
+ end ; end
@@ -1,50 +1,23 @@
1
- # Copyright (c) 2004, Jamis Buck (jamis@jamisbuck.org)
2
- # All rights reserved.
3
-
4
- # Redistribution and use in source and binary forms, with or without
5
- # modification, are permitted provided that the following conditions
6
- # are met:
7
-
8
- # * Redistributions of source code must retain the above copyright
9
- # notice, this list of conditions and the following disclaimer.
10
-
11
- # * Redistributions in binary form must reproduce the above copyright
12
- # notice, this list of conditions and the following disclaimer in
13
- # the documentation and/or other materials provided with the
14
- # distribution.
15
-
16
- # * The names of its contributors may not be used to endorse or
17
- # promote products derived from this software without specific prior
18
- # written permission.
19
-
20
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23
- # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24
- # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25
- # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
- # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
- # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30
- # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
- # POSSIBILITY OF SUCH DAMAGE.
1
+ require 'sqlite3/constants'
2
+ require 'sqlite3/errors'
3
+ require 'sqlite3/pragmas'
4
+ require 'sqlite3/statement'
5
+ require 'sqlite3/translator'
6
+ require 'sqlite3/value'
32
7
 
33
8
  module SQLite3
34
9
 
35
10
  # The Database class encapsulates a single connection to a SQLite3 database.
36
11
  # Its usage is very straightforward:
37
12
  #
38
- # require "sqlite3"
13
+ # require 'sqlite3'
39
14
  #
40
- # db = SQLite3::Database.new("data.db")
41
- #
42
- # db.execute("select * from table") do |row|
43
- # p row
15
+ # SQLite3::Database.new( "data.db" ) do |db|
16
+ # db.execute( "select * from table" ) do |row|
17
+ # p row
18
+ # end
44
19
  # end
45
20
  #
46
- # db.close
47
- #
48
21
  # It wraps the lower-level methods provides by the selected driver, and
49
22
  # includes the Pragmas module for access to various pragma convenience
50
23
  # methods.
@@ -60,8 +33,9 @@ module SQLite3
60
33
  # module before performing a query, and if you have not enabled results as
61
34
  # hashes, then the results will all be indexible by field name.
62
35
  class Database
36
+ attr_reader :collations
37
+
63
38
  include Pragmas
64
- include Extensions
65
39
 
66
40
  class << self
67
41
 
@@ -70,74 +44,42 @@ module SQLite3
70
44
  # Quotes the given string, making it safe to use in an SQL statement.
71
45
  # It replaces all instances of the single-quote character with two
72
46
  # single-quote characters. The modified string is returned.
73
- def quote(string)
74
- string.gsub(/'/, "''")
47
+ def quote( string )
48
+ string.gsub( /'/, "''" )
75
49
  end
76
50
 
77
51
  end
78
52
 
79
- # The low-level opaque database handle that this object wraps.
80
- attr_reader :handle
81
-
82
- # A reference to the underlying SQLite3 driver used by this database.
83
- attr_reader :driver
84
-
85
53
  # A boolean that indicates whether rows in result sets should be returned
86
54
  # as hashes or not. By default, rows are returned as arrays.
87
55
  attr_accessor :results_as_hash
88
56
 
89
- # Encoding used to comunicate with database.
90
- attr_reader :encoding
91
-
92
- # Create a new Database object that opens the given file. If utf16
93
- # is +true+, the filename is interpreted as a UTF-16 encoded string.
94
- #
95
- # By default, the new database will return result rows as arrays
96
- # (#results_as_hash) and has type translation disabled (#type_translation=).
97
- def initialize(file_name, options = {})
98
- @encoding = Encoding.find(options.fetch(:encoding, "utf-8"))
99
-
100
- @driver = Driver.new
101
-
102
- @statement_factory = options[:statement_factory] || Statement
103
-
104
- result, @handle = @driver.open(file_name, Encoding.utf_16?(@encoding))
105
- Error.check(result, self, "could not open database")
106
-
107
- @closed = false
108
- @results_as_hash = options.fetch(:results_as_hash, false)
109
- end
110
-
111
- # Return +true+ if the string is a valid (ie, parsable) SQL statement, and
112
- # +false+ otherwise
113
- def complete?(string)
114
- @driver.complete?(string)
115
- end
116
-
117
- # Return a string describing the last error to have occurred with this
118
- # database.
119
- def errmsg
120
- @driver.errmsg(@handle)
57
+ def type_translation= value # :nodoc:
58
+ warn(<<-eowarn) if $VERBOSE
59
+ #{caller[0]} is calling SQLite3::Database#type_translation=
60
+ SQLite3::Database#type_translation= is deprecated and will be removed
61
+ in version 2.0.0.
62
+ eowarn
63
+ @type_translation = value
121
64
  end
122
-
123
- # Return an integer representing the last error to have occurred with this
124
- # database.
125
- def errcode
126
- @driver.errcode(@handle)
127
- end
128
-
129
- # Closes this database.
130
- def close
131
- unless @closed
132
- result = @driver.close(@handle)
133
- Error.check(result, self)
134
- end
135
- @closed = true
65
+ attr_reader :type_translation # :nodoc:
66
+
67
+ # Return the type translator employed by this database instance. Each
68
+ # database instance has its own type translator; this allows for different
69
+ # type handlers to be installed in each instance without affecting other
70
+ # instances. Furthermore, the translators are instantiated lazily, so that
71
+ # if a database does not use type translation, it will not be burdened by
72
+ # the overhead of a useless type translator. (See the Translator class.)
73
+ def translator
74
+ @translator ||= Translator.new
136
75
  end
137
76
 
138
- # Returns +true+ if this database instance has been closed (see #close).
139
- def closed?
140
- @closed
77
+ # Installs (or removes) a block that will be invoked for every access
78
+ # to the database. If the block returns 0 (or +nil+), the statement
79
+ # is allowed to proceed. Returning 1 causes an authorization error to
80
+ # occur, and returning 2 causes the access to be silently denied.
81
+ def authorizer( &block )
82
+ self.authorizer = block
141
83
  end
142
84
 
143
85
  # Returns a Statement object representing the given SQL. This does not
@@ -145,16 +87,14 @@ module SQLite3
145
87
  #
146
88
  # The Statement can then be executed using Statement#execute.
147
89
  #
148
- def prepare(sql)
149
- stmt = @statement_factory.new(self, sql, Encoding.utf_16?(@encoding))
150
- if block_given?
151
- begin
152
- yield stmt
153
- ensure
154
- stmt.close
155
- end
156
- else
157
- return stmt
90
+ def prepare sql
91
+ stmt = SQLite3::Statement.new( self, sql )
92
+ return stmt unless block_given?
93
+
94
+ begin
95
+ yield stmt
96
+ ensure
97
+ stmt.close
158
98
  end
159
99
  end
160
100
 
@@ -172,13 +112,51 @@ module SQLite3
172
112
  #
173
113
  # See also #execute2, #query, and #execute_batch for additional ways of
174
114
  # executing statements.
175
- def execute(sql, *bind_vars)
176
- prepare(sql) do |stmt|
177
- result = stmt.execute(*bind_vars)
115
+ def execute sql, bind_vars = [], *args, &block
116
+ # FIXME: This is a terrible hack and should be removed but is required
117
+ # for older versions of rails
118
+ hack = Object.const_defined?(:ActiveRecord) && sql =~ /^PRAGMA index_list/
119
+
120
+ if bind_vars.nil? || !args.empty?
121
+ if args.empty?
122
+ bind_vars = []
123
+ else
124
+ bind_vars = [bind_vars] + args
125
+ end
126
+
127
+ warn(<<-eowarn) if $VERBOSE
128
+ #{caller[0]} is calling SQLite3::Database#execute with nil or multiple bind params
129
+ without using an array. Please switch to passing bind parameters as an array.
130
+ Support for bind parameters as *args will be removed in 2.0.0.
131
+ eowarn
132
+ end
133
+
134
+ prepare( sql ) do |stmt|
135
+ stmt.bind_params(bind_vars)
136
+ columns = stmt.columns
137
+ stmt = ResultSet.new(self, stmt).to_a if type_translation
138
+
178
139
  if block_given?
179
- result.each { |row| yield row }
140
+ stmt.each do |row|
141
+ if @results_as_hash
142
+ yield type_translation ? row : ordered_map_for(columns, row)
143
+ else
144
+ yield row
145
+ end
146
+ end
180
147
  else
181
- return result.inject([]) { |arr, row| arr << row; arr }
148
+ if @results_as_hash
149
+ stmt.map { |row|
150
+ h = type_translation ? row : ordered_map_for(columns, row)
151
+
152
+ # FIXME UGH TERRIBLE HACK!
153
+ h['unique'] = h['unique'].to_s if hack
154
+
155
+ h
156
+ }
157
+ else
158
+ stmt.to_a
159
+ end
182
160
  end
183
161
  end
184
162
  end
@@ -193,15 +171,104 @@ module SQLite3
193
171
  #
194
172
  # See also #execute, #query, and #execute_batch for additional ways of
195
173
  # executing statements.
196
- def execute2(sql, *bind_vars)
197
- prepare(sql) do |stmt|
198
- result = stmt.execute(*bind_vars)
174
+ def execute2( sql, *bind_vars )
175
+ prepare( sql ) do |stmt|
176
+ result = stmt.execute( *bind_vars )
199
177
  if block_given?
200
- yield result.columns
178
+ yield stmt.columns
201
179
  result.each { |row| yield row }
202
180
  else
203
- return result.inject([result.columns]) { |arr,row| arr << row; arr }
181
+ return result.inject( [ stmt.columns ] ) { |arr,row|
182
+ arr << row; arr }
183
+ end
184
+ end
185
+ end
186
+
187
+ # Executes all SQL statements in the given string. By contrast, the other
188
+ # means of executing queries will only execute the first statement in the
189
+ # string, ignoring all subsequent statements. This will execute each one
190
+ # in turn. The same bind parameters, if given, will be applied to each
191
+ # statement.
192
+ #
193
+ # This always returns +nil+, making it unsuitable for queries that return
194
+ # rows.
195
+ def execute_batch( sql, bind_vars = [], *args )
196
+ # FIXME: remove this stuff later
197
+ unless [Array, Hash].include?(bind_vars.class)
198
+ bind_vars = [bind_vars]
199
+ warn(<<-eowarn) if $VERBOSE
200
+ #{caller[0]} is calling SQLite3::Database#execute_batch with bind parameters
201
+ that are not a list of a hash. Please switch to passing bind parameters as an
202
+ array or hash. Support for this behavior will be removed in version 2.0.0.
203
+ eowarn
204
+ end
205
+
206
+ # FIXME: remove this stuff later
207
+ if bind_vars.nil? || !args.empty?
208
+ if args.empty?
209
+ bind_vars = []
210
+ else
211
+ bind_vars = [nil] + args
212
+ end
213
+
214
+ warn(<<-eowarn) if $VERBOSE
215
+ #{caller[0]} is calling SQLite3::Database#execute_batch with nil or multiple bind params
216
+ without using an array. Please switch to passing bind parameters as an array.
217
+ Support for this behavior will be removed in version 2.0.0.
218
+ eowarn
219
+ end
220
+
221
+ sql = sql.strip
222
+ until sql.empty? do
223
+ prepare( sql ) do |stmt|
224
+ # FIXME: this should probably use sqlite3's api for batch execution
225
+ # This implementation requires stepping over the results.
226
+ if bind_vars.length == stmt.bind_parameter_count
227
+ stmt.bind_params(bind_vars)
228
+ end
229
+ stmt.step
230
+ sql = stmt.remainder.strip
231
+ end
232
+ end
233
+ nil
234
+ end
235
+
236
+ # This is a convenience method for creating a statement, binding
237
+ # paramters to it, and calling execute:
238
+ #
239
+ # result = db.query( "select * from foo where a=?", 5 )
240
+ # # is the same as
241
+ # result = db.prepare( "select * from foo where a=?" ).execute( 5 )
242
+ #
243
+ # You must be sure to call +close+ on the ResultSet instance that is
244
+ # returned, or you could have problems with locks on the table. If called
245
+ # with a block, +close+ will be invoked implicitly when the block
246
+ # terminates.
247
+ def query( sql, bind_vars = [], *args )
248
+
249
+ if bind_vars.nil? || !args.empty?
250
+ if args.empty?
251
+ bind_vars = []
252
+ else
253
+ bind_vars = [nil] + args
204
254
  end
255
+
256
+ warn(<<-eowarn) if $VERBOSE
257
+ #{caller[0]} is calling SQLite3::Database#query with nil or multiple bind params
258
+ without using an array. Please switch to passing bind parameters as an array.
259
+ Support for this will be removed in version 2.0.0.
260
+ eowarn
261
+ end
262
+
263
+ result = prepare( sql ).execute( bind_vars )
264
+ if block_given?
265
+ begin
266
+ yield result
267
+ ensure
268
+ result.close
269
+ end
270
+ else
271
+ return result
205
272
  end
206
273
  end
207
274
 
@@ -209,9 +276,8 @@ module SQLite3
209
276
  # discarding all others. It is otherwise identical to #execute.
210
277
  #
211
278
  # See also #get_first_value.
212
- def get_first_row(sql, *bind_vars)
213
- execute(sql, *bind_vars) { |row| return row }
214
- nil
279
+ def get_first_row( sql, *bind_vars )
280
+ execute( sql, *bind_vars ).first
215
281
  end
216
282
 
217
283
  # A convenience method for obtaining the first value of the first row of a
@@ -219,34 +285,183 @@ module SQLite3
219
285
  # identical to #execute.
220
286
  #
221
287
  # See also #get_first_row.
222
- def get_first_value(sql, *bind_vars)
223
- execute(sql, *bind_vars) { |row| return row[0] }
288
+ def get_first_value( sql, *bind_vars )
289
+ execute( sql, *bind_vars ) { |row| return row[0] }
224
290
  nil
225
291
  end
226
292
 
227
- # Obtains the unique row ID of the last row to be inserted by this Database
228
- # instance.
229
- def last_insert_row_id
230
- @driver.last_insert_rowid(@handle)
293
+ alias :busy_timeout :busy_timeout=
294
+
295
+ # Creates a new function for use in SQL statements. It will be added as
296
+ # +name+, with the given +arity+. (For variable arity functions, use
297
+ # -1 for the arity.)
298
+ #
299
+ # The block should accept at least one parameter--the FunctionProxy
300
+ # instance that wraps this function invocation--and any other
301
+ # arguments it needs (up to its arity).
302
+ #
303
+ # The block does not return a value directly. Instead, it will invoke
304
+ # the FunctionProxy#result= method on the +func+ parameter and
305
+ # indicate the return value that way.
306
+ #
307
+ # Example:
308
+ #
309
+ # db.create_function( "maim", 1 ) do |func, value|
310
+ # if value.nil?
311
+ # func.result = nil
312
+ # else
313
+ # func.result = value.split(//).sort.join
314
+ # end
315
+ # end
316
+ #
317
+ # puts db.get_first_value( "select maim(name) from table" )
318
+ def create_function name, arity, text_rep=Constants::TextRep::ANY, &block
319
+ define_function(name) do |*args|
320
+ fp = FunctionProxy.new
321
+ block.call(fp, *args)
322
+ fp.result
323
+ end
324
+ self
231
325
  end
232
326
 
233
- # Returns the number of changes made to this database instance by the last
234
- # operation performed. Note that a "delete from table" without a where
235
- # clause will not affect this value.
236
- def changes
237
- @driver.changes(@handle)
327
+ # Creates a new aggregate function for use in SQL statements. Aggregate
328
+ # functions are functions that apply over every row in the result set,
329
+ # instead of over just a single row. (A very common aggregate function
330
+ # is the "count" function, for determining the number of rows that match
331
+ # a query.)
332
+ #
333
+ # The new function will be added as +name+, with the given +arity+. (For
334
+ # variable arity functions, use -1 for the arity.)
335
+ #
336
+ # The +step+ parameter must be a proc object that accepts as its first
337
+ # parameter a FunctionProxy instance (representing the function
338
+ # invocation), with any subsequent parameters (up to the function's arity).
339
+ # The +step+ callback will be invoked once for each row of the result set.
340
+ #
341
+ # The +finalize+ parameter must be a +proc+ object that accepts only a
342
+ # single parameter, the FunctionProxy instance representing the current
343
+ # function invocation. It should invoke FunctionProxy#result= to
344
+ # store the result of the function.
345
+ #
346
+ # Example:
347
+ #
348
+ # db.create_aggregate( "lengths", 1 ) do
349
+ # step do |func, value|
350
+ # func[ :total ] ||= 0
351
+ # func[ :total ] += ( value ? value.length : 0 )
352
+ # end
353
+ #
354
+ # finalize do |func|
355
+ # func.result = func[ :total ] || 0
356
+ # end
357
+ # end
358
+ #
359
+ # puts db.get_first_value( "select lengths(name) from table" )
360
+ #
361
+ # See also #create_aggregate_handler for a more object-oriented approach to
362
+ # aggregate functions.
363
+ def create_aggregate( name, arity, step=nil, finalize=nil,
364
+ text_rep=Constants::TextRep::ANY, &block )
365
+
366
+ factory = Class.new do
367
+ def self.step( &block )
368
+ define_method(:step, &block)
369
+ end
370
+
371
+ def self.finalize( &block )
372
+ define_method(:finalize, &block)
373
+ end
374
+ end
375
+
376
+ if block_given?
377
+ factory.instance_eval(&block)
378
+ else
379
+ factory.class_eval do
380
+ define_method(:step, step)
381
+ define_method(:finalize, finalize)
382
+ end
383
+ end
384
+
385
+ proxy = factory.new
386
+ proxy.extend(Module.new {
387
+ attr_accessor :ctx
388
+
389
+ def step( *args )
390
+ super(@ctx, *args)
391
+ end
392
+
393
+ def finalize
394
+ super(@ctx)
395
+ end
396
+ })
397
+ proxy.ctx = FunctionProxy.new
398
+ define_aggregator(name, proxy)
238
399
  end
239
400
 
240
- # Indicates that if a request for a resource terminates because that
241
- # resource is busy, SQLite should sleep and retry for up to the indicated
242
- # number of milliseconds. By default, SQLite does not retry
243
- # busy resources. To restore the default behavior, send 0 as the
244
- # +ms+ parameter.
401
+ # This is another approach to creating an aggregate function (see
402
+ # #create_aggregate). Instead of explicitly specifying the name,
403
+ # callbacks, arity, and type, you specify a factory object
404
+ # (the "handler") that knows how to obtain all of that information. The
405
+ # handler should respond to the following messages:
406
+ #
407
+ # +arity+:: corresponds to the +arity+ parameter of #create_aggregate. This
408
+ # message is optional, and if the handler does not respond to it,
409
+ # the function will have an arity of -1.
410
+ # +name+:: this is the name of the function. The handler _must_ implement
411
+ # this message.
412
+ # +new+:: this must be implemented by the handler. It should return a new
413
+ # instance of the object that will handle a specific invocation of
414
+ # the function.
245
415
  #
246
- # See also the mutually exclusive #busy_handler.
247
- def busy_timeout(ms)
248
- result = @driver.busy_timeout(@handle, ms)
249
- Error.check(result, self)
416
+ # The handler instance (the object returned by the +new+ message, described
417
+ # above), must respond to the following messages:
418
+ #
419
+ # +step+:: this is the method that will be called for each step of the
420
+ # aggregate function's evaluation. It should implement the same
421
+ # signature as the +step+ callback for #create_aggregate.
422
+ # +finalize+:: this is the method that will be called to finalize the
423
+ # aggregate function's evaluation. It should implement the
424
+ # same signature as the +finalize+ callback for
425
+ # #create_aggregate.
426
+ #
427
+ # Example:
428
+ #
429
+ # class LengthsAggregateHandler
430
+ # def self.arity; 1; end
431
+ #
432
+ # def initialize
433
+ # @total = 0
434
+ # end
435
+ #
436
+ # def step( ctx, name )
437
+ # @total += ( name ? name.length : 0 )
438
+ # end
439
+ #
440
+ # def finalize( ctx )
441
+ # ctx.result = @total
442
+ # end
443
+ # end
444
+ #
445
+ # db.create_aggregate_handler( LengthsAggregateHandler )
446
+ # puts db.get_first_value( "select lengths(name) from A" )
447
+ def create_aggregate_handler( handler )
448
+ proxy = Class.new do
449
+ def initialize handler
450
+ @handler = handler
451
+ @fp = FunctionProxy.new
452
+ end
453
+
454
+ def step( *args )
455
+ @handler.step(@fp, *args)
456
+ end
457
+
458
+ def finalize
459
+ @handler.finalize @fp
460
+ @fp.result
461
+ end
462
+ end
463
+ define_aggregator(handler.name, proxy.new(handler.new))
464
+ self
250
465
  end
251
466
 
252
467
  # Begins a new transaction. Note that nested transactions are not allowed
@@ -265,7 +480,7 @@ module SQLite3
265
480
  # If a block is not given, it is the caller's responsibility to end the
266
481
  # transaction explicitly, either by calling #commit, or by calling
267
482
  # #rollback.
268
- def transaction(mode = :deferred)
483
+ def transaction( mode = :deferred )
269
484
  execute "begin #{mode.to_s} transaction"
270
485
  @transaction_active = true
271
486
 
@@ -308,5 +523,65 @@ module SQLite3
308
523
  def transaction_active?
309
524
  @transaction_active
310
525
  end
526
+
527
+ # Returns +true+ if the database has been open in readonly mode
528
+ # A helper to check before performing any operation
529
+ def readonly?
530
+ @readonly
531
+ end
532
+
533
+ # A helper class for dealing with custom functions (see #create_function,
534
+ # #create_aggregate, and #create_aggregate_handler). It encapsulates the
535
+ # opaque function object that represents the current invocation. It also
536
+ # provides more convenient access to the API functions that operate on
537
+ # the function object.
538
+ #
539
+ # This class will almost _always_ be instantiated indirectly, by working
540
+ # with the create methods mentioned above.
541
+ class FunctionProxy
542
+ attr_accessor :result
543
+
544
+ # Create a new FunctionProxy that encapsulates the given +func+ object.
545
+ # If context is non-nil, the functions context will be set to that. If
546
+ # it is non-nil, it must quack like a Hash. If it is nil, then none of
547
+ # the context functions will be available.
548
+ def initialize
549
+ @result = nil
550
+ @context = {}
551
+ end
552
+
553
+ # Set the result of the function to the given error message.
554
+ # The function will then return that error.
555
+ def set_error( error )
556
+ @driver.result_error( @func, error.to_s, -1 )
557
+ end
558
+
559
+ # (Only available to aggregate functions.) Returns the number of rows
560
+ # that the aggregate has processed so far. This will include the current
561
+ # row, and so will always return at least 1.
562
+ def count
563
+ @driver.aggregate_count( @func )
564
+ end
565
+
566
+ # Returns the value with the given key from the context. This is only
567
+ # available to aggregate functions.
568
+ def []( key )
569
+ @context[ key ]
570
+ end
571
+
572
+ # Sets the value with the given key in the context. This is only
573
+ # available to aggregate functions.
574
+ def []=( key, value )
575
+ @context[ key ] = value
576
+ end
577
+ end
578
+
579
+ private
580
+
581
+ def ordered_map_for columns, row
582
+ h = Hash[*columns.zip(row).flatten]
583
+ row.each_with_index { |r, i| h[i] = r }
584
+ h
585
+ end
311
586
  end
312
587
  end