sqlite3 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -19,3 +19,5 @@ rdoc
19
19
  pkg
20
20
 
21
21
  ## PROJECT::SPECIFIC
22
+ *.gem
23
+ *.gemspec
@@ -10,7 +10,7 @@ Description goes here.
10
10
  future version unintentionally.
11
11
  * Commit, do not mess with rakefile, version, or history.
12
12
  (if you want to have your own version, that is fine but
13
- bump version in a commit by itself I can ignore when I pull)
13
+ bump version in a commit by itself I can ignore when I pull)
14
14
  * Send me a pull request. Bonus points for topic branches.
15
15
 
16
16
  == Copyright
data/Rakefile CHANGED
@@ -7,13 +7,21 @@ begin
7
7
  require 'jeweler'
8
8
  Jeweler::Tasks.new do |gem|
9
9
  gem.name = "sqlite3"
10
- gem.summary = %Q{SQLite3 bindings to Ruby 1.8/1.9}
11
- gem.description = %Q{SQLite3 bindings to Ruby 1.8/1.9}
10
+ gem.summary = %Q{SQLite3/Ruby FFI bindings}
11
+ gem.description = %Q{SQLite3/Ruby FFI bindings}
12
12
  gem.email = "qoobaa@gmail.com"
13
13
  gem.homepage = "http://github.com/qoobaa/sqlite3"
14
14
  gem.authors = ["Jakub Kuźma"]
15
- gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
15
+ gem.add_development_dependency "test-unit", ">= 2.0"
16
16
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ gem.post_install_message = <<-EOM
18
+
19
+ WARNING!
20
+
21
+ This is an early alpha version of SQLite3/Ruby FFI bindings!
22
+ If you need native, production ready bindings install sqlite3-ruby instead.
23
+
24
+ EOM
17
25
  end
18
26
  rescue LoadError
