sqlite3-ruby 0.9.0-mswin32

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,720 @@
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
+
75
+ alias :open :new
76
+
77
+ # Quotes the given string, making it safe to use in an SQL statement.
78
+ # It replaces all instances of the single-quote character with two
79
+ # single-quote characters. The modified string is returned.
80
+ def quote( string )
81
+ string.gsub( /'/, "''" )
82
+ end
83
+
84
+ end
85
+
86
+ # The low-level opaque database handle that this object wraps.
87
+ attr_reader :handle
88
+
89
+ # A reference to the underlying SQLite3 driver used by this database.
90
+ attr_reader :driver
91
+
92
+ # A boolean that indicates whether rows in result sets should be returned
93
+ # as hashes or not. By default, rows are returned as arrays.
94
+ attr_accessor :results_as_hash
95
+
96
+ # A boolean indicating whether or not type translation is enabled for this
97
+ # database.
98
+ attr_accessor :type_translation
99
+
100
+ # Create a new Database object that opens the given file. If utf16
101
+ # is +true+, the filename is interpreted as a UTF-16 encoded string.
102
+ #
103
+ # By default, the new database will return result rows as arrays
104
+ # (#results_as_hash) and has type translation disabled (#type_translation=).
105
+ def initialize( file_name, options={} )
106
+ utf16 = options.fetch(:utf16, false)
107
+ load_driver( options[:driver] )
108
+
109
+ @statement_factory = options[:statement_factory] || Statement
110
+
111
+ result, @handle = @driver.open( file_name, utf16 )
112
+ Error.check( result, nil, "could not open database" )
113
+
114
+ @closed = false
115
+ @results_as_hash = options.fetch(:results_as_hash,false)
116
+ @type_translation = options.fetch(:type_translation,false)
117
+ @translator = nil
118
+ end
119
+
120
+ # Return +true+ if the string is a valid (ie, parsable) SQL statement, and
121
+ # +false+ otherwise. If +utf16+ is +true+, then the string is a UTF-16
122
+ # character string.
123
+ def complete?( string, utf16=false )
124
+ @driver.complete?( string, utf16 )
125
+ end
126
+
127
+ # Return a string describing the last error to have occurred with this
128
+ # database.
129
+ def errmsg( utf16=false )
130
+ @driver.errmsg( @handle, utf16 )
131
+ end
132
+
133
+ # Return an integer representing the last error to have occurred with this
134
+ # database.
135
+ def errcode
136
+ @driver.errcode( @handle )
137
+ end
138
+
139
+ # Return the type translator employed by this database instance. Each
140
+ # database instance has its own type translator; this allows for different
141
+ # type handlers to be installed in each instance without affecting other
142
+ # instances. Furthermore, the translators are instantiated lazily, so that
143
+ # if a database does not use type translation, it will not be burdened by
144
+ # the overhead of a useless type translator. (See the Translator class.)
145
+ def translator
146
+ @translator ||= Translator.new
147
+ end
148
+
149
+ # Closes this database.
150
+ def close
151
+ unless @closed
152
+ result = @driver.close( @handle )
153
+ Error.check( result, self )
154
+ end
155
+ @closed = true
156
+ end
157
+
158
+ # Returns +true+ if this database instance has been closed (see #close).
159
+ def closed?
160
+ @closed
161
+ end
162
+
163
+ # Installs (or removes) a block that will be invoked for every SQL
164
+ # statement executed. The block receives a two parameters: the +data+
165
+ # argument, and the SQL statement executed. If the block is +nil+,
166
+ # any existing tracer will be uninstalled.
167
+ def trace( data=nil, &block )
168
+ @driver.trace( @handle, data, &block )
169
+ end
170
+
171
+ # Installs (or removes) a block that will be invoked for every access
172
+ # to the database. If the block returns 0 (or +nil+), the statement
173
+ # is allowed to proceed. Returning 1 causes an authorization error to
174
+ # occur, and returning 2 causes the access to be silently denied.
175
+ def authorizer( data=nil, &block )
176
+ result = @driver.set_authorizer( @handle, data, &block )
177
+ Error.check( result, self )
178
+ end
179
+
180
+ # Returns a Statement object representing the given SQL. This does not
181
+ # execute the statement; it merely prepares the statement for execution.
182
+ def prepare( sql )
183
+ stmt = @statement_factory.new( self, sql )
184
+ if block_given?
185
+ begin
186
+ yield stmt
187
+ ensure
188
+ stmt.close
189
+ end
190
+ else
191
+ return stmt
192
+ end
193
+ end
194
+
195
+ # Executes the given SQL statement. If additional parameters are given,
196
+ # they are treated as bind variables, and are bound to the placeholders in
197
+ # the query.
198
+ #
199
+ # Note that if any of the values passed to this are hashes, then the
200
+ # key/value pairs are each bound separately, with the key being used as
201
+ # the name of the placeholder to bind the value to.
202
+ #
203
+ # The block is optional. If given, it will be invoked for each row returned
204
+ # by the query. Otherwise, any results are accumulated into an array and
205
+ # returned wholesale.
206
+ #
207
+ # See also #execute2, and #execute_batch for additional ways of
208
+ # executing statements.
209
+ def execute( sql, *bind_vars )
210
+ prepare( sql ) do |stmt|
211
+ result = stmt.execute( *bind_vars )
212
+ if block_given?
213
+ result.each { |row| yield row }
214
+ else
215
+ return result.inject( [] ) { |arr,row| arr << row; arr }
216
+ end
217
+ end
218
+ end
219
+
220
+ # Executes the given SQL statement, exactly as with #execute. However, the
221
+ # first row returned (either via the block, or in the returned array) is
222
+ # always the names of the columns. Subsequent rows correspond to the data
223
+ # from the result set.
224
+ #
225
+ # Thus, even if the query itself returns no rows, this method will always
226
+ # return at least one row--the names of the columns.
227
+ #
228
+ # See also #execute, and #execute_batch for additional ways of
229
+ # executing statements.
230
+ def execute2( sql, *bind_vars )
231
+ prepare( sql ) do |stmt|
232
+ result = stmt.execute( *bind_vars )
233
+ if block_given?
234
+ yield result.columns
235
+ result.each { |row| yield row }
236
+ else
237
+ return result.inject( [ result.columns ] ) { |arr,row|
238
+ arr << row; arr }
239
+ end
240
+ end
241
+ end
242
+
243
+ # Executes all SQL statements in the given string. By contrast, the other
244
+ # means of executing queries will only execute the first statement in the
245
+ # string, ignoring all subsequent statements. This will execute each one
246
+ # in turn. The same bind parameters, if given, will be applied to each
247
+ # statement.
248
+ #
249
+ # This always returns +nil+, making it unsuitable for queries that return
250
+ # rows.
251
+ def execute_batch( sql, *bind_vars )
252
+ sql = sql.strip
253
+ until sql.empty? do
254
+ prepare( sql ) do |stmt|
255
+ stmt.execute( *bind_vars )
256
+ sql = stmt.remainder.strip
257
+ end
258
+ end
259
+ nil
260
+ end
261
+
262
+ # A convenience method for obtaining the first row of a result set, and
263
+ # discarding all others. It is otherwise identical to #execute.
264
+ #
265
+ # See also #get_first_value.
266
+ def get_first_row( sql, *bind_vars )
267
+ execute( sql, *bind_vars ) { |row| return row }
268
+ nil
269
+ end
270
+
271
+ # A convenience method for obtaining the first value of the first row of a
272
+ # result set, and discarding all other values and rows. It is otherwise
273
+ # identical to #execute.
274
+ #
275
+ # See also #get_first_row.
276
+ def get_first_value( sql, *bind_vars )
277
+ execute( sql, *bind_vars ) { |row| return row[0] }
278
+ nil
279
+ end
280
+
281
+ # Obtains the unique row ID of the last row to be inserted by this Database
282
+ # instance.
283
+ def last_insert_row_id
284
+ @driver.last_insert_rowid( @handle )
285
+ end
286
+
287
+ # Returns the number of changes made to this database instance by the last
288
+ # operation performed. Note that a "delete from table" without a where
289
+ # clause will not affect this value.
290
+ def changes
291
+ @driver.changes( @handle )
292
+ end
293
+
294
+ # Returns the total number of changes made to this database instance
295
+ # since it was opened.
296
+ def total_changes
297
+ @driver.total_changes( @handle )
298
+ end
299
+
300
+ # Interrupts the currently executing operation, causing it to abort.
301
+ def interrupt
302
+ @driver.interrupt( @handle )
303
+ end
304
+
305
+ # Register a busy handler with this database instance. When a requested
306
+ # resource is busy, this handler will be invoked. If the handler returns
307
+ # +false+, the operation will be aborted; otherwise, the resource will
308
+ # be requested again.
309
+ #
310
+ # The handler will be invoked with the name of the resource that was
311
+ # busy, and the number of times it has been retried.
312
+ #
313
+ # See also #busy_timeout.
314
+ def busy_handler( data=nil, &block ) # :yields: data, retries
315
+ result = @driver.busy_handler( @handle, data, &block )
316
+ Error.check( result, self )
317
+ end
318
+
319
+ # Indicates that if a request for a resource terminates because that
320
+ # resource is busy, SQLite should wait for the indicated number of
321
+ # milliseconds before trying again. By default, SQLite does not retry
322
+ # busy resources. To restore the default behavior, send 0 as the
323
+ # +ms+ parameter.
324
+ #
325
+ # See also #busy_handler.
326
+ def busy_timeout( ms )
327
+ result = @driver.busy_timeout( @handle, ms )
328
+ Error.check( result, self )
329
+ end
330
+
331
+ # Creates a new function for use in SQL statements. It will be added as
332
+ # +name+, with the given +arity+. (For variable arity functions, use
333
+ # -1 for the arity.)
334
+ #
335
+ # The block should accept at least one parameter--the FunctionProxy
336
+ # instance that wraps this function invocation--and any other
337
+ # arguments it needs (up to its arity).
338
+ #
339
+ # The block does not return a value directly. Instead, it will invoke
340
+ # the FunctionProxy#set_result method on the +func+ parameter and
341
+ # indicate the return value that way.
342
+ #
343
+ # Example:
344
+ #
345
+ # db.create_function( "maim", 1 ) do |func, value|
346
+ # if value.nil?
347
+ # func.result = nil
348
+ # else
349
+ # func.result = value.split(//).sort.join
350
+ # end
351
+ # end
352
+ #
353
+ # puts db.get_first_value( "select maim(name) from table" )
354
+ def create_function( name, arity, text_rep=Constants::TextRep::ANY,
355
+ &block ) # :yields: func, *args
356
+ # begin
357
+ callback = proc do |func,*args|
358
+ begin
359
+ block.call( FunctionProxy.new( @driver, func ),
360
+ *args.map{|v| Value.new(self,v)} )
361
+ rescue StandardError, Exception => e
362
+ @driver.result_error( func,
363
+ "#{e.message} (#{e.class})", -1 )
364
+ end
365
+ end
366
+
367
+ result = @driver.create_function( @handle, name, arity, text_rep, nil,
368
+ callback, nil, nil )
369
+ Error.check( result, self )
370
+
371
+ self
372
+ end
373
+
374
+ # Creates a new aggregate function for use in SQL statements. Aggregate
375
+ # functions are functions that apply over every row in the result set,
376
+ # instead of over just a single row. (A very common aggregate function
377
+ # is the "count" function, for determining the number of rows that match
378
+ # a query.)
379
+ #
380
+ # The new function will be added as +name+, with the given +arity+. (For
381
+ # variable arity functions, use -1 for the arity.)
382
+ #
383
+ # The +step+ parameter must be a proc object that accepts as its first
384
+ # parameter a FunctionProxy instance (representing the function
385
+ # invocation), with any subsequent parameters (up to the function's arity).
386
+ # The +step+ callback will be invoked once for each row of the result set.
387
+ #
388
+ # The +finalize+ parameter must be a +proc+ object that accepts only a
389
+ # single parameter, the FunctionProxy instance representing the current
390
+ # function invocation. It should invoke FunctionProxy#set_result to
391
+ # store the result of the function.
392
+ #
393
+ # Example:
394
+ #
395
+ # db.create_aggregate( "lengths", 1 ) do
396
+ # step do |func, value|
397
+ # func[ :total ] ||= 0
398
+ # func[ :total ] += ( value ? value.length : 0 )
399
+ # end
400
+ #
401
+ # finalize do |func|
402
+ # func.set_result( func[ :total ] || 0 )
403
+ # end
404
+ # end
405
+ #
406
+ # puts db.get_first_value( "select lengths(name) from table" )
407
+ #
408
+ # See also #create_aggregate_handler for a more object-oriented approach to
409
+ # aggregate functions.
410
+ def create_aggregate( name, arity, step=nil, finalize=nil,
411
+ text_rep=Constants::TextRep::ANY, &block )
412
+ # begin
413
+ if block
414
+ proxy = AggregateDefinitionProxy.new
415
+ proxy.instance_eval &block
416
+ step ||= proxy.step_callback
417
+ finalize ||= proxy.finalize_callback
418
+ end
419
+
420
+ step_callback = proc do |func,*args|
421
+ ctx = @driver.aggregate_context( func )
422
+ unless ctx[:__error]
423
+ begin
424
+ step.call( FunctionProxy.new( @driver, func, ctx ),
425
+ *args.map{|v| Value.new(self,v)} )
426
+ rescue Exception => e
427
+ ctx[:__error] = e
428
+ end
429
+ end
430
+ end
431
+
432
+ finalize_callback = proc do |func|
433
+ ctx = @driver.aggregate_context( func )
434
+ unless ctx[:__error]
435
+ begin
436
+ finalize.call( FunctionProxy.new( @driver, func, ctx ) )
437
+ rescue Exception => e
438
+ @driver.result_error( func,
439
+ "#{e.message} (#{e.class})", -1 )
440
+ end
441
+ else
442
+ e = ctx[:__error]
443
+ @driver.result_error( func,
444
+ "#{e.message} (#{e.class})", -1 )
445
+ end
446
+ end
447
+
448
+ result = @driver.create_function( @handle, name, arity, text_rep, nil,
449
+ nil, step_callback, finalize_callback )
450
+ Error.check( result, self )
451
+
452
+ self
453
+ end
454
+
455
+ # This is another approach to creating an aggregate function (see
456
+ # #create_aggregate). Instead of explicitly specifying the name,
457
+ # callbacks, arity, and type, you specify a factory object
458
+ # (the "handler") that knows how to obtain all of that information. The
459
+ # handler should respond to the following messages:
460
+ #
461
+ # +arity+:: corresponds to the +arity+ parameter of #create_aggregate. This
462
+ # message is optional, and if the handler does not respond to it,
463
+ # the function will have an arity of -1.
464
+ # +name+:: this is the name of the function. The handler _must_ implement
465
+ # this message.
466
+ # +new+:: this must be implemented by the handler. It should return a new
467
+ # instance of the object that will handle a specific invocation of
468
+ # the function.
469
+ #
470
+ # The handler instance (the object returned by the +new+ message, described
471
+ # above), must respond to the following messages:
472
+ #
473
+ # +step+:: this is the method that will be called for each step of the
474
+ # aggregate function's evaluation. It should implement the same
475
+ # signature as the +step+ callback for #create_aggregate.
476
+ # +finalize+:: this is the method that will be called to finalize the
477
+ # aggregate function's evaluation. It should implement the
478
+ # same signature as the +finalize+ callback for
479
+ # #create_aggregate.
480
+ #
481
+ # Example:
482
+ #
483
+ # class LengthsAggregateHandler
484
+ # def self.arity; 1; end
485
+ #
486
+ # def initialize
487
+ # @total = 0
488
+ # end
489
+ #
490
+ # def step( ctx, name )
491
+ # @total += ( name ? name.length : 0 )
492
+ # end
493
+ #
494
+ # def finalize( ctx )
495
+ # ctx.set_result( @total )
496
+ # end
497
+ # end
498
+ #
499
+ # db.create_aggregate_handler( LengthsAggregateHandler )
500
+ # puts db.get_first_value( "select lengths(name) from A" )
501
+ def create_aggregate_handler( handler )
502
+ arity = -1
503
+ text_rep = Constants::TextRep::ANY
504
+
505
+ arity = handler.arity if handler.respond_to?(:arity)
506
+ text_rep = handler.text_rep if handler.respond_to?(:text_rep)
507
+ name = handler.name
508
+
509
+ step = proc do |func,*args|
510
+ ctx = @driver.aggregate_context( func )
511
+ unless ctx[ :__error ]
512
+ ctx[ :handler ] ||= handler.new
513
+ begin
514
+ ctx[ :handler ].step( FunctionProxy.new( @driver, func, ctx ),
515
+ *args.map{|v| Value.new(self,v)} )
516
+ rescue Exception, StandardError => e
517
+ ctx[ :__error ] = e
518
+ end
519
+ end
520
+ end
521
+
522
+ finalize = proc do |func|
523
+ ctx = @driver.aggregate_context( func )
524
+ unless ctx[ :__error ]
525
+ ctx[ :handler ] ||= handler.new
526
+ begin
527
+ ctx[ :handler ].finalize( FunctionProxy.new( @driver, func, ctx ) )
528
+ rescue Exception => e
529
+ ctx[ :__error ] = e
530
+ end
531
+ end
532
+
533
+ if ctx[ :__error ]
534
+ e = ctx[ :__error ]
535
+ @driver.sqlite3_result_error( func, "#{e.message} (#{e.class})", -1 )
536
+ end
537
+ end
538
+
539
+ result = @driver.create_function( @handle, name, arity, text_rep, nil,
540
+ nil, step, finalize )
541
+ Error.check( result, self )
542
+
543
+ self
544
+ end
545
+
546
+ # Begins a new transaction. Note that nested transactions are not allowed
547
+ # by SQLite, so attempting to nest a transaction will result in a runtime
548
+ # exception.
549
+ #
550
+ # The +mode+ parameter may be either <tt>:deferred</tt> (the default),
551
+ # <tt>:immediate</tt>, or <tt>:exclusive</tt>.
552
+ #
553
+ # If a block is given, the database instance is yielded to it, and the
554
+ # transaction is committed when the block terminates. If the block
555
+ # raises an exception, a rollback will be performed instead. Note that if
556
+ # a block is given, #commit and #rollback should never be called
557
+ # explicitly or you'll get an error when the block terminates.
558
+ #
559
+ # If a block is not given, it is the caller's responsibility to end the
560
+ # transaction explicitly, either by calling #commit, or by calling
561
+ # #rollback.
562
+ def transaction( mode = :deferred )
563
+ execute "begin #{mode.to_s} transaction"
564
+ @transaction_active = true
565
+
566
+ if block_given?
567
+ abort = false
568
+ begin
569
+ yield self
570
+ rescue Exception
571
+ abort = true
572
+ raise
573
+ ensure
574
+ abort and rollback or commit
575
+ end
576
+ end
577
+
578
+ true
579
+ end
580
+
581
+ # Commits the current transaction. If there is no current transaction,
582
+ # this will cause an error to be raised. This returns +true+, in order
583
+ # to allow it to be used in idioms like
584
+ # <tt>abort? and rollback or commit</tt>.
585
+ def commit
586
+ execute "commit transaction"
587
+ @transaction_active = false
588
+ true
589
+ end
590
+
591
+ # Rolls the current transaction back. If there is no current transaction,
592
+ # this will cause an error to be raised. This returns +true+, in order
593
+ # to allow it to be used in idioms like
594
+ # <tt>abort? and rollback or commit</tt>.
595
+ def rollback
596
+ execute "rollback transaction"
597
+ @transaction_active = false
598
+ true
599
+ end
600
+
601
+ # Returns +true+ if there is a transaction active, and +false+ otherwise.
602
+ def transaction_active?
603
+ @transaction_active
604
+ end
605
+
606
+ # Loads the corresponding driver, or if it is nil, attempts to locate a
607
+ # suitable driver.
608
+ def load_driver( driver )
609
+ case driver
610
+ when Class
611
+ # do nothing--use what was given
612
+ when Symbol, String
613
+ require "sqlite3/driver/#{driver.to_s.downcase}/driver"
614
+ driver = SQLite3::Driver.const_get( driver )::Driver
615
+ else
616
+ [ "Native", "DL" ].each do |d|
617
+ begin
618
+ require "sqlite3/driver/#{d.downcase}/driver"
619
+ driver = SQLite3::Driver.const_get( d )::Driver
620
+ break
621
+ rescue SyntaxError
622
+ raise
623
+ rescue ScriptError, Exception
624
+ end
625
+ end
626
+ raise "no driver for sqlite3 found" unless driver
627
+ end
628
+
629
+ @driver = driver.new
630
+ end
631
+ private :load_driver
632
+
633
+ # A helper class for dealing with custom functions (see #create_function,
634
+ # #create_aggregate, and #create_aggregate_handler). It encapsulates the
635
+ # opaque function object that represents the current invocation. It also
636
+ # provides more convenient access to the API functions that operate on
637
+ # the function object.
638
+ #
639
+ # This class will almost _always_ be instantiated indirectly, by working
640
+ # with the create methods mentioned above.
641
+ class FunctionProxy
642
+
643
+ # Create a new FunctionProxy that encapsulates the given +func+ object.
644
+ # If context is non-nil, the functions context will be set to that. If
645
+ # it is non-nil, it must quack like a Hash. If it is nil, then none of
646
+ # the context functions will be available.
647
+ def initialize( driver, func, context=nil )
648
+ @driver = driver
649
+ @func = func
650
+ @context = context
651
+ end
652
+
653
+ # Calls #set_result to set the result of this function.
654
+ def result=( result )
655
+ set_result( result )
656
+ end
657
+
658
+ # Set the result of the function to the given value. The function will
659
+ # then return this value.
660
+ def set_result( result, utf16=false )
661
+ @driver.result_text( @func, result, utf16 )
662
+ end
663
+
664
+ # Set the result of the function to the given error message.
665
+ # The function will then return that error.
666
+ def set_error( error )
667
+ @driver.result_error( @func, error.to_s, -1 )
668
+ end
669
+
670
+ # (Only available to aggregate functions.) Returns the number of rows
671
+ # that the aggregate has processed so far. This will include the current
672
+ # row, and so will always return at least 1.
673
+ def count
674
+ ensure_aggregate!
675
+ @driver.aggregate_count( @func )
676
+ end
677
+
678
+ # Returns the value with the given key from the context. This is only
679
+ # available to aggregate functions.
680
+ def []( key )
681
+ ensure_aggregate!
682
+ @context[ key ]
683
+ end
684
+
685
+ # Sets the value with the given key in the context. This is only
686
+ # available to aggregate functions.
687
+ def []=( key, value )
688
+ ensure_aggregate!
689
+ @context[ key ] = value
690
+ end
691
+
692
+ # A function for performing a sanity check, to ensure that the function
693
+ # being invoked is an aggregate function. This is implied by the
694
+ # existence of the context variable.
695
+ def ensure_aggregate!
696
+ unless @context
697
+ raise MisuseException, "function is not an aggregate"
698
+ end
699
+ end
700
+ private :ensure_aggregate!
701
+
702
+ end
703
+
704
+ # A proxy used for defining the callbacks to an aggregate function.
705
+ class AggregateDefinitionProxy # :nodoc:
706
+ attr_reader :step_callback, :finalize_callback
707
+
708
+ def step( &block )
709
+ @step_callback = block
710
+ end
711
+
712
+ def finalize( &block )
713
+ @finalize_callback = block
714
+ end
715
+ end
716
+
717
+ end
718
+
719
+ end
720
+