sqlite3 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +7 -4
- data/VERSION +1 -1
- data/lib/sqlite3.rb +15 -0
- data/lib/sqlite3/database.rb +16 -339
- data/lib/sqlite3/driver/ffi/api.rb +65 -218
- data/lib/sqlite3/driver/ffi/driver.rb +141 -194
- data/lib/sqlite3/encoding.rb +43 -0
- data/lib/sqlite3/errors.rb +0 -2
- data/lib/sqlite3/pragmas.rb +0 -2
- data/lib/sqlite3/resultset.rb +8 -8
- data/lib/sqlite3/statement.rb +4 -18
- data/lib/sqlite3/translator.rb +0 -3
- data/lib/sqlite3/value.rb +0 -2
- data/test/fixtures/SQLite.gif +0 -0
- data/test/test_database_initialization.rb +25 -0
- data/test/test_database_queries_utf_16.rb +80 -0
- data/test/test_database_queries_utf_8.rb +80 -0
- metadata +18 -10
- data/test/test_database_queries.rb +0 -29
data/Rakefile
CHANGED
@@ -16,12 +16,15 @@ begin
|
|
16
16
|
gem.add_development_dependency "test-unit", ">= 2.0"
|
17
17
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
18
|
gem.post_install_message = <<-EOM
|
19
|
-
|
20
|
-
WARNING!
|
21
|
-
|
19
|
+
==== WARNING ===================================================================
|
22
20
|
This is an early alpha version of SQLite3/Ruby FFI bindings!
|
23
|
-
|
21
|
+
Currently we support Ruby 1.9 ONLY.
|
22
|
+
|
23
|
+
If you need native bindings for Ruby 1.8 - install sqlite3-ruby instead.
|
24
|
+
You may need to uninstall this sqlite3 gem as well.
|
24
25
|
|
26
|
+
Thank you for installing sqlite3 gem! Suggestions: qoobaa@gmail.com
|
27
|
+
================================================================================
|
25
28
|
EOM
|
26
29
|
end
|
27
30
|
rescue LoadError
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
data/lib/sqlite3.rb
CHANGED
@@ -1 +1,16 @@
|
|
1
|
+
# start (required by translator)
|
2
|
+
require "time"
|
3
|
+
require "date"
|
4
|
+
# end
|
5
|
+
|
6
|
+
require "ffi"
|
7
|
+
|
8
|
+
require "sqlite3/constants"
|
9
|
+
require "sqlite3/errors"
|
10
|
+
require "sqlite3/pragmas"
|
11
|
+
require "sqlite3/statement"
|
12
|
+
require "sqlite3/translator"
|
13
|
+
require "sqlite3/resultset"
|
14
|
+
require "sqlite3/value"
|
15
|
+
require "sqlite3/encoding"
|
1
16
|
require "sqlite3/database"
|
data/lib/sqlite3/database.rb
CHANGED
@@ -1,10 +1,3 @@
|
|
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
1
|
module SQLite3
|
9
2
|
|
10
3
|
# The Database class encapsulates a single connection to a SQLite3 database.
|
@@ -64,38 +57,41 @@ module SQLite3
|
|
64
57
|
# database.
|
65
58
|
attr_accessor :type_translation
|
66
59
|
|
60
|
+
# Encoding used to comunicate with database.
|
61
|
+
attr_reader :encoding
|
62
|
+
|
67
63
|
# Create a new Database object that opens the given file. If utf16
|
68
64
|
# is +true+, the filename is interpreted as a UTF-16 encoded string.
|
69
65
|
#
|
70
66
|
# By default, the new database will return result rows as arrays
|
71
67
|
# (#results_as_hash) and has type translation disabled (#type_translation=).
|
72
68
|
def initialize(file_name, options = {})
|
73
|
-
|
69
|
+
@encoding = Encoding.find(options.fetch(:encoding, "utf-8"))
|
70
|
+
|
74
71
|
load_driver(options[:driver])
|
75
72
|
|
76
73
|
@statement_factory = options[:statement_factory] || Statement
|
77
74
|
|
78
|
-
result, @handle = @driver.open(file_name,
|
75
|
+
result, @handle = @driver.open(file_name, Encoding.utf_16?(@encoding))
|
79
76
|
Error.check(result, self, "could not open database")
|
80
77
|
|
81
78
|
@closed = false
|
82
|
-
@results_as_hash = options.fetch(:results_as_hash,false)
|
83
|
-
@type_translation = options.fetch(:type_translation,false)
|
79
|
+
@results_as_hash = options.fetch(:results_as_hash, false)
|
80
|
+
@type_translation = options.fetch(:type_translation, false)
|
84
81
|
@translator = nil
|
85
82
|
@transaction_active = false
|
86
83
|
end
|
87
84
|
|
88
85
|
# Return +true+ if the string is a valid (ie, parsable) SQL statement, and
|
89
|
-
# +false+ otherwise
|
90
|
-
|
91
|
-
|
92
|
-
@driver.complete?(string, utf16)
|
86
|
+
# +false+ otherwise
|
87
|
+
def complete?(string)
|
88
|
+
@driver.complete?(string)
|
93
89
|
end
|
94
90
|
|
95
91
|
# Return a string describing the last error to have occurred with this
|
96
92
|
# database.
|
97
|
-
def errmsg
|
98
|
-
@driver.errmsg(@handle
|
93
|
+
def errmsg
|
94
|
+
@driver.errmsg(@handle)
|
99
95
|
end
|
100
96
|
|
101
97
|
# Return an integer representing the last error to have occurred with this
|
@@ -128,30 +124,13 @@ module SQLite3
|
|
128
124
|
@closed
|
129
125
|
end
|
130
126
|
|
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
127
|
# Returns a Statement object representing the given SQL. This does not
|
149
128
|
# execute the statement; it merely prepares the statement for execution.
|
150
129
|
#
|
151
130
|
# The Statement can then be executed using Statement#execute.
|
152
131
|
#
|
153
132
|
def prepare(sql)
|
154
|
-
stmt = @statement_factory.new(self, sql)
|
133
|
+
stmt = @statement_factory.new(self, sql, Encoding.utf_16?(@encoding))
|
155
134
|
if block_given?
|
156
135
|
begin
|
157
136
|
yield stmt
|
@@ -296,20 +275,6 @@ module SQLite3
|
|
296
275
|
@driver.interrupt(@handle)
|
297
276
|
end
|
298
277
|
|
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
278
|
# Indicates that if a request for a resource terminates because that
|
314
279
|
# resource is busy, SQLite should sleep and retry for up to the indicated
|
315
280
|
# number of milliseconds. By default, SQLite does not retry
|
@@ -322,210 +287,6 @@ module SQLite3
|
|
322
287
|
Error.check(result, self)
|
323
288
|
end
|
324
289
|
|
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
290
|
# Begins a new transaction. Note that nested transactions are not allowed
|
530
291
|
# by SQLite, so attempting to nest a transaction will result in a runtime
|
531
292
|
# exception.
|
@@ -586,6 +347,8 @@ module SQLite3
|
|
586
347
|
@transaction_active
|
587
348
|
end
|
588
349
|
|
350
|
+
private
|
351
|
+
|
589
352
|
# Loads the corresponding driver, or if it is nil, attempts to locate a
|
590
353
|
# suitable driver.
|
591
354
|
def load_driver(driver)
|
@@ -611,93 +374,7 @@ module SQLite3
|
|
611
374
|
|
612
375
|
@driver = driver.new
|
613
376
|
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
377
|
|
700
378
|
end
|
701
|
-
|
702
379
|
end
|
703
380
|
|