sqlite3-ruby 0.5.0

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.

Potentially problematic release.


This version of sqlite3-ruby might be problematic. Click here for more details.

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