sqlite3-ruby 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

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
+