19
27
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1 @@
1
+ require "sqlite3/database"
@@ -0,0 +1,51 @@
1
+ module SQLite3
2
+ module Constants
3
+
4
+ module TextRep
5
+ UTF8 = 1
6
+ UTF16LE = 2
7
+ UTF16BE = 3
8
+ UTF16 = 4
9
+ ANY = 5
10
+ end
11
+
12
+ module ColumnType
13
+ INTEGER = 1
14
+ FLOAT = 2
15
+ TEXT = 3
16
+ BLOB = 4
17
+ NULL = 5
18
+ end
19
+
20
+ module ErrorCode
21
+ OK = 0 # Successful result
22
+ ERROR = 1 # SQL error or missing database
23
+ INTERNAL = 2 # An internal logic error in SQLite
24
+ PERM = 3 # Access permission denied
25
+ ABORT = 4 # Callback routine requested an abort
26
+ BUSY = 5 # The database file is locked
27
+ LOCKED = 6 # A table in the database is locked
28
+ NOMEM = 7 # A malloc() failed
29
+ READONLY = 8 # Attempt to write a readonly database
30
+ INTERRUPT = 9 # Operation terminated by sqlite_interrupt()
31
+ IOERR = 10 # Some kind of disk I/O error occurred
32
+ CORRUPT = 11 # The database disk image is malformed
33
+ NOTFOUND = 12 # (Internal Only) Table or record not found
34
+ FULL = 13 # Insertion failed because database is full
35
+ CANTOPEN = 14 # Unable to open the database file
36
+ PROTOCOL = 15 # Database lock protocol error
37
+ EMPTY = 16 # (Internal Only) Database table is empty
38
+ SCHEMA = 17 # The database schema changed
39
+ TOOBIG = 18 # Too much data for one row of a table
40
+ CONSTRAINT = 19 # Abort due to contraint violation
41
+ MISMATCH = 20 # Data type mismatch
42
+ MISUSE = 21 # Library used incorrectly
43
+ NOLFS = 22 # Uses OS features not supported on host
44
+ AUTH = 23 # Authorization denied
45
+
46
+ ROW = 100 # sqlite_step() has another row ready
47
+ DONE = 101 # sqlite_step() has finished executing
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,703 @@
1
+ require "sqlite3/constants"
2
+ require "sqlite3/errors"
3
+ require "sqlite3/pragmas"
4
+ require "sqlite3/statement"
5
+ require "sqlite3/translator"
6
+ require "sqlite3/value"
7
+
8
+ module SQLite3
9
+
10
+ # The Database class encapsulates a single connection to a SQLite3 database.
11
+ # Its usage is very straightforward:
12
+ #
13
+ # require "sqlite3"
14
+ #
15
+ # db = SQLite3::Database.new("data.db")
16
+ #
17
+ # db.execute("select * from table") do |row|
18
+ # p row
19
+ # end
20
+ #
21
+ # db.close
22
+ #
23
+ # It wraps the lower-level methods provides by the selected driver, and
24
+ # includes the Pragmas module for access to various pragma convenience
25
+ # methods.
26
+ #
27
+ # The Database class provides type translation services as well, by which
28
+ # the SQLite3 data types (which are all represented as strings) may be
29
+ # converted into their corresponding types (as defined in the schemas
30
+ # for their tables). This translation only occurs when querying data from
31
+ # the database--insertions and updates are all still typeless.
32
+ #
33
+ # Furthermore, the Database class has been designed to work well with the
34
+ # ArrayFields module from Ara Howard. If you require the ArrayFields
35
+ # module before performing a query, and if you have not enabled results as
36
+ # hashes, then the results will all be indexible by field name.
37
+ class Database
38
+ include Pragmas
39
+
40
+ class << self
41
+
42
+ alias :open :new
43
+
44
+ # Quotes the given string, making it safe to use in an SQL statement.
45
+ # It replaces all instances of the single-quote character with two
46
+ # single-quote characters. The modified string is returned.
47
+ def quote(string)
48
+ string.gsub(/'/, "''")
49
+ end
50
+
51
+ end
52
+
53
+ # The low-level opaque database handle that this object wraps.
54
+ attr_reader :handle
55
+
56
+ # A reference to the underlying SQLite3 driver used by this database.
57
+ attr_reader :driver
58
+
59
+ # A boolean that indicates whether rows in result sets should be returned
60
+ # as hashes or not. By default, rows are returned as arrays.
61
+ attr_accessor :results_as_hash
62
+
63
+ # A boolean indicating whether or not type translation is enabled for this
64
+ # database.
65
+ attr_accessor :type_translation
66
+
67
+ # Create a new Database object that opens the given file. If utf16
68
+ # is +true+, the filename is interpreted as a UTF-16 encoded string.
69
+ #
70
+ # By default, the new database will return result rows as arrays
71
+ # (#results_as_hash) and has type translation disabled (#type_translation=).
72
+ def initialize(file_name, options = {})
73
+ utf16 = options.fetch(:utf16, false)
74
+ load_driver(options[:driver])
75
+
76
+ @statement_factory = options[:statement_factory] || Statement
77
+
78
+ result, @handle = @driver.open(file_name, utf16)
79
+ Error.check(result, self, "could not open database")
80
+
81
+ @closed = false
82
+ @results_as_hash = options.fetch(:results_as_hash,false)
83
+ @type_translation = options.fetch(:type_translation,false)
84
+ @translator = nil
85
+ @transaction_active = false
86
+ end
87
+
88
+ # Return +true+ if the string is a valid (ie, parsable) SQL statement, and
89
+ # +false+ otherwise. If +utf16+ is +true+, then the string is a UTF-16
90
+ # character string.
91
+ def complete?(string, utf16 = false)
92
+ @driver.complete?(string, utf16)
93
+ end
94
+
95
+ # Return a string describing the last error to have occurred with this
96
+ # database.
97
+ def errmsg(utf16 = false)
98
+ @driver.errmsg(@handle, utf16)
99
+ end
100
+
101
+ # Return an integer representing the last error to have occurred with this
102
+ # database.
103
+ def errcode
104
+ @driver.errcode(@handle)
105
+ end
106
+
107
+ # Return the type translator employed by this database instance. Each
108
+ # database instance has its own type translator; this allows for different
109
+ # type handlers to be installed in each instance without affecting other
110
+ # instances. Furthermore, the translators are instantiated lazily, so that
111
+ # if a database does not use type translation, it will not be burdened by
112
+ # the overhead of a useless type translator. (See the Translator class.)
113
+ def translator
114
+ @translator ||= Translator.new
115
+ end
116
+
117
+ # Closes this database.
118
+ def close
119
+ unless @closed
120
+ result = @driver.close(@handle)
121
+ Error.check(result, self)
122
+ end
123
+ @closed = true
124
+ end
125
+
126
+ # Returns +true+ if this database instance has been closed (see #close).
127
+ def closed?
128
+ @closed
129
+ end
130
+
131
+ # Installs (or removes) a block that will be invoked for every SQL
132
+ # statement executed. The block receives a two parameters: the +data+
133
+ # argument, and the SQL statement executed. If the block is +nil+,
134
+ # any existing tracer will be uninstalled.
135
+ def trace(data = nil, &block)
136
+ @driver.trace(@handle, data, &block)
137
+ end
138
+
139
+ # Installs (or removes) a block that will be invoked for every access
140
+ # to the database. If the block returns 0 (or +nil+), the statement
141
+ # is allowed to proceed. Returning 1 causes an authorization error to
142
+ # occur, and returning 2 causes the access to be silently denied.
143
+ def authorizer(data = nil, &block)
144
+ result = @driver.set_authorizer(@handle, data, &block)
145
+ Error.check(result, self)
146
+ end
147
+
148
+ # Returns a Statement object representing the given SQL. This does not
149
+ # execute the statement; it merely prepares the statement for execution.
150
+ #
151
+ # The Statement can then be executed using Statement#execute.
152
+ #
153
+ def prepare(sql)
154
+ stmt = @statement_factory.new(self, sql)
155
+ if block_given?
156
+ begin
157
+ yield stmt
158
+ ensure
159
+ stmt.close
160
+ end
161
+ else
162
+ return stmt
163
+ end
164
+ end
165
+
166
+ # Executes the given SQL statement. If additional parameters are given,
167
+ # they are treated as bind variables, and are bound to the placeholders in
168
+ # the query.
169
+ #
170
+ # Note that if any of the values passed to this are hashes, then the
171
+ # key/value pairs are each bound separately, with the key being used as
172
+ # the name of the placeholder to bind the value to.
173
+ #
174
+ # The block is optional. If given, it will be invoked for each row returned
175
+ # by the query. Otherwise, any results are accumulated into an array and
176
+ # returned wholesale.
177
+ #
178
+ # See also #execute2, #query, and #execute_batch for additional ways of
179
+ # executing statements.
180
+ def execute(sql, *bind_vars)
181
+ prepare(sql) do |stmt|
182
+ result = stmt.execute(*bind_vars)
183
+ if block_given?
184
+ result.each { |row| yield row }
185
+ else
186
+ return result.inject([]) { |arr, row| arr << row; arr }
187
+ end
188
+ end
189
+ end
190
+
191
+ # Executes the given SQL statement, exactly as with #execute. However, the
192
+ # first row returned (either via the block, or in the returned array) is
193
+ # always the names of the columns. Subsequent rows correspond to the data
194
+ # from the result set.
195
+ #
196
+ # Thus, even if the query itself returns no rows, this method will always
197
+ # return at least one row--the names of the columns.
198
+ #
199
+ # See also #execute, #query, and #execute_batch for additional ways of
200
+ # executing statements.
201
+ def execute2(sql, *bind_vars)
202
+ prepare(sql) do |stmt|
203
+ result = stmt.execute(*bind_vars)
204
+ if block_given?
205
+ yield result.columns
206
+ result.each { |row| yield row }
207
+ else
208
+ return result.inject([result.columns]) { |arr,row| arr << row; arr }
209
+ end
210
+ end
211
+ end
212
+
213
+ # Executes all SQL statements in the given string. By contrast, the other
214
+ # means of executing queries will only execute the first statement in the
215
+ # string, ignoring all subsequent statements. This will execute each one
216
+ # in turn. The same bind parameters, if given, will be applied to each
217
+ # statement.
218
+ #
219
+ # This always returns +nil+, making it unsuitable for queries that return
220
+ # rows.
221
+ def execute_batch(sql, *bind_vars)
222
+ sql = sql.strip
223
+ until sql.empty? do
224
+ prepare(sql) do |stmt|
225
+ stmt.execute(*bind_vars)
226
+ sql = stmt.remainder.strip
227
+ end
228
+ end
229
+ nil
230
+ end
231
+
232
+ # This is a convenience method for creating a statement, binding
233
+ # paramters to it, and calling execute:
234
+ #
235
+ # result = db.query("select * from foo where a=?", 5)
236
+ # # is the same as
237
+ # result = db.prepare("select * from foo where a=?").execute(5)
238
+ #
239
+ # You must be sure to call +close+ on the ResultSet instance that is
240
+ # returned, or you could have problems with locks on the table. If called
241
+ # with a block, +close+ will be invoked implicitly when the block
242
+ # terminates.
243
+ def query(sql, *bind_vars)
244
+ result = prepare(sql).execute(*bind_vars)
245
+ if block_given?
246
+ begin
247
+ yield result
248
+ ensure
249
+ result.close
250
+ end
251
+ else
252
+ return result
253
+ end
254
+ end
255
+
256
+ # A convenience method for obtaining the first row of a result set, and
257
+ # discarding all others. It is otherwise identical to #execute.
258
+ #
259
+ # See also #get_first_value.
260
+ def get_first_row(sql, *bind_vars)
261
+ execute(sql, *bind_vars) { |row| return row }
262
+ nil
263
+ end
264
+
265
+ # A convenience method for obtaining the first value of the first row of a
266
+ # result set, and discarding all other values and rows. It is otherwise
267
+ # identical to #execute.
268
+ #
269
+ # See also #get_first_row.
270
+ def get_first_value(sql, *bind_vars)
271
+ execute(sql, *bind_vars) { |row| return row[0] }
272
+ nil
273
+ end
274
+
275
+ # Obtains the unique row ID of the last row to be inserted by this Database
276
+ # instance.
277
+ def last_insert_row_id
278
+ @driver.last_insert_rowid(@handle)
279
+ end
280
+
281
+ # Returns the number of changes made to this database instance by the last
282
+ # operation performed. Note that a "delete from table" without a where
283
+ # clause will not affect this value.
284
+ def changes
285
+ @driver.changes(@handle)
286
+ end
287
+
288
+ # Returns the total number of changes made to this database instance
289
+ # since it was opened.
290
+ def total_changes
291
+ @driver.total_changes(@handle)
292
+ end
293
+
294
+ # Interrupts the currently executing operation, causing it to abort.
295
+ def interrupt
296
+ @driver.interrupt(@handle)
297
+ end
298
+
299
+ # Register a busy handler with this database instance. When a requested
300
+ # resource is busy, this handler will be invoked. If the handler returns
301
+ # +false+, the operation will be aborted; otherwise, the resource will
302
+ # be requested again.
303
+ #
304
+ # The handler will be invoked with the name of the resource that was
305
+ # busy, and the number of times it has been retried.
306
+ #
307
+ # See also the mutually exclusive #busy_timeout.
308
+ def busy_handler(data = nil, &block) # :yields: data, retries
309
+ result = @driver.busy_handler(@handle, data, &block)
310
+ Error.check(result, self)
311
+ end
312
+
313
+ # Indicates that if a request for a resource terminates because that
314
+ # resource is busy, SQLite should sleep and retry for up to the indicated
315
+ # number of milliseconds. By default, SQLite does not retry
316
+ # busy resources. To restore the default behavior, send 0 as the
317
+ # +ms+ parameter.
318
+ #
319
+ # See also the mutually exclusive #busy_handler.
320
+ def busy_timeout(ms)
321
+ result = @driver.busy_timeout(@handle, ms)
322
+ Error.check(result, self)
323
+ end
324
+
325
+ # Creates a new function for use in SQL statements. It will be added as
326
+ # +name+, with the given +arity+. (For variable arity functions, use
327
+ # -1 for the arity.)
328
+ #
329
+ # The block should accept at least one parameter--the FunctionProxy
330
+ # instance that wraps this function invocation--and any other
331
+ # arguments it needs (up to its arity).
332
+ #
333
+ # The block does not return a value directly. Instead, it will invoke
334
+ # the FunctionProxy#set_result method on the +func+ parameter and
335
+ # indicate the return value that way.
336
+ #
337
+ # Example:
338
+ #
339
+ # db.create_function("maim", 1) do |func, value|
340
+ # if value.nil?
341
+ # func.result = nil
342
+ # else
343
+ # func.result = value.split(//).sort.join
344
+ # end
345
+ # end
346
+ #
347
+ # puts db.get_first_value("select maim(name) from table")
348
+ def create_function(name, arity, text_rep = Constants::TextRep::ANY, &block) # :yields: func, *args
349
+ # begin
350
+ callback = proc do |func, *args|
351
+ begin
352
+ block.call(FunctionProxy.new(@driver, func), *args.map { |v| Value.new(self, v) })
353
+ rescue StandardError, Exception => e
354
+ @driver.result_error(func, "#{e.message} (#{e.class})", -1)
355
+ end
356
+ end
357
+
358
+ result = @driver.create_function(@handle, name, arity, text_rep, nil, callback, nil, nil)
359
+ Error.check(result, self)
360
+
361
+ self
362
+ end
363
+
364
+ # Creates a new aggregate function for use in SQL statements. Aggregate
365
+ # functions are functions that apply over every row in the result set,
366
+ # instead of over just a single row. (A very common aggregate function
367
+ # is the "count" function, for determining the number of rows that match
368
+ # a query.)
369
+ #
370
+ # The new function will be added as +name+, with the given +arity+. (For
371
+ # variable arity functions, use -1 for the arity.)
372
+ #
373
+ # The +step+ parameter must be a proc object that accepts as its first
374
+ # parameter a FunctionProxy instance (representing the function
375
+ # invocation), with any subsequent parameters (up to the function's arity).
376
+ # The +step+ callback will be invoked once for each row of the result set.
377
+ #
378
+ # The +finalize+ parameter must be a +proc+ object that accepts only a
379
+ # single parameter, the FunctionProxy instance representing the current
380
+ # function invocation. It should invoke FunctionProxy#set_result to
381
+ # store the result of the function.
382
+ #
383
+ # Example:
384
+ #
385
+ # db.create_aggregate("lengths", 1) do
386
+ # step do |func, value|
387
+ # func[:total] ||= 0
388
+ # func[:total] += (value ? value.length : 0)
389
+ # end
390
+ #
391
+ # finalize do |func|
392
+ # func.set_result(func[:total] || 0)
393
+ # end
394
+ # end
395
+ #
396
+ # puts db.get_first_value("select lengths(name) from table")
397
+ #
398
+ # See also #create_aggregate_handler for a more object-oriented approach to
399
+ # aggregate functions.
400
+ def create_aggregate(name, arity, step = nil, finalize = nil, text_rep = Constants::TextRep::ANY, &block)
401
+ # begin
402
+ if block
403
+ proxy = AggregateDefinitionProxy.new
404
+ proxy.instance_eval(&block)
405
+ step ||= proxy.step_callback
406
+ finalize ||= proxy.finalize_callback
407
+ end
408
+
409
+ step_callback = proc do |func, *args|
410
+ ctx = @driver.aggregate_context(func)
411
+ unless ctx[:__error]
412
+ begin
413
+ step.call(FunctionProxy.new(@driver, func, ctx), *args.map { |v| Value.new(self, v) })
414
+ rescue Exception => e
415
+ ctx[:__error] = e
416
+ end
417
+ end
418
+ end
419
+
420
+ finalize_callback = proc do |func|
421
+ ctx = @driver.aggregate_context(func)
422
+ unless ctx[:__error]
423
+ begin
424
+ finalize.call(FunctionProxy.new(@driver, func, ctx))
425
+ rescue Exception => e
426
+ @driver.result_error(func, "#{e.message} (#{e.class})", -1)
427
+ end
428
+ else
429
+ e = ctx[:__error]
430
+ @driver.result_error(func, "#{e.message} (#{e.class})", -1)
431
+ end
432
+ end
433
+
434
+ result = @driver.create_function(@handle, name, arity, text_rep, nil, nil, step_callback, finalize_callback)
435
+ Error.check(result, self)
436
+
437
+ self
438
+ end
439
+
440
+ # This is another approach to creating an aggregate function (see
441
+ # #create_aggregate). Instead of explicitly specifying the name,
442
+ # callbacks, arity, and type, you specify a factory object
443
+ # (the "handler") that knows how to obtain all of that information. The
444
+ # handler should respond to the following messages:
445
+ #
446
+ # +arity+:: corresponds to the +arity+ parameter of #create_aggregate. This
447
+ # message is optional, and if the handler does not respond to it,
448
+ # the function will have an arity of -1.
449
+ # +name+:: this is the name of the function. The handler _must_ implement
450
+ # this message.
451
+ # +new+:: this must be implemented by the handler. It should return a new
452
+ # instance of the object that will handle a specific invocation of
453
+ # the function.
454
+ #
455
+ # The handler instance (the object returned by the +new+ message, described
456
+ # above), must respond to the following messages:
457
+ #
458
+ # +step+:: this is the method that will be called for each step of the
459
+ # aggregate function's evaluation. It should implement the same
460
+ # signature as the +step+ callback for #create_aggregate.
461
+ # +finalize+:: this is the method that will be called to finalize the
462
+ # aggregate function's evaluation. It should implement the
463
+ # same signature as the +finalize+ callback for
464
+ # #create_aggregate.
465
+ #
466
+ # Example:
467
+ #
468
+ # class LengthsAggregateHandler
469
+ # def self.arity; 1; end
470
+ #
471
+ # def initialize
472
+ # @total = 0
473
+ # end
474
+ #
475
+ # def step(ctx, name)
476
+ # @total += (name ? name.length : 0)
477
+ # end
478
+ #
479
+ # def finalize(ctx)
480
+ # ctx.set_result(@total)
481
+ # end
482
+ # end
483
+ #
484
+ # db.create_aggregate_handler(LengthsAggregateHandler)
485
+ # puts db.get_first_value("select lengths(name) from A")
486
+ def create_aggregate_handler(handler)
487
+ arity = -1
488
+ text_rep = Constants::TextRep::ANY
489
+
490
+ arity = handler.arity if handler.respond_to?(:arity)
491
+ text_rep = handler.text_rep if handler.respond_to?(:text_rep)
492
+ name = handler.name
493
+
494
+ step = proc do |func,*args|
495
+ ctx = @driver.aggregate_context(func)
496
+ unless ctx[:__error]
497
+ ctx[:handler] ||= handler.new
498
+ begin
499
+ ctx[:handler].step(FunctionProxy.new(@driver, func, ctx), *args.map{|v| Value.new(self,v)})
500
+ rescue Exception, StandardError => e
501
+ ctx[:__error] = e
502
+ end
503
+ end
504
+ end
505
+
506
+ finalize = proc do |func|
507
+ ctx = @driver.aggregate_context(func)
508
+ unless ctx[:__error]
509
+ ctx[:handler] ||= handler.new
510
+ begin
511
+ ctx[:handler].finalize(FunctionProxy.new(@driver, func, ctx))
512
+ rescue Exception => e
513
+ ctx[:__error] = e
514
+ end
515
+ end
516
+
517
+ if ctx[:__error]
518
+ e = ctx[:__error]
519
+ @driver.sqlite3_result_error(func, "#{e.message} (#{e.class})", -1)
520
+ end
521
+ end
522
+
523
+ result = @driver.create_function(@handle, name, arity, text_rep, nil, nil, step, finalize)
524
+ Error.check(result, self)
525
+
526
+ self
527
+ end
528
+
529
+ # Begins a new transaction. Note that nested transactions are not allowed
530
+ # by SQLite, so attempting to nest a transaction will result in a runtime
531
+ # exception.
532
+ #
533
+ # The +mode+ parameter may be either <tt>:deferred</tt> (the default),
534
+ # <tt>:immediate</tt>, or <tt>:exclusive</tt>.
535
+ #
536
+ # If a block is given, the database instance is yielded to it, and the
537
+ # transaction is committed when the block terminates. If the block
538
+ # raises an exception, a rollback will be performed instead. Note that if
539
+ # a block is given, #commit and #rollback should never be called
540
+ # explicitly or you'll get an error when the block terminates.
541
+ #
542
+ # If a block is not given, it is the caller's responsibility to end the
543
+ # transaction explicitly, either by calling #commit, or by calling
544
+ # #rollback.
545
+ def transaction(mode = :deferred)
546
+ execute "begin #{mode.to_s} transaction"
547
+ @transaction_active = true
548
+
549
+ if block_given?
550
+ abort = false
551
+ begin
552
+ yield self
553
+ rescue ::Object
554
+ abort = true
555
+ raise
556
+ ensure
557
+ abort and rollback or commit
558
+ end
559
+ end
560
+
561
+ true
562
+ end
563
+
564
+ # Commits the current transaction. If there is no current transaction,
565
+ # this will cause an error to be raised. This returns +true+, in order
566
+ # to allow it to be used in idioms like
567
+ # <tt>abort? and rollback or commit</tt>.
568
+ def commit
569
+ execute "commit transaction"
570
+ @transaction_active = false
571
+ true
572
+ end
573
+
574
+ # Rolls the current transaction back. If there is no current transaction,
575
+ # this will cause an error to be raised. This returns +true+, in order
576
+ # to allow it to be used in idioms like
577
+ # <tt>abort? and rollback or commit</tt>.
578
+ def rollback
579
+ execute "rollback transaction"
580
+ @transaction_active = false
581
+ true
582
+ end
583
+
584
+ # Returns +true+ if there is a transaction active, and +false+ otherwise.
585
+ def transaction_active?
586
+ @transaction_active
587
+ end
588
+
589
+ # Loads the corresponding driver, or if it is nil, attempts to locate a
590
+ # suitable driver.
591
+ def load_driver(driver)
592
+ case driver
593
+ when Class
594
+ # do nothing--use what was given
595
+ when Symbol, String
596
+ require "sqlite3/driver/#{driver.to_s.downcase}/driver"
597
+ driver = SQLite3::Driver.const_get(driver)::Driver
598
+ else
599
+ ["FFI"].each do |d|
600
+ begin
601
+ require "sqlite3/driver/#{d.downcase}/driver"
602
+ driver = SQLite3::Driver.const_get(d)::Driver
603
+ break
604
+ rescue SyntaxError
605
+ raise
606
+ rescue ScriptError, Exception, NameError
607
+ end
608
+ end
609
+ raise "no driver for sqlite3 found" unless driver
610
+ end
611
+
612
+ @driver = driver.new
613
+ end
614
+ private :load_driver
615
+
616
+ # A helper class for dealing with custom functions (see #create_function,
617
+ # #create_aggregate, and #create_aggregate_handler). It encapsulates the
618
+ # opaque function object that represents the current invocation. It also
619
+ # provides more convenient access to the API functions that operate on
620
+ # the function object.
621
+ #
622
+ # This class will almost _always_ be instantiated indirectly, by working
623
+ # with the create methods mentioned above.
624
+ class FunctionProxy
625
+
626
+ # Create a new FunctionProxy that encapsulates the given +func+ object.
627
+ # If context is non-nil, the functions context will be set to that. If
628
+ # it is non-nil, it must quack like a Hash. If it is nil, then none of
629
+ # the context functions will be available.
630
+ def initialize(driver, func, context = nil)
631
+ @driver = driver
632
+ @func = func
633
+ @context = context
634
+ end
635
+
636
+ # Calls #set_result to set the result of this function.
637
+ def result=(result)
638
+ set_result(result)
639
+ end
640
+
641
+ # Set the result of the function to the given value. The function will
642
+ # then return this value.
643
+ def set_result(result, utf16 = false)
644
+ @driver.result_text(@func, result, utf16)
645
+ end
646
+
647
+ # Set the result of the function to the given error message.
648
+ # The function will then return that error.
649
+ def set_error(error)
650
+ @driver.result_error(@func, error.to_s, -1)
651
+ end
652
+
653
+ # (Only available to aggregate functions.) Returns the number of rows
654
+ # that the aggregate has processed so far. This will include the current
655
+ # row, and so will always return at least 1.
656
+ def count
657
+ ensure_aggregate!
658
+ @driver.aggregate_count(@func)
659
+ end
660
+
661
+ # Returns the value with the given key from the context. This is only
662
+ # available to aggregate functions.
663
+ def [](key)
664
+ ensure_aggregate!
665
+ @context[key]
666
+ end
667
+
668
+ # Sets the value with the given key in the context. This is only
669
+ # available to aggregate functions.
670
+ def []=(key, value)
671
+ ensure_aggregate!
672
+ @context[key] = value
673
+ end
674
+
675
+ # A function for performing a sanity check, to ensure that the function
676
+ # being invoked is an aggregate function. This is implied by the
677
+ # existence of the context variable.
678
+ def ensure_aggregate!
679
+ unless @context
680
+ raise MisuseException, "function is not an aggregate"
681
+ end
682
+ end
683
+ private :ensure_aggregate!
684
+
685
+ end
686
+
687
+ # A proxy used for defining the callbacks to an aggregate function.
688
+ class AggregateDefinitionProxy # :nodoc:
689
+ attr_reader :step_callback, :finalize_callback
690
+
691
+ def step(&block)
692
+ @step_callback = block
693
+ end
694
+
695
+ def finalize(&block)
696
+ @finalize_callback = block
697
+ end
698
+ end
699
+
700
+ end
701
+
702
+ end
703
+