Update vendored Sqlite3-ruby to 1.3.0

Also, some tweaks to Maruku.
This commit is contained in:
Jacques Distler 2010-06-10 22:42:33 -05:00
parent 9a80cacc34
commit 4f8759cdf3
55 changed files with 3071 additions and 8028 deletions

View file

@ -11,9 +11,9 @@ module MaRuKu
return nil return nil
end end
begin begin
require 'itex_stringsupport'
rescue LoadError
require 'instiki_stringsupport' require 'instiki_stringsupport'
rescue LoadError
require 'itex_stringsupport'
end end
parser = Itex2MML::Parser.new parser = Itex2MML::Parser.new

View file

@ -6,13 +6,9 @@ module MaRuKu; module Out; module HTML
# or return an empty array on error # or return an empty array on error
# return [] # return []
# or have a string parsed by REXML: # or have a string parsed by REXML:
begin mathml = "<code>#{tex.gsub( /&/, "&amp;" ).
require 'itex_stringsupport' gsub( /</, "&lt;" ).gsub( />/, "&gt;" ).
rescue LoadError gsub(/'/, "&#39;" ).gsub(/"/, "&quot;" )}</code>"
require 'instiki_stringsupport'
end
tex = tex.escapeHTML
mathml = "<code>#{tex}</code>"
return Document.new(mathml).root return Document.new(mathml).root
end end

View file

@ -0,0 +1,48 @@
= API Changes
* SQLite3::ResultSet used to query the database for the first row, regardless
of whether the user asked for it or not. I have removed that so that rows
will not be returned until the user asks for them. This is a subtle but
sometimes important change in behavior.
83882d2208ed189361617d5ab8532a325aaf729d
* SQLite3::Database#trace now takes either a block or an object that responds
to "call". The previous implementation passed around a VALUE that was cast
to a void *. This is dangerous because the value could get garbage collected
before the proc was called. If the user wants data passed around with the
block, they should use variables available to the closure or create an
object.
* SQLite3::Statement#step automatically converts to ruby types, where before
all values were automatically yielded as strings. This will only be a
problem for people who were accessing information about the database that
wasn't previously passed through the pure ruby conversion code.
* SQLite3::Database#errmsg no longer takes a parameter to return error
messages as UTF-16. Do people even use that? I opt for staying UTF-8 when
possible. See test_integration.rb test_errmsg_utf16
* SQLite3::Database#authorize same changes as trace
* test/test_tc_database.rb was removed because we no longer use the Driver
design pattern.
= Garbage Collection Strategy
All statements keep pointers back to their respective database connections.
The @connection instance variable on the Statement handle keeps the database
connection alive. Memory allocated for a statement handler will be freed in
two cases:
* close is called on the statement
* The SQLite3::Database object gets garbage collected
We can't free the memory for the statement in the garbage collection function
for the statement handler. The reason is because there exists a race
condition. We cannot guarantee the order in which objects will be garbage
collected. So, it is possible that a connection and a statement are up for
garbage collection. If the database connection were to be free'd before the
statement, then boom. Instead we'll be conservative and free unclosed
statements when the connection is terminated.

View file

@ -1,3 +1,95 @@
=== 1.3.0 / 2010-06-06
* Enhancements
* Complete rewrite of C-based adapter from SWIG to hand-crafted one [tenderlove]
See API_CHANGES document for details.
This closes: Bug #27300, Bug #27241, Patch #16020
* Improved UTF, Unicode, M17N, all that handling and proper BLOB handling [tenderlove, nurse]
* Added support for type translations [tenderlove]
@db.translator.add_translator('sometime') do |type, thing|
'output' # this will be returned as value for that column
end
* Experimental
* Added API to access and load extensions. [kashif]
These functions maps directly into SQLite3 own enable_load_extension()
and load_extension() C-API functions. See SQLite3::Database API documentation for details.
This closes: Patches #9178
* Bugfixes
* Corrected gem dependencies (runtime and development)
* Fixed threaded tests [Alexey Borzenkov]
* Removed GitHub gemspec
* Fixed "No definition for" warnings from RDoc
* Generate zip and tgz files for releases
* Added Luis Lavena as gem Author (maintainer)
* Prevent mkmf interfere with Mighty Snow Leopard
* Allow extension compilation search for common lib paths [kashif]
(lookup /usr/local, /opt/local and /usr)
* Corrected extension compilation under MSVC [romuloceccon]
* Define load_extension functionality based on availability [tenderlove]
* Deprecation notices for Database#query. Fixes RF #28192
=== 1.3.0.beta.2 / 2010-05-15
* Enhancements
* Added support for type translations [tenderlove]
@db.translator.add_translator('sometime') do |type, thing|
'output' # this will be returned as value for that column
end
* Bugfixes
* Allow extension compilation search for common lib paths [kashif]
(lookup /usr/local, /opt/local and /usr)
* Corrected extension compilation under MSVC [romuloceccon]
* Define load_extension functionality based on availability [tenderlove]
* Deprecation notices for Database#query. Fixes RF #28192
=== 1.3.0.beta.1 / 2010-05-10
* Enhancements
* Complete rewrite of C-based adapter from SWIG to hand-crafted one [tenderlove]
See API_CHANGES document for details.
This closes: Bug #27300, Bug #27241, Patch #16020
* Improved UTF, Unicode, M17N, all that handling and proper BLOB handling [tenderlove, nurse]
* Experimental
* Added API to access and load extensions. [kashif]
These functions maps directly into SQLite3 own enable_load_extension()
and load_extension() C-API functions. See SQLite3::Database API documentation for details.
This closes: Patches #9178
* Bugfixes
* Corrected gem dependencies (runtime and development)
* Fixed threaded tests [Alexey Borzenkov]
* Removed GitHub gemspec
* Fixed "No definition for" warnings from RDoc
* Generate zip and tgz files for releases
* Added Luis Lavena as gem Author (maintainer)
* Prevent mkmf interfere with Mighty Snow Leopard
=== 1.2.5 / 25 Jul 2009
* Check for illegal nil before executing SQL [Erik Veenstra]
* Switch to Hoe for gem task management and packaging.
* Advertise rake-compiler as development dependency.
* Build gem binaries for Windows.
* Improved Ruby 1.9 support compatibility.
* Taint returned values. Patch #20325.
* Database.open and Database.new now take an optional block [Gerrit Kaiser]
=== 1.2.4.1 (internal) / 5 Jul 2009
* Check for illegal nil before executing SQL [Erik Veenstra]
* Switch to Hoe for gem task management and packaging.
* Advertise rake-compiler as development dependency.
* Build gem binaries for Windows.
* Improved Ruby 1.9 support compatibility.
=== 1.2.4 / 27 Aug 2008 === 1.2.4 / 27 Aug 2008
* Package the updated C file for source builds. [Jamis Buck] * Package the updated C file for source builds. [Jamis Buck]

View file

@ -1,68 +0,0 @@
=== 1.2.5 / 25 Jul 2009
* Check for illegal nil before executing SQL [Erik Veenstra]
* Switch to Hoe for gem task management and packaging.
* Advertise rake-compiler as development dependency.
* Build gem binaries for Windows.
* Improved Ruby 1.9 support compatibility.
* Taint returned values. Patch #20325.
=== 1.2.4.1 (internal) / 5 Jul 2009
* Check for illegal nil before executing SQL [Erik Veenstra]
* Switch to Hoe for gem task management and packaging.
* Advertise rake-compiler as development dependency.
* Build gem binaries for Windows.
* Improved Ruby 1.9 support compatibility.
=== 1.2.4 / 27 Aug 2008
* Package the updated C file for source builds. [Jamis Buck]
=== 1.2.3 / 26 Aug 2008
* Fix incorrect permissions on database.rb and translator.rb [Various]
* Avoid using Object#extend for greater speedups [Erik Veenstra]
* Ruby 1.9 compatibility tweaks for Array#zip [jimmy88@gmail.com]
* Fix linking against Ruby 1.8.5 [Rob Holland <rob@inversepath.com>]
=== 1.2.2 / 31 May 2008
* Make the table_info method adjust the returned default value for the rows
so that the sqlite3 change in 3.3.8 and greater can be handled
transparently [Jamis Buck <jamis@37signals.com>]
* Ruby 1.9 compatibility tweaks [Roman Le Negrate <roman2k@free.fr>]
* Various performance enhancements [thanks Erik Veenstra]
* Correct busy_handler documentation [Rob Holland <rob@inversepath.com>]
* Use int_bind64 on Fixnum values larger than a 32bit C int can take. [Rob Holland <rob@inversepath.com>]
* Work around a quirk in SQLite's error reporting by calling sqlite3_reset
to produce a more informative error code upon a failure from
sqlite3_step. [Rob Holland <rob@inversepath.com>]
* Various documentation, test, and style tweaks [Rob Holland <rob@inversepath.com>]
* Be more granular with time/data translation [Rob Holland <rob@inversepath.com>]
* Use Date directly for parsing rather than going via Time [Rob Holland <rob@inversepath.com>]
* Check for the rt library and fdatasync so we link against that when
needed [Rob Holland <rob@inversepath.com>]
* Rename data structures to avoid collision on win32. based on patch
by: Luis Lavena [Rob Holland <rob@inversepath.com>]
* Add test for defaults [Daniel Rodríguez Troitiño]
* Correctly unquote double-quoted pragma defaults [Łukasz Dargiewicz <lukasz.dargiewicz@gmail.com>]

View file

@ -1,20 +1,24 @@
API_CHANGES.rdoc
CHANGELOG.rdoc
ChangeLog.cvs ChangeLog.cvs
History.txt
LICENSE LICENSE
Manifest.txt Manifest.txt
README.txt README.rdoc
Rakefile Rakefile
ext/sqlite3_api/extconf.rb ext/sqlite3/database.c
ext/sqlite3_api/sqlite3_api.i ext/sqlite3/database.h
ext/sqlite3_api/sqlite3_api_wrap.c ext/sqlite3/exception.c
ext/sqlite3/exception.h
ext/sqlite3/extconf.rb
ext/sqlite3/sqlite3.c
ext/sqlite3/sqlite3_ruby.h
ext/sqlite3/statement.c
ext/sqlite3/statement.h
faq/faq.rb faq/faq.rb
faq/faq.yml faq/faq.yml
lib/sqlite3.rb lib/sqlite3.rb
lib/sqlite3/constants.rb lib/sqlite3/constants.rb
lib/sqlite3/database.rb lib/sqlite3/database.rb
lib/sqlite3/driver/dl/api.rb
lib/sqlite3/driver/dl/driver.rb
lib/sqlite3/driver/native/driver.rb
lib/sqlite3/errors.rb lib/sqlite3/errors.rb
lib/sqlite3/pragmas.rb lib/sqlite3/pragmas.rb
lib/sqlite3/resultset.rb lib/sqlite3/resultset.rb
@ -23,19 +27,18 @@ lib/sqlite3/translator.rb
lib/sqlite3/value.rb lib/sqlite3/value.rb
lib/sqlite3/version.rb lib/sqlite3/version.rb
setup.rb setup.rb
tasks/benchmark.rake
tasks/faq.rake tasks/faq.rake
tasks/gem.rake tasks/gem.rake
tasks/native.rake tasks/native.rake
tasks/vendor_sqlite3.rake tasks/vendor_sqlite3.rake
test/bm.rb
test/driver/dl/tc_driver.rb
test/helper.rb test/helper.rb
test/native-vs-dl.rb
test/test_database.rb test/test_database.rb
test/test_errors.rb test/test_deprecated.rb
test/test_encoding.rb
test/test_integration.rb test/test_integration.rb
test/test_integration_open_close.rb test/test_integration_open_close.rb
test/test_integration_pending.rb test/test_integration_pending.rb
test/test_integration_resultset.rb test/test_integration_resultset.rb
test/test_integration_statement.rb test/test_integration_statement.rb
test/test_sqlite3.rb
test/test_statement.rb

View file

@ -1,15 +1,23 @@
= SQLite3/Ruby Interface = SQLite3/Ruby Interface
* http://github.com/luislavena/sqlite3-ruby
* http://rubyforge.org/projects/sqlite-ruby
* http://sqlite-ruby.rubyforge.org
== DESCRIPTION
This module allows Ruby programs to interface with the SQLite3 This module allows Ruby programs to interface with the SQLite3
database engine (http://www.sqlite.org). You must have the database engine (http://www.sqlite.org). You must have the
SQLite engine installed in order to build this module. SQLite engine installed in order to build this module.
Note that this module is NOT compatible with SQLite 2.x. Note that this module is NOT compatible with SQLite 2.x.
== Compilation and Installation == Compilation and Installation
Simply do the following, after installing SQLite3: Install SQLite3, enabling option SQLITE_ENABLE_COLUMN_METADATA (see
www.sqlite.org/compile.html for details).
Then do the following:
ruby setup.rb config ruby setup.rb config
ruby setup.rb setup ruby setup.rb setup
@ -25,11 +33,6 @@ If you have sqlite3 installed in a non-standard location, you can specify the lo
gem install sqlite3-ruby -- --with-sqlite3-include=/opt/local/include \ gem install sqlite3-ruby -- --with-sqlite3-include=/opt/local/include \
--with-sqlite3-lib=/opt/local/lib --with-sqlite3-lib=/opt/local/lib
Also, the gem ships with the C source-code pre-built, so (as of version 1.1.1)
you no longer need to have SWIG installed. However, if you have SWIG installed
and you want to generate the C file yourself, you can specify the
<code>--with-swig</code> option.
== Usage == Usage
For help figuring out the SQLite3/Ruby interface, check out the For help figuring out the SQLite3/Ruby interface, check out the
@ -41,7 +44,7 @@ FAQ, please send them to jamis@37signals.com
The source repository is accessible via git: The source repository is accessible via git:
git clone git://github.com/jamis/sqlite3-ruby.git git clone git://github.com/luislavena/sqlite3-ruby.git
== Contact Information == Contact Information

View file

@ -1,56 +0,0 @@
= SQLite3/Ruby Interface
* http://sqlite3-ruby.rubyforge.org
* http://rubyforge.org/projects/sqlite3-ruby
* http://github.com/jamis/sqlite3-ruby
== DESCRIPTION
This module allows Ruby programs to interface with the SQLite3
database engine (http://www.sqlite.org). You must have the
SQLite engine installed in order to build this module.
Note that this module is NOT compatible with SQLite 2.x.
== Compilation and Installation
Simply do the following, after installing SQLite3:
ruby setup.rb config
ruby setup.rb setup
ruby setup.rb install
Alternatively, you can download and install the RubyGem package for
SQLite3/Ruby (you must have RubyGems and SQLite3 installed, first):
gem install sqlite3-ruby
If you have sqlite3 installed in a non-standard location, you can specify the location of the include and lib files by doing:
gem install sqlite3-ruby -- --with-sqlite3-include=/opt/local/include \
--with-sqlite3-lib=/opt/local/lib
Also, the gem ships with the C source-code pre-built, so (as of version 1.1.1)
you no longer need to have SWIG installed. However, if you have SWIG installed
and you want to generate the C file yourself, you can specify the
<code>--with-swig</code> option.
== Usage
For help figuring out the SQLite3/Ruby interface, check out the
FAQ[http://sqlite-ruby.rubyforge.org/sqlite3/faq.html]. It includes examples of
usage. If you have any questions that you feel should be address in the
FAQ, please send them to jamis@37signals.com
== Source Code
The source repository is accessible via git:
git clone git://github.com/jamis/sqlite3-ruby.git
== Contact Information
The project page is http://rubyforge.org/projects/sqlite-ruby. There, you can
find links to mailing lists and forums that you can use to discuss this
library. Additionally, there are trackers for submitting bugs and feature
requests. Feel free to use them!

View file

@ -1,5 +1,10 @@
require 'rubygems' #
require 'rake' # NOTE: Keep this file clean.
# Add your customizations inside tasks directory.
# Thank You.
#
# load rakefile extensions (tasks) # load rakefile extensions (tasks)
Dir['tasks/*.rake'].each { |f| import f } Dir['tasks/*.rake'].sort.each { |f| load f }
# vim: syntax=ruby

View file

@ -0,0 +1,693 @@
#include <sqlite3_ruby.h>
#define REQUIRE_OPEN_DB(_ctxt) \
if(!_ctxt->db) \
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed database");
VALUE cSqlite3Database;
static VALUE sym_utf16, sym_results_as_hash, sym_type_translation;
static void deallocate(void * ctx)
{
sqlite3RubyPtr c = (sqlite3RubyPtr)ctx;
sqlite3 * db = c->db;
sqlite3_stmt * stmt;
if(db) {
while((stmt = sqlite3_next_stmt(db, NULL)) != NULL) {
sqlite3_finalize(stmt);
}
sqlite3_close(db);
}
xfree(c);
}
static VALUE allocate(VALUE klass)
{
sqlite3RubyPtr ctx = xcalloc((size_t)1, sizeof(sqlite3Ruby));
return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
}
static char *
utf16_string_value_ptr(VALUE str)
{
StringValue(str);
rb_str_buf_cat(str, "\x00", 1L);
return RSTRING_PTR(str);
}
/* call-seq: SQLite3::Database.new(file, options = {})
*
* Create a new Database object that opens the given file. If utf16
* is +true+, the filename is interpreted as a UTF-16 encoded string.
*
* By default, the new database will return result rows as arrays
* (#results_as_hash) and has type translation disabled (#type_translation=).
*/
static VALUE initialize(int argc, VALUE *argv, VALUE self)
{
sqlite3RubyPtr ctx;
VALUE file;
VALUE opts;
VALUE zvfs;
int status;
Data_Get_Struct(self, sqlite3Ruby, ctx);
rb_scan_args(argc, argv, "12", &file, &opts, &zvfs);
if(NIL_P(opts)) opts = rb_hash_new();
#ifdef HAVE_RUBY_ENCODING_H
if(UTF16_LE_P(file)) {
status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
} else {
#endif
if(Qtrue == rb_hash_aref(opts, sym_utf16)) {
status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
} else {
#ifdef HAVE_RUBY_ENCODING_H
if(!UTF8_P(file)) {
file = rb_str_export_to_enc(file, rb_utf8_encoding());
}
#endif
status = sqlite3_open_v2(
StringValuePtr(file),
&ctx->db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
NIL_P(zvfs) ? NULL : StringValuePtr(zvfs)
);
}
#ifdef HAVE_RUBY_ENCODING_H
}
#endif
CHECK(ctx->db, status)
rb_iv_set(self, "@tracefunc", Qnil);
rb_iv_set(self, "@authorizer", Qnil);
rb_iv_set(self, "@encoding", Qnil);
rb_iv_set(self, "@busy_handler", Qnil);
rb_iv_set(self, "@results_as_hash", rb_hash_aref(opts, sym_results_as_hash));
rb_iv_set(self, "@type_translation", rb_hash_aref(opts, sym_type_translation));
if(rb_block_given_p()) {
rb_yield(self);
rb_funcall(self, rb_intern("close"), 0);
}
return self;
}
/* call-seq: db.close
*
* Closes this database.
*/
static VALUE sqlite3_rb_close(VALUE self)
{
sqlite3RubyPtr ctx;
sqlite3 * db;
Data_Get_Struct(self, sqlite3Ruby, ctx);
db = ctx->db;
CHECK(db, sqlite3_close(ctx->db));
ctx->db = NULL;
return self;
}
/* call-seq: db.closed?
*
* Returns +true+ if this database instance has been closed (see #close).
*/
static VALUE closed_p(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
if(!ctx->db) return Qtrue;
return Qfalse;
}
/* call-seq: total_changes
*
* Returns the total number of changes made to this database instance
* since it was opened.
*/
static VALUE total_changes(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
return INT2NUM((long)sqlite3_total_changes(ctx->db));
}
static void tracefunc(void * data, const char *sql)
{
VALUE self = (VALUE)data;
VALUE thing = rb_iv_get(self, "@tracefunc");
rb_funcall(thing, rb_intern("call"), 1, rb_str_new2(sql));
}
/* call-seq:
* trace { |sql| ... }
* trace(Class.new { def call sql; end }.new)
*
* Installs (or removes) a block that will be invoked for every SQL
* statement executed. The block receives one parameter: the SQL statement
* executed. If the block is +nil+, any existing tracer will be uninstalled.
*/
static VALUE trace(int argc, VALUE *argv, VALUE self)
{
sqlite3RubyPtr ctx;
VALUE block;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
rb_scan_args(argc, argv, "01", &block);
if(NIL_P(block) && rb_block_given_p()) block = rb_block_proc();
rb_iv_set(self, "@tracefunc", block);
sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)self);
return self;
}
static int rb_sqlite3_busy_handler(void * ctx, int count)
{
VALUE self = (VALUE)(ctx);
VALUE handle = rb_iv_get(self, "@busy_handler");
VALUE result = rb_funcall(handle, rb_intern("call"), 1, INT2NUM((long)count));
if(Qfalse == result) return 0;
return 1;
}
/* call-seq:
* busy_handler { |count| ... }
* busy_handler(Class.new { def call count; end }.new)
*
* Register a busy handler with this database instance. When a requested
* resource is busy, this handler will be invoked. If the handler returns
* +false+, the operation will be aborted; otherwise, the resource will
* be requested again.
*
* The handler will be invoked with the name of the resource that was
* busy, and the number of times it has been retried.
*
* See also the mutually exclusive #busy_timeout.
*/
static VALUE busy_handler(int argc, VALUE *argv, VALUE self)
{
sqlite3RubyPtr ctx;
VALUE block;
int status;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
rb_scan_args(argc, argv, "01", &block);
if(NIL_P(block) && rb_block_given_p()) block = rb_block_proc();
rb_iv_set(self, "@busy_handler", block);
status = sqlite3_busy_handler(
ctx->db, NIL_P(block) ? NULL : rb_sqlite3_busy_handler, (void *)self);
CHECK(ctx->db, status);
return self;
}
/* call-seq: last_insert_row_id
*
* Obtains the unique row ID of the last row to be inserted by this Database
* instance.
*/
static VALUE last_insert_row_id(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
return LL2NUM(sqlite3_last_insert_rowid(ctx->db));
}
static VALUE sqlite3val2rb(sqlite3_value * val)
{
switch(sqlite3_value_type(val)) {
case SQLITE_INTEGER:
return LL2NUM(sqlite3_value_int64(val));
break;
case SQLITE_FLOAT:
return rb_float_new(sqlite3_value_double(val));
break;
case SQLITE_TEXT:
return rb_tainted_str_new2((const char *)sqlite3_value_text(val));
break;
case SQLITE_BLOB:
return rb_tainted_str_new2((const char *)sqlite3_value_blob(val));
break;
case SQLITE_NULL:
return Qnil;
break;
default:
rb_raise(rb_eRuntimeError, "bad type"); /* FIXME */
}
}
static void set_sqlite3_func_result(sqlite3_context * ctx, VALUE result)
{
switch(TYPE(result)) {
case T_NIL:
sqlite3_result_null(ctx);
break;
case T_FIXNUM:
sqlite3_result_int64(ctx, (sqlite3_int64)FIX2LONG(result));
break;
case T_BIGNUM:
#if SIZEOF_LONG < 8
if (RBIGNUM_LEN(result) * SIZEOF_BDIGITS <= 8) {
sqlite3_result_int64(ctx, NUM2LL(result));
break;
}
#endif
case T_FLOAT:
sqlite3_result_double(ctx, NUM2DBL(result));
break;
case T_STRING:
sqlite3_result_text(
ctx,
(const char *)StringValuePtr(result),
(int)RSTRING_LEN(result),
SQLITE_TRANSIENT
);
break;
default:
rb_raise(rb_eRuntimeError, "can't return %s",
rb_class2name(CLASS_OF(result)));
}
}
static void rb_sqlite3_func(sqlite3_context * ctx, int argc, sqlite3_value **argv)
{
VALUE callable = (VALUE)sqlite3_user_data(ctx);
VALUE * params = NULL;
VALUE result;
int i;
if (argc > 0) {
params = xcalloc((size_t)argc, sizeof(VALUE *));
for(i = 0; i < argc; i++) {
params[i] = sqlite3val2rb(argv[i]);
}
}
result = rb_funcall2(callable, rb_intern("call"), argc, params);
xfree(params);
set_sqlite3_func_result(ctx, result);
}
#ifndef HAVE_RB_PROC_ARITY
int rb_proc_arity(VALUE self)
{
return (int)NUM2INT(rb_funcall(self, rb_intern("arity"), 0));
}
#endif
/* call-seq: define_function(name) { |args,...| }
*
* Define a function named +name+ with +args+. The arity of the block
* will be used as the arity for the function defined.
*/
static VALUE define_function(VALUE self, VALUE name)
{
sqlite3RubyPtr ctx;
VALUE block;
int status;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
block = rb_block_proc();
status = sqlite3_create_function(
ctx->db,
StringValuePtr(name),
rb_proc_arity(block),
SQLITE_UTF8,
(void *)block,
rb_sqlite3_func,
NULL,
NULL
);
CHECK(ctx->db, status);
return self;
}
static int sqlite3_obj_method_arity(VALUE obj, ID id)
{
VALUE method = rb_funcall(obj, rb_intern("method"), 1, ID2SYM(id));
VALUE arity = rb_funcall(method, rb_intern("arity"), 0);
return (int)NUM2INT(arity);
}
static void rb_sqlite3_step(sqlite3_context * ctx, int argc, sqlite3_value **argv)
{
VALUE callable = (VALUE)sqlite3_user_data(ctx);
VALUE * params = NULL;
int i;
if (argc > 0) {
params = xcalloc((size_t)argc, sizeof(VALUE *));
for(i = 0; i < argc; i++) {
params[i] = sqlite3val2rb(argv[i]);
}
}
rb_funcall2(callable, rb_intern("step"), argc, params);
xfree(params);
}
static void rb_sqlite3_final(sqlite3_context * ctx)
{
VALUE callable = (VALUE)sqlite3_user_data(ctx);
VALUE result = rb_funcall(callable, rb_intern("finalize"), 0);
set_sqlite3_func_result(ctx, result);
}
/* call-seq: define_aggregator(name, aggregator)
*
* Define an aggregate function named +name+ using the object +aggregator+.
* +aggregator+ must respond to +step+ and +finalize+. +step+ will be called
* with row information and +finalize+ must return the return value for the
* aggregator function.
*/
static VALUE define_aggregator(VALUE self, VALUE name, VALUE aggregator)
{
sqlite3RubyPtr ctx;
int arity, status;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
arity = sqlite3_obj_method_arity(aggregator, rb_intern("step"));
status = sqlite3_create_function(
ctx->db,
StringValuePtr(name),
arity,
SQLITE_UTF8,
(void *)aggregator,
NULL,
rb_sqlite3_step,
rb_sqlite3_final
);
rb_iv_set(self, "@agregator", aggregator);
CHECK(ctx->db, status);
return self;
}
/* call-seq: interrupt
*
* Interrupts the currently executing operation, causing it to abort.
*/
static VALUE interrupt(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
sqlite3_interrupt(ctx->db);
return self;
}
/* call-seq: errmsg
*
* Return a string describing the last error to have occurred with this
* database.
*/
static VALUE errmsg(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
return rb_str_new2(sqlite3_errmsg(ctx->db));
}
/* call-seq: errcode
*
* Return an integer representing the last error to have occurred with this
* database.
*/
static VALUE errcode_(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
return INT2NUM((long)sqlite3_errcode(ctx->db));
}
/* call-seq: complete?(sql)
*
* Return +true+ if the string is a valid (ie, parsable) SQL statement, and
* +false+ otherwise.
*/
static VALUE complete_p(VALUE UNUSED(self), VALUE sql)
{
if(sqlite3_complete(StringValuePtr(sql)))
return Qtrue;
return Qfalse;
}
/* call-seq: changes
*
* Returns the number of changes made to this database instance by the last
* operation performed. Note that a "delete from table" without a where
* clause will not affect this value.
*/
static VALUE changes(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
return INT2NUM(sqlite3_changes(ctx->db));
}
static int rb_sqlite3_auth(
void *ctx,
int _action,
const char * _a,
const char * _b,
const char * _c,
const char * _d)
{
VALUE self = (VALUE)ctx;
VALUE action = INT2NUM(_action);
VALUE a = _a ? rb_str_new2(_a) : Qnil;
VALUE b = _b ? rb_str_new2(_b) : Qnil;
VALUE c = _c ? rb_str_new2(_c) : Qnil;
VALUE d = _d ? rb_str_new2(_d) : Qnil;
VALUE callback = rb_iv_get(self, "@authorizer");
VALUE result = rb_funcall(callback, rb_intern("call"), 5, action, a, b, c, d);
if(T_FIXNUM == TYPE(result)) return (int)NUM2INT(result);
if(Qtrue == result) return SQLITE_OK;
if(Qfalse == result) return SQLITE_DENY;
return SQLITE_IGNORE;
}
/* call-seq: set_authorizer = auth
*
* Set the authorizer for this database. +auth+ must respond to +call+, and
* +call+ must take 5 arguments.
*
* Installs (or removes) a block that will be invoked for every access
* to the database. If the block returns 0 (or +true+), the statement
* is allowed to proceed. Returning 1 or false causes an authorization error to
* occur, and returning 2 or nil causes the access to be silently denied.
*/
static VALUE set_authorizer(VALUE self, VALUE authorizer)
{
sqlite3RubyPtr ctx;
int status;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
status = sqlite3_set_authorizer(
ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)self
);
CHECK(ctx->db, status);
rb_iv_set(self, "@authorizer", authorizer);
return self;
}
/* call-seq: db.busy_timeout = ms
*
* Indicates that if a request for a resource terminates because that
* resource is busy, SQLite should sleep and retry for up to the indicated
* number of milliseconds. By default, SQLite does not retry
* busy resources. To restore the default behavior, send 0 as the
* +ms+ parameter.
*
* See also the mutually exclusive #busy_handler.
*/
static VALUE set_busy_timeout(VALUE self, VALUE timeout)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
CHECK(ctx->db, sqlite3_busy_timeout(ctx->db, (int)NUM2INT(timeout)));
return self;
}
/* call-seq: db.load_extension(file)
*
* Loads an SQLite extension library from the named file. Extension
* loading must be enabled using db.enable_load_extension(1) prior
* to calling this API.
*/
static VALUE load_extension(VALUE self, VALUE file)
{
sqlite3RubyPtr ctx;
int status;
char *errMsg;
VALUE errexp;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
status = sqlite3_load_extension(ctx->db, RSTRING_PTR(file), 0, &errMsg);
if (status != SQLITE_OK)
{
errexp = rb_exc_new2(rb_eRuntimeError, errMsg);
sqlite3_free(errMsg);
rb_exc_raise(errexp);
}
return self;
}
/* call-seq: db.enable_load_extension(onoff)
*
* Enable or disable extension loading.
*/
static VALUE enable_load_extension(VALUE self, VALUE onoff)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
CHECK(ctx->db, sqlite3_enable_load_extension(ctx->db, (int)NUM2INT(onoff)));
return self;
}
#ifdef HAVE_RUBY_ENCODING_H
static int enc_cb(void * _self, int UNUSED(columns), char **data, char **UNUSED(names))
{
VALUE self = (VALUE)_self;
int index = rb_enc_find_index(data[0]);
rb_encoding * e = rb_enc_from_index(index);
rb_iv_set(self, "@encoding", rb_enc_from_encoding(e));
return 0;
}
/* call-seq: db.encoding
*
* Fetch the encoding set on this database
*/
static VALUE db_encoding(VALUE self)
{
sqlite3RubyPtr ctx;
VALUE enc;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
enc = rb_iv_get(self, "@encoding");
if(NIL_P(enc)) {
sqlite3_exec(ctx->db, "PRAGMA encoding", enc_cb, (void *)self, NULL);
}
return rb_iv_get(self, "@encoding");
}
#endif
void init_sqlite3_database()
{
ID id_utf16, id_results_as_hash, id_type_translation;
#if 0
VALUE mSqlite3 = rb_define_module("SQLite3");
#endif
cSqlite3Database = rb_define_class_under(mSqlite3, "Database", rb_cObject);
rb_define_alloc_func(cSqlite3Database, allocate);
rb_define_method(cSqlite3Database, "initialize", initialize, -1);
rb_define_method(cSqlite3Database, "close", sqlite3_rb_close, 0);
rb_define_method(cSqlite3Database, "closed?", closed_p, 0);
rb_define_method(cSqlite3Database, "total_changes", total_changes, 0);
rb_define_method(cSqlite3Database, "trace", trace, -1);
rb_define_method(cSqlite3Database, "last_insert_row_id", last_insert_row_id, 0);
rb_define_method(cSqlite3Database, "define_function", define_function, 1);
rb_define_method(cSqlite3Database, "define_aggregator", define_aggregator, 2);
rb_define_method(cSqlite3Database, "interrupt", interrupt, 0);
rb_define_method(cSqlite3Database, "errmsg", errmsg, 0);
rb_define_method(cSqlite3Database, "errcode", errcode_, 0);
rb_define_method(cSqlite3Database, "complete?", complete_p, 1);
rb_define_method(cSqlite3Database, "changes", changes, 0);
rb_define_method(cSqlite3Database, "authorizer=", set_authorizer, 1);
rb_define_method(cSqlite3Database, "busy_handler", busy_handler, -1);
rb_define_method(cSqlite3Database, "busy_timeout=", set_busy_timeout, 1);
#ifdef HAVE_SQLITE3_LOAD_EXTENSION
rb_define_method(cSqlite3Database, "load_extension", load_extension, 1);
#endif
#ifdef HAVE_SQLITE3_ENABLE_LOAD_EXTENSION
rb_define_method(cSqlite3Database, "enable_load_extension", enable_load_extension, 1);
#endif
#ifdef HAVE_RUBY_ENCODING_H
rb_define_method(cSqlite3Database, "encoding", db_encoding, 0);
#endif
id_utf16 = rb_intern("utf16");
sym_utf16 = ID2SYM(id_utf16);
id_results_as_hash = rb_intern("results_as_hash");
sym_results_as_hash = ID2SYM(id_results_as_hash);
id_type_translation = rb_intern("type_translation");
sym_type_translation = ID2SYM(id_type_translation);
}

View file

@ -0,0 +1,15 @@
#ifndef SQLITE3_DATABASE_RUBY
#define SQLITE3_DATABASE_RUBY
#include <sqlite3_ruby.h>
struct _sqlite3Ruby {
sqlite3 *db;
};
typedef struct _sqlite3Ruby sqlite3Ruby;
typedef sqlite3Ruby * sqlite3RubyPtr;
void init_sqlite3_database();
#endif

View file

@ -0,0 +1,94 @@
#include <sqlite3_ruby.h>
void rb_sqlite3_raise(sqlite3 * db, int status)
{
VALUE klass = Qnil;
switch(status) {
case SQLITE_OK:
return;
break;
case SQLITE_ERROR:
klass = rb_path2class("SQLite3::SQLException");
break;
case SQLITE_INTERNAL:
klass = rb_path2class("SQLite3::InternalException");
break;
case SQLITE_PERM:
klass = rb_path2class("SQLite3::PermissionException");
break;
case SQLITE_ABORT:
klass = rb_path2class("SQLite3::AbortException");
break;
case SQLITE_BUSY:
klass = rb_path2class("SQLite3::BusyException");
break;
case SQLITE_LOCKED:
klass = rb_path2class("SQLite3::LockedException");
break;
case SQLITE_NOMEM:
klass = rb_path2class("SQLite3::MemoryException");
break;
case SQLITE_READONLY:
klass = rb_path2class("SQLite3::ReadOnlyException");
break;
case SQLITE_INTERRUPT:
klass = rb_path2class("SQLite3::InterruptException");
break;
case SQLITE_IOERR:
klass = rb_path2class("SQLite3::IOException");
break;
case SQLITE_CORRUPT:
klass = rb_path2class("SQLite3::CorruptException");
break;
case SQLITE_NOTFOUND:
klass = rb_path2class("SQLite3::NotFoundException");
break;
case SQLITE_FULL:
klass = rb_path2class("SQLite3::FullException");
break;
case SQLITE_CANTOPEN:
klass = rb_path2class("SQLite3::CantOpenException");
break;
case SQLITE_PROTOCOL:
klass = rb_path2class("SQLite3::ProtocolException");
break;
case SQLITE_EMPTY:
klass = rb_path2class("SQLite3::EmptyException");
break;
case SQLITE_SCHEMA:
klass = rb_path2class("SQLite3::SchemaChangedException");
break;
case SQLITE_TOOBIG:
klass = rb_path2class("SQLite3::TooBigException");
break;
case SQLITE_CONSTRAINT:
klass = rb_path2class("SQLite3::ConstraintException");
break;
case SQLITE_MISMATCH:
klass = rb_path2class("SQLite3::MismatchException");
break;
case SQLITE_MISUSE:
klass = rb_path2class("SQLite3::MisuseException");
break;
case SQLITE_NOLFS:
klass = rb_path2class("SQLite3::UnsupportedException");
break;
case SQLITE_AUTH:
klass = rb_path2class("SQLite3::AuthorizationException");
break;
case SQLITE_FORMAT:
klass = rb_path2class("SQLite3::FormatException");
break;
case SQLITE_RANGE:
klass = rb_path2class("SQLite3::RangeException");
break;
case SQLITE_NOTADB:
klass = rb_path2class("SQLite3::NotADatabaseException");
break;
default:
klass = rb_eRuntimeError;
}
rb_raise(klass, "%s", sqlite3_errmsg(db));
}

View file

@ -0,0 +1,8 @@
#ifndef SQLITE3_EXCEPTION_RUBY
#define SQLITE3_EXCEPTION_RUBY
#define CHECK(_db, _status) rb_sqlite3_raise(_db, _status);
void rb_sqlite3_raise(sqlite3 * db, int status);
#endif

View file

@ -0,0 +1,39 @@
ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
require 'mkmf'
# :stopdoc:
RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
sqlite = dir_config('sqlite3', ['/usr/local', '/opt/local', '/usr'])
if RUBY_PLATFORM =~ /mswin/
$CFLAGS << ' -W3'
else
$CFLAGS << ' -O3 -Wall -Wcast-qual -Wwrite-strings -Wconversion' <<
' -Wmissing-noreturn -Winline'
end
def asplode missing
if RUBY_PLATFORM =~ /mswin/
abort "#{missing} is missing. Install SQLite3 from " +
"http://www.sqlite.org/ first."
else
abort "#{missing} is missing. Try 'port install sqlite3 +universal' " +
"or 'yum install sqlite3-devel'"
end
end
asplode('sqlite3.h') unless find_header 'sqlite3.h'
asplode('sqlite3') unless find_library 'sqlite3', 'sqlite3_libversion_number'
# Functions defined in 1.9 but not 1.8
have_func('rb_proc_arity')
# These functions may not be defined
have_func('sqlite3_column_database_name')
have_func('sqlite3_enable_load_extension')
have_func('sqlite3_load_extension')
create_makefile('sqlite3/sqlite3_native')

View file

@ -0,0 +1,33 @@
#include <sqlite3_ruby.h>
VALUE mSqlite3;
VALUE cSqlite3Blob;
static VALUE libversion(VALUE UNUSED(klass))
{
return INT2NUM(sqlite3_libversion_number());
}
void Init_sqlite3_native()
{
/*
* SQLite3 is a wrapper around the popular database
* sqlite[http://sqlite.org].
*
* For an example of usage, see SQLite3::Database.
*/
mSqlite3 = rb_define_module("SQLite3");
/* A class for differentiating between strings and blobs, when binding them
* into statements.
*/
cSqlite3Blob = rb_define_class_under(mSqlite3, "Blob", rb_cString);
/* Initialize the sqlite3 library */
sqlite3_initialize();
init_sqlite3_database();
init_sqlite3_statement();
rb_define_singleton_method(mSqlite3, "libversion", libversion, 0);
}

View file

@ -0,0 +1,43 @@
#ifndef SQLITE3_RUBY
#define SQLITE3_RUBY
#include <ruby.h>
#ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define UNUSED(x) /*@unused@*/ x
#else
# define UNUSED(x) x
#endif
#ifndef RBIGNUM_LEN
#define RBIGNUM_LEN(x) RBIGNUM(x)->len
#endif
#ifdef HAVE_RUBY_ENCODING_H
#include <ruby/encoding.h>
#define UTF8_P(_obj) (rb_enc_get_index(_obj) == rb_utf8_encindex())
#define UTF16_LE_P(_obj) (rb_enc_get_index(_obj) == rb_enc_find_index("UTF-16LE"))
#define SQLITE3_UTF8_STR_NEW2(_obj) \
(rb_enc_associate_index(rb_str_new2(_obj), rb_utf8_encindex()))
#else
#define SQLITE3_UTF8_STR_NEW2(_obj) (rb_str_new2(_obj))
#endif
#include <sqlite3.h>
extern VALUE mSqlite3;
extern VALUE cSqlite3Blob;
#include <database.h>
#include <statement.h>
#include <exception.h>
#endif

View file

@ -0,0 +1,419 @@
#include <sqlite3_ruby.h>
#define REQUIRE_OPEN_STMT(_ctxt) \
if(!_ctxt->st) \
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed statement");
VALUE cSqlite3Statement;
static void deallocate(void * ctx)
{
sqlite3StmtRubyPtr c = (sqlite3StmtRubyPtr)ctx;
xfree(c);
}
static VALUE allocate(VALUE klass)
{
sqlite3StmtRubyPtr ctx = xcalloc((size_t)1, sizeof(sqlite3StmtRuby));
ctx->st = NULL;
ctx->done_p = 0;
return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
}
/* call-seq: SQLite3::Statement.new(db, sql)
*
* Create a new statement attached to the given Database instance, and which
* encapsulates the given SQL text. If the text contains more than one
* statement (i.e., separated by semicolons), then the #remainder property
* will be set to the trailing text.
*/
static VALUE initialize(VALUE self, VALUE db, VALUE sql)
{
sqlite3RubyPtr db_ctx;
sqlite3StmtRubyPtr ctx;
const char *tail = NULL;
int status;
Data_Get_Struct(db, sqlite3Ruby, db_ctx);
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
if(!db_ctx->db)
rb_raise(rb_eArgError, "prepare called on a closed database");
#ifdef HAVE_RUBY_ENCODING_H
if(!UTF8_P(sql)) {
VALUE encoding = rb_funcall(db, rb_intern("encoding"), 0);
rb_encoding * enc = NIL_P(encoding) ? rb_utf8_encoding() :
rb_to_encoding(encoding);
sql = rb_str_export_to_enc(sql, enc);
}
#endif
status = sqlite3_prepare_v2(
db_ctx->db,
(const char *)StringValuePtr(sql),
(int)RSTRING_LEN(sql),
&ctx->st,
&tail
);
CHECK(db_ctx->db, status);
rb_iv_set(self, "@connection", db);
rb_iv_set(self, "@remainder", rb_str_new2(tail));
rb_iv_set(self, "@columns", Qnil);
rb_iv_set(self, "@types", Qnil);
return self;
}
/* call-seq: stmt.close
*
* Closes the statement by finalizing the underlying statement
* handle. The statement must not be used after being closed.
*/
static VALUE sqlite3_rb_close(VALUE self)
{
sqlite3StmtRubyPtr ctx;
sqlite3 * db;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
db = sqlite3_db_handle(ctx->st);
CHECK(db, sqlite3_finalize(ctx->st));
ctx->st = NULL;
return self;
}
/* call-seq: stmt.closed?
*
* Returns true if the statement has been closed.
*/
static VALUE closed_p(VALUE self)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
if(!ctx->st) return Qtrue;
return Qfalse;
}
static VALUE step(VALUE self)
{
sqlite3StmtRubyPtr ctx;
sqlite3_stmt *stmt;
int value, length;
VALUE list;
#ifdef HAVE_RUBY_ENCODING_H
rb_encoding * internal_encoding;
int enc_index;
#endif
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
if(ctx->done_p) return Qnil;
#ifdef HAVE_RUBY_ENCODING_H
{
VALUE db = rb_iv_get(self, "@connection");
VALUE encoding = rb_funcall(db, rb_intern("encoding"), 0);
enc_index = NIL_P(encoding) ? rb_utf8_encindex() : rb_to_encoding_index(encoding);
internal_encoding = rb_default_internal_encoding();
}
#endif
stmt = ctx->st;
value = sqlite3_step(stmt);
length = sqlite3_column_count(stmt);
list = rb_ary_new2((long)length);
switch(value) {
case SQLITE_ROW:
{
int i;
for(i = 0; i < length; i++) {
switch(sqlite3_column_type(stmt, i)) {
case SQLITE_INTEGER:
rb_ary_push(list, LL2NUM(sqlite3_column_int64(stmt, i)));
break;
case SQLITE_FLOAT:
rb_ary_push(list, rb_float_new(sqlite3_column_double(stmt, i)));
break;
case SQLITE_TEXT:
{
VALUE str = rb_tainted_str_new(
(const char *)sqlite3_column_text(stmt, i),
(long)sqlite3_column_bytes(stmt, i)
);
#ifdef HAVE_RUBY_ENCODING_H
rb_enc_associate_index(str, enc_index);
if(internal_encoding)
str = rb_str_export_to_enc(str, internal_encoding);
#endif
rb_ary_push(list, str);
}
break;
case SQLITE_BLOB:
{
VALUE str = rb_tainted_str_new(
(const char *)sqlite3_column_blob(stmt, i),
(long)sqlite3_column_bytes(stmt, i)
);
rb_ary_push(list, str);
}
break;
case SQLITE_NULL:
rb_ary_push(list, Qnil);
break;
default:
rb_raise(rb_eRuntimeError, "bad type");
}
}
}
break;
case SQLITE_DONE:
ctx->done_p = 1;
return Qnil;
break;
default:
CHECK(sqlite3_db_handle(ctx->st), value);
}
return list;
}
/* call-seq: stmt.bind_param(key, value)
*
* Binds value to the named (or positional) placeholder. If +param+ is a
* Fixnum, it is treated as an index for a positional placeholder.
* Otherwise it is used as the name of the placeholder to bind to.
*
* See also #bind_params.
*/
static VALUE bind_param(VALUE self, VALUE key, VALUE value)
{
sqlite3StmtRubyPtr ctx;
int status;
int index;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
switch(TYPE(key)) {
case T_SYMBOL:
key = rb_funcall(key, rb_intern("to_s"), 0);
case T_STRING:
if(RSTRING_PTR(key)[0] != ':') key = rb_str_plus(rb_str_new2(":"), key);
index = sqlite3_bind_parameter_index(ctx->st, StringValuePtr(key));
break;
default:
index = (int)NUM2INT(key);
}
if(index == 0)
rb_raise(rb_path2class("SQLite3::Exception"), "no such bind parameter");
switch(TYPE(value)) {
case T_STRING:
if(CLASS_OF(value) == cSqlite3Blob
#ifdef HAVE_RUBY_ENCODING_H
|| rb_enc_get_index(value) == rb_ascii8bit_encindex()
#endif
) {
status = sqlite3_bind_blob(
ctx->st,
index,
(const char *)StringValuePtr(value),
(int)RSTRING_LEN(value),
SQLITE_TRANSIENT
);
} else {
#ifdef HAVE_RUBY_ENCODING_H
if(!UTF8_P(value)) {
VALUE db = rb_iv_get(self, "@connection");
VALUE encoding = rb_funcall(db, rb_intern("encoding"), 0);
rb_encoding * enc = rb_to_encoding(encoding);
value = rb_str_export_to_enc(value, enc);
}
#endif
status = sqlite3_bind_text(
ctx->st,
index,
(const char *)StringValuePtr(value),
(int)RSTRING_LEN(value),
SQLITE_TRANSIENT
);
}
break;
case T_BIGNUM:
#if SIZEOF_LONG < 8
if (RBIGNUM_LEN(value) * SIZEOF_BDIGITS <= 8) {
status = sqlite3_bind_int64(ctx->st, index, (sqlite3_int64)NUM2LL(value));
break;
}
#endif
case T_FLOAT:
status = sqlite3_bind_double(ctx->st, index, NUM2DBL(value));
break;
case T_FIXNUM:
status = sqlite3_bind_int64(ctx->st, index, (sqlite3_int64)FIX2LONG(value));
break;
case T_NIL:
status = sqlite3_bind_null(ctx->st, index);
break;
default:
rb_raise(rb_eRuntimeError, "can't prepare %s",
rb_class2name(CLASS_OF(value)));
break;
}
CHECK(sqlite3_db_handle(ctx->st), status);
return self;
}
/* call-seq: stmt.reset!
*
* Resets the statement. This is typically done internally, though it might
* occassionally be necessary to manually reset the statement.
*/
static VALUE reset_bang(VALUE self)
{
sqlite3StmtRubyPtr ctx;
int status;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
status = sqlite3_reset(ctx->st);
CHECK(sqlite3_db_handle(ctx->st), status);
ctx->done_p = 0;
return self;
}
/* call-seq: stmt.done?
*
* returns true if all rows have been returned.
*/
static VALUE done_p(VALUE self)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
if(ctx->done_p) return Qtrue;
return Qfalse;
}
/* call-seq: stmt.column_count
*
* Returns the number of columns to be returned for this statement
*/
static VALUE column_count(VALUE self)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
return INT2NUM((long)sqlite3_column_count(ctx->st));
}
/* call-seq: stmt.column_name(index)
*
* Get the column name at +index+. 0 based.
*/
static VALUE column_name(VALUE self, VALUE index)
{
sqlite3StmtRubyPtr ctx;
const char * name;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
name = sqlite3_column_name(ctx->st, (int)NUM2INT(index));
if(name) return rb_str_new2(name);
return Qnil;
}
/* call-seq: stmt.column_decltype(index)
*
* Get the column type at +index+. 0 based.
*/
static VALUE column_decltype(VALUE self, VALUE index)
{
sqlite3StmtRubyPtr ctx;
const char * name;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
if(name) return rb_str_new2(name);
return Qnil;
}
/* call-seq: stmt.bind_parameter_count
*
* Return the number of bind parameters
*/
static VALUE bind_parameter_count(VALUE self)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
return INT2NUM((long)sqlite3_bind_parameter_count(ctx->st));
}
#ifdef HAVE_SQLITE3_COLUMN_DATABASE_NAME
/* call-seq: stmt.database_name(column_index)
*
* Return the database name for the column at +column_index+
*/
static VALUE database_name(VALUE self, VALUE index)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
return SQLITE3_UTF8_STR_NEW2(
sqlite3_column_database_name(ctx->st, NUM2INT(index)));
}
#endif
void init_sqlite3_statement()
{
cSqlite3Statement = rb_define_class_under(mSqlite3, "Statement", rb_cObject);
rb_define_alloc_func(cSqlite3Statement, allocate);
rb_define_method(cSqlite3Statement, "initialize", initialize, 2);
rb_define_method(cSqlite3Statement, "close", sqlite3_rb_close, 0);
rb_define_method(cSqlite3Statement, "closed?", closed_p, 0);
rb_define_method(cSqlite3Statement, "bind_param", bind_param, 2);
rb_define_method(cSqlite3Statement, "reset!", reset_bang, 0);
rb_define_method(cSqlite3Statement, "step", step, 0);
rb_define_method(cSqlite3Statement, "done?", done_p, 0);
rb_define_method(cSqlite3Statement, "column_count", column_count, 0);
rb_define_method(cSqlite3Statement, "column_name", column_name, 1);
rb_define_method(cSqlite3Statement, "column_decltype", column_decltype, 1);
rb_define_method(cSqlite3Statement, "bind_parameter_count", bind_parameter_count, 0);
#ifdef HAVE_SQLITE3_COLUMN_DATABASE_NAME
rb_define_method(cSqlite3Statement, "database_name", database_name, 1);
#endif
}

View file

@ -0,0 +1,16 @@
#ifndef SQLITE3_STATEMENT_RUBY
#define SQLITE3_STATEMENT_RUBY
#include <sqlite3_ruby.h>
struct _sqlite3StmtRuby {
sqlite3_stmt *st;
int done_p;
};
typedef struct _sqlite3StmtRuby sqlite3StmtRuby;
typedef sqlite3StmtRuby * sqlite3StmtRubyPtr;
void init_sqlite3_statement();
#endif

View file

@ -1,4 +0,0 @@
extconf.rb
post-clean.rb
post-distclean.rb
sqlite3_api.i

View file

@ -1,146 +0,0 @@
SHELL = /bin/sh
#### Start of system configuration section. ####
srcdir = .
topdir = /opt/local/lib/ruby/1.8/i686-darwin9.2.2
hdrdir = $(topdir)
VPATH = $(srcdir):$(topdir):$(hdrdir)
prefix = $(DESTDIR)/opt/local
exec_prefix = $(prefix)
sitedir = $(prefix)/lib/ruby/site_ruby
rubylibdir = $(libdir)/ruby/$(ruby_version)
archdir = $(rubylibdir)/$(arch)
sbindir = $(exec_prefix)/sbin
vendordir = $(prefix)/lib/ruby/vendor_ruby
datadir = $(prefix)/share
includedir = $(prefix)/include
infodir = $(prefix)/info
sysconfdir = $(prefix)/etc
mandir = $(DESTDIR)/opt/local/share/man
libdir = $(exec_prefix)/lib
sharedstatedir = $(prefix)/com
oldincludedir = $(DESTDIR)/usr/include
sitearchdir = $(sitelibdir)/$(sitearch)
vendorarchdir = $(vendorlibdir)/$(vendorarch)
bindir = $(exec_prefix)/bin
localstatedir = $(prefix)/var
vendorlibdir = $(vendordir)/$(ruby_version)
sitelibdir = $(sitedir)/$(ruby_version)
libexecdir = $(exec_prefix)/libexec
CC = gcc
LIBRUBY = $(LIBRUBY_SO)
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
RUBY_EXTCONF_H =
CFLAGS = -fno-common -O2 -fno-common -pipe -fno-common
INCFLAGS = -I. -I. -I/opt/local/lib/ruby/1.8/i686-darwin9.2.2 -I.
CPPFLAGS = -DHAVE_SQLITE3_H -I/usr/local/include -I/opt/local/include
CXXFLAGS = $(CFLAGS)
DLDFLAGS = -L. -L/opt/local/lib
LDSHARED = cc -dynamic -bundle -undefined suppress -flat_namespace
AR = ar
EXEEXT =
RUBY_INSTALL_NAME = ruby
RUBY_SO_NAME = ruby
arch = i686-darwin9.2.2
sitearch = i686-darwin9.2.2
vendorarch = i686-darwin9.2.2
ruby_version = 1.8
ruby = /opt/local/bin/ruby
RUBY = $(ruby)
RM = rm -f
MAKEDIRS = mkdir -p
INSTALL = /usr/bin/install -c
INSTALL_PROG = $(INSTALL) -m 0755
INSTALL_DATA = $(INSTALL) -m 644
COPY = cp
#### End of system configuration section. ####
preload =
libpath = . $(libdir) /usr/local/lib
LIBPATH = -L"." -L"$(libdir)" -L"/usr/local/lib"
DEFFILE =
CLEANFILES = mkmf.log
DISTCLEANFILES =
extout =
extout_prefix =
target_prefix =
LOCAL_LIBS =
LIBS = $(LIBRUBYARG_SHARED) -lsqlite3 -lpthread -ldl -lobjc
SRCS = sqlite3_api_wrap.c
OBJS = sqlite3_api_wrap.o
TARGET = sqlite3_api
DLLIB = $(TARGET).bundle
EXTSTATIC =
STATIC_LIB =
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
TARGET_SO = $(DLLIB)
CLEANLIBS = $(TARGET).bundle $(TARGET).il? $(TARGET).tds $(TARGET).map
CLEANOBJS = *.o *.a *.s[ol] *.pdb *.exp *.bak
all: $(DLLIB)
static: $(STATIC_LIB)
clean:
@-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
distclean: clean
@-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
@-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
realclean: distclean
install: install-so install-rb
install-so: $(RUBYARCHDIR)
install-so: $(RUBYARCHDIR)/$(DLLIB)
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
install-rb: pre-install-rb install-rb-default
install-rb-default: pre-install-rb-default
pre-install-rb: Makefile
pre-install-rb-default: Makefile
$(RUBYARCHDIR):
$(MAKEDIRS) $@
site-install: site-install-so site-install-rb
site-install-so: install-so
site-install-rb: install-rb
.SUFFIXES: .c .m .cc .cxx .cpp .C .o
.cc.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
.cxx.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
.cpp.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
.C.o:
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
.c.o:
$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $<
$(DLLIB): $(OBJS)
@-$(RM) $@
$(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
$(OBJS): ruby.h defines.h

View file

@ -1,10 +0,0 @@
require 'mkmf'
dir_config( "sqlite3" )
have_library( "rt", "fdatasync" )
if have_header( "sqlite3.h" ) && have_library( "sqlite3", "sqlite3_open" ) then
$CFLAGS << " -fno-strict-aliasing" unless RUBY_PLATFORM =~ /mswin/
create_makefile( "sqlite3_api" )
end

View file

@ -1,71 +0,0 @@
have_library: checking for fdatasync() in -lrt... -------------------- no
"gcc -o conftest -I. -I/opt/local/lib/ruby/1.8/i686-darwin9.2.2 -I. -I/usr/local/include -I/opt/local/include -O2 -fno-common -pipe -fno-common conftest.c -L"." -L"/opt/local/lib" -L"/usr/local/lib" -L. -L/opt/local/lib -lruby-static -lrt -lpthread -ldl -lobjc "
conftest.c: In function t:
conftest.c:3: error: fdatasync undeclared (first use in this function)
conftest.c:3: error: (Each undeclared identifier is reported only once
conftest.c:3: error: for each function it appears in.)
checked program was:
/* begin */
1: /*top*/
2: int main() { return 0; }
3: int t() { void ((*volatile p)()); p = (void ((*)()))fdatasync; return 0; }
/* end */
"gcc -o conftest -I. -I/opt/local/lib/ruby/1.8/i686-darwin9.2.2 -I. -I/usr/local/include -I/opt/local/include -O2 -fno-common -pipe -fno-common conftest.c -L"." -L"/opt/local/lib" -L"/usr/local/lib" -L. -L/opt/local/lib -lruby-static -lrt -lpthread -ldl -lobjc "
ld: library not found for -lrt
collect2: ld returned 1 exit status
checked program was:
/* begin */
1: /*top*/
2: int main() { return 0; }
3: int t() { fdatasync(); return 0; }
/* end */
"gcc -o conftest -I. -I/opt/local/lib/ruby/1.8/i686-darwin9.2.2 -I. -I/usr/local/include -I/opt/local/include -O2 -fno-common -pipe -fno-common conftest.c -L"." -L"/opt/local/lib" -L"/usr/local/lib" -L. -L/opt/local/lib -lruby-static -lrt -lpthread -ldl -lobjc "
ld: library not found for -lrt
collect2: ld returned 1 exit status
checked program was:
/* begin */
1: int fdatasync();
2: /*top*/
3: int main() { return 0; }
4: int t() { fdatasync(); return 0; }
/* end */
--------------------
have_header: checking for sqlite3.h... -------------------- yes
"gcc -E -I. -I/opt/local/lib/ruby/1.8/i686-darwin9.2.2 -I. -I/usr/local/include -I/opt/local/include -O2 -fno-common -pipe -fno-common conftest.c -o conftest.i"
checked program was:
/* begin */
1: #include <sqlite3.h>
/* end */
--------------------
have_library: checking for sqlite3_open() in -lsqlite3... -------------------- yes
"gcc -o conftest -I. -I/opt/local/lib/ruby/1.8/i686-darwin9.2.2 -I. -I/usr/local/include -I/opt/local/include -O2 -fno-common -pipe -fno-common conftest.c -L"." -L"/opt/local/lib" -L"/usr/local/lib" -L. -L/opt/local/lib -lruby-static -lsqlite3 -lpthread -ldl -lobjc "
conftest.c: In function t:
conftest.c:3: error: sqlite3_open undeclared (first use in this function)
conftest.c:3: error: (Each undeclared identifier is reported only once
conftest.c:3: error: for each function it appears in.)
checked program was:
/* begin */
1: /*top*/
2: int main() { return 0; }
3: int t() { void ((*volatile p)()); p = (void ((*)()))sqlite3_open; return 0; }
/* end */
"gcc -o conftest -I. -I/opt/local/lib/ruby/1.8/i686-darwin9.2.2 -I. -I/usr/local/include -I/opt/local/include -O2 -fno-common -pipe -fno-common conftest.c -L"." -L"/opt/local/lib" -L"/usr/local/lib" -L. -L/opt/local/lib -lruby-static -lsqlite3 -lpthread -ldl -lobjc "
checked program was:
/* begin */
1: /*top*/
2: int main() { return 0; }
3: int t() { sqlite3_open(); return 0; }
/* end */
--------------------

View file

@ -1,362 +0,0 @@
%module "SQLite3::driver::native::API"
%include "typemaps.i"
%{
#include <sqlite3.h>
#include "ruby.h"
#ifndef RSTRING_PTR
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
#endif
#ifndef RSTRING_LEN
#define RSTRING_LEN(s) (RSTRING(s)->len)
#endif
#ifndef STR2CSTR
#define STR2CSTR StringValueCStr
#endif
#define Init_API Init_sqlite3_api
struct CallbackData {
VALUE proc;
VALUE proc2;
VALUE data;
};
typedef struct CallbackData CallbackData;
typedef void RUBY_BLOB;
typedef void RUBY_VALBLOB;
int Sqlite3_ruby_busy_handler(void* data,int value) {
VALUE result;
CallbackData *cb = (CallbackData*)data;
result = rb_funcall(
cb->proc, rb_intern("call"), 2, cb->data, INT2FIX(value) );
return FIX2INT(result);
}
static void mark_CallbackData(void* ptr) {
CallbackData* cb = (CallbackData*)ptr;
if (cb->proc != Qnil)
rb_gc_mark(cb->proc);
if (cb->proc2 != Qnil)
rb_gc_mark(cb->proc2);
if (cb->data != Qnil)
rb_gc_mark(cb->data);
}
int Sqlite3_ruby_authorizer(void* data,int type,
const char* a,const char* b,const char* c,const char* d)
{
VALUE result;
CallbackData *cb = (CallbackData*)data;
result = rb_funcall(
cb->proc, rb_intern("call"), 6, cb->data, INT2FIX(type),
( a ? rb_str_new2(a) : Qnil ), ( b ? rb_str_new2(b) : Qnil ),
( c ? rb_str_new2(c) : Qnil ), ( d ? rb_str_new2(d) : Qnil ) );
return FIX2INT(result);
}
void Sqlite3_ruby_trace(void* data, const char *sql) {
CallbackData *cb = (CallbackData*)data;
rb_funcall( cb->proc, rb_intern("call"), 2, cb->data,
sql ? rb_str_new2(sql) : Qnil );
}
void Sqlite3_ruby_function_step(sqlite3_context* ctx,int n,
sqlite3_value** args)
{
CallbackData *data;
VALUE rb_args;
VALUE *rb_context;
int idx;
data = (CallbackData*)sqlite3_user_data(ctx);
if( data->proc2 != Qnil ) {
rb_context = (VALUE*)sqlite3_aggregate_context(ctx,sizeof(VALUE));
if( *rb_context == 0 ) {
*rb_context = rb_hash_new();
rb_gc_register_address( rb_context );
}
}
rb_args = rb_ary_new2(n+1);
rb_ary_push( rb_args, SWIG_NewPointerObj(ctx,SWIGTYPE_p_sqlite3_context,0) );
for( idx = 0; idx < n; idx++ ) {
rb_ary_push( rb_args, SWIG_NewPointerObj(args[idx],
SWIGTYPE_p_sqlite3_value,0) );
}
rb_apply( data->proc, rb_intern("call"), rb_args );
}
void Sqlite3_ruby_function_final(sqlite3_context *ctx) {
VALUE *rb_context;
CallbackData *data;
rb_context = (VALUE*)sqlite3_aggregate_context(ctx,sizeof(VALUE));
if( *rb_context == 0 ) {
*rb_context = rb_hash_new();
rb_gc_register_address( rb_context );
}
data = (CallbackData*)sqlite3_user_data(ctx);
rb_funcall( data->proc2, rb_intern("call"), 1,
SWIG_NewPointerObj(ctx,SWIGTYPE_p_sqlite3_context,0) );
rb_gc_unregister_address( rb_context );
}
%}
%markfunc CallbackData "mark_CallbackData";
struct CallbackData {
VALUE proc;
VALUE proc2;
VALUE data;
};
%typemap(in) const void *str {
$1 = (void*)RSTRING_PTR($input);
}
%typemap(in) (const char *filename, sqlite3**) {
$1 = STR2CSTR($input);
$2 = (sqlite3**)malloc( sizeof( sqlite3* ) );
}
%typemap(argout) (const char *filename, sqlite3**) {
VALUE ary;
ary = rb_ary_new2(2);
rb_ary_push( ary, $result );
rb_ary_push( ary, SWIG_NewPointerObj( *$2, SWIGTYPE_p_sqlite3, 0 ) );
free( $2 );
$result = ary;
}
%typemap(in) (const void *filename, sqlite3**) {
$1 = (void*)RSTRING_PTR($input);
$2 = (sqlite3**)malloc( sizeof( sqlite3* ) );
}
%typemap(argout) (const void *filename, sqlite3**) {
VALUE ary;
ary = rb_ary_new2(2);
rb_ary_push( ary, $result );
rb_ary_push( ary, SWIG_NewPointerObj( *$2, SWIGTYPE_p_sqlite3, 0 ) );
free( $2 );
$result = ary;
}
typedef void RUBY_BLOB;
%typemap(out) const RUBY_BLOB * {
$result = $1 ?
rb_str_new( (char*)$1, sqlite3_column_bytes( arg1, arg2 ) ) : Qnil;
}
typedef void RUBY_VALBLOB;
%typemap(out) const RUBY_VALBLOB * {
$result = $1 ? rb_str_new( (char*)$1, sqlite3_value_bytes( arg1 ) ) : Qnil;
}
%typemap(out) const void * {
int i;
if( $1 ) {
for( i = 0; ((char*)$1)[i]; i += 2 );
$result = rb_str_new( (char*)$1, i );
} else $result = Qnil;
}
%typemap(in) (const char * sql,int,sqlite3_stmt**,const char**) (sqlite3_stmt *stmt, char *errmsg) {
$1 = RSTRING_PTR($input);
$2 = RSTRING_LEN($input);
$3 = &stmt2;
$4 = &errmsg2;
}
%typemap(argout) (const char* sql,int,sqlite3_stmt**,const char**) {
VALUE ary;
ary = rb_ary_new2(3);
rb_ary_push( ary, $result );
rb_ary_push( ary, SWIG_NewPointerObj( stmt2, SWIGTYPE_p_sqlite3_stmt, 0 ) );
rb_ary_push( ary, errmsg2 ? rb_str_new2( errmsg2 ) : Qnil );
$result = ary;
}
%typemap(in) (const void* sql,int,sqlite3_stmt**,const void**) (sqlite3_stmt *stmt, void *errmsg) {
$1 = RSTRING_PTR($input);
$2 = RSTRING_LEN($input);
$3 = &stmt2;
$4 = &errmsg2;
}
%typemap(argout) (const void* sql,int,sqlite3_stmt**,const void**) {
VALUE ary;
int i;
for( i = 0; ((char*)errmsg2)[i]; i += 2 );
ary = rb_ary_new2(3);
rb_ary_push( ary, $result );
rb_ary_push( ary, SWIG_NewPointerObj( stmt2, SWIGTYPE_p_sqlite3_stmt, 0 ) );
rb_ary_push( ary, errmsg2 ? rb_str_new( (char*)errmsg2, i ) : Qnil );
$result = ary;
}
%typemap(in) (const void *blob,int) {
$1 = (void*)RSTRING_PTR($input);
$2 = RSTRING_LEN($input);
}
%typemap(in) (const void *blob,int,void(*free)(void*)) {
$1 = (void*)RSTRING_PTR($input);
$2 = RSTRING_LEN($input);
$3 = SQLITE_TRANSIENT;
}
%typemap(in) (const char *text,int) {
$1 = RSTRING_PTR($input);
$2 = RSTRING_LEN($input);
}
%typemap(in) (const char *text,int,void(*free)(void*)) {
$1 = RSTRING_PTR($input);
$2 = RSTRING_LEN($input);
$3 = SQLITE_TRANSIENT;
}
%typemap(in) (const void *utf16,int) {
$1 = (void*)RSTRING_PTR($input);
$2 = RSTRING_LEN($input);
}
%typemap(in) (const void *utf16,int,void(*free)(void*)) {
$1 = (void*)RSTRING_PTR($input);
$2 = RSTRING_LEN($input);
$3 = SQLITE_TRANSIENT;
}
%typemap(out) sqlite_int64 {
$result = rb_ll2inum( $1 );
}
%typemap(out) const char * {
$result = $1 ? rb_str_new2($1) : Qnil;
}
%typemap(in) sqlite_int64 {
$1 = rb_num2ll( $input );
}
%typemap(in) (sqlite3_context*,int data_size) {
SWIG_ConvertPtr($input,(void**)&$1, SWIGTYPE_p_sqlite3_context, 1);
$2 = 4;
}
%typemap(out) VALUE* {
$result = *(VALUE*)$1;
}
%constant int Sqlite3_ruby_busy_handler(void*,int);
%constant int Sqlite3_ruby_authorizer(void*,int,const char*,const char*,const char*,const char*);
%constant void Sqlite3_ruby_trace(void*,const char*);
%constant void Sqlite3_ruby_function_step(sqlite3_context* ctx,int n,
sqlite3_value** args);
%constant void Sqlite3_ruby_function_final(sqlite3_context* ctx);
const char *sqlite3_libversion(void);
int sqlite3_close(sqlite3*);
sqlite_int64 sqlite3_last_insert_rowid(sqlite3*);
int sqlite3_changes(sqlite3*);
int sqlite3_total_changes(sqlite3*);
void sqlite3_interrupt(sqlite3*);
int sqlite3_complete(const char*);
int sqlite3_complete16(const void *str);
int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
int sqlite3_busy_timeout(sqlite3*,int);
int sqlite3_set_authorizer(sqlite3*, int(*)(void*,int,const char*,const char*,const char*,const char*), void*);
int sqlite3_trace(sqlite3*, void(*)(void*,const char*), void*);
int sqlite3_open(const char *filename, sqlite3 **);
int sqlite3_open16(const void *filename, sqlite3 **);
int sqlite3_errcode(sqlite3*);
const char *sqlite3_errmsg(sqlite3*);
const void *sqlite3_errmsg16(sqlite3*);
int sqlite3_prepare(sqlite3*,const char* sql,int,sqlite3_stmt**,const char**);
int sqlite3_prepare16(sqlite3*,const void* sql,int,sqlite3_stmt**,const void**);
int sqlite3_bind_blob(sqlite3_stmt*,int,const void *blob,int,void(*free)(void*));
int sqlite3_bind_double(sqlite3_stmt*,int,double);
int sqlite3_bind_int(sqlite3_stmt*,int,int);
int sqlite3_bind_int64(sqlite3_stmt*,int,sqlite_int64);
int sqlite3_bind_null(sqlite3_stmt*,int);
int sqlite3_bind_text(sqlite3_stmt*,int,const char*text,int,void(*free)(void*));
int sqlite3_bind_text16(sqlite3_stmt*,int,const void*utf16,int,void(*free)(void*));
int sqlite3_bind_parameter_count(sqlite3_stmt*);
const char *sqlite3_bind_parameter_name(sqlite3_stmt*,int);
int sqlite3_bind_parameter_index(sqlite3_stmt*,const char*);
int sqlite3_column_count(sqlite3_stmt*);
const char *sqlite3_column_name(sqlite3_stmt*,int);
const void *sqlite3_column_name16(sqlite3_stmt*,int);
const char *sqlite3_column_decltype(sqlite3_stmt*,int);
const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
int sqlite3_step(sqlite3_stmt*);
int sqlite3_data_count(sqlite3_stmt*);
const RUBY_BLOB *sqlite3_column_blob(sqlite3_stmt*,int);
int sqlite3_column_bytes(sqlite3_stmt*,int);
int sqlite3_column_bytes16(sqlite3_stmt*,int);
double sqlite3_column_double(sqlite3_stmt*,int);
double sqlite3_column_int(sqlite3_stmt*,int);
sqlite_int64 sqlite3_column_int64(sqlite3_stmt*,int);
const char *sqlite3_column_text(sqlite3_stmt*,int);
const void *sqlite3_column_text16(sqlite3_stmt*,int);
int sqlite3_column_type(sqlite3_stmt*,int);
int sqlite3_finalize(sqlite3_stmt*);
int sqlite3_reset(sqlite3_stmt*);
int sqlite3_create_function(sqlite3*,const char*str,int,int,void*,void(*func)(sqlite3_context*,int,sqlite3_value**),void(*step)(sqlite3_context*,int,sqlite3_value**),void(*final)(sqlite3_context*));
int sqlite3_create_function16(sqlite3*,const void*str,int,int,void*,void(*func)(sqlite3_context*,int,sqlite3_value**),void(*step)(sqlite3_context*,int,sqlite3_value**),void(*final)(sqlite3_context*));
int sqlite3_aggregate_count(sqlite3_context*);
const RUBY_VALBLOB *sqlite3_value_blob(sqlite3_value*);
int sqlite3_value_bytes(sqlite3_value*);
int sqlite3_value_bytes16(sqlite3_value*);
double sqlite3_value_double(sqlite3_value*);
int sqlite3_value_int(sqlite3_value*);
sqlite_int64 sqlite3_value_int64(sqlite3_value*);
const char *sqlite3_value_text(sqlite3_value*);
const void *sqlite3_value_text16(sqlite3_value*);
const void *sqlite3_value_text16le(sqlite3_value*);
const void *sqlite3_value_text16be(sqlite3_value*);
int sqlite3_value_type(sqlite3_value*);
void sqlite3_result_blob(sqlite3_context*,const void *blob,int,void(*free)(void*));
void sqlite3_result_double(sqlite3_context*,double);
void sqlite3_result_error(sqlite3_context*,const char *text,int);
void sqlite3_result_error16(sqlite3_context*,const void *blob,int);
void sqlite3_result_int(sqlite3_context*,int);
void sqlite3_result_int64(sqlite3_context*,sqlite_int64);
void sqlite3_result_text(sqlite3_context*,const char* text,int,void(*free)(void*));
void sqlite3_result_text16(sqlite3_context*,const void* utf16,int,void(*free)(void*));
void sqlite3_result_text16le(sqlite3_context*,const void* utf16,int,void(*free)(void*));
void sqlite3_result_text16be(sqlite3_context*,const void* utf16,int,void(*free)(void*));
void sqlite3_result_value(sqlite3_context*,sqlite3_value*);
VALUE *sqlite3_aggregate_context(sqlite3_context*,int data_size);

File diff suppressed because it is too large Load diff

View file

@ -1,7 +0,0 @@
REM This is not guaranteed to work, ever. It's just a little helper
REM script that I threw together to help me build the win32 version of
REM the library. If someone with more win32-fu than I wants to make
REM something more robust, please feel free! I'd love to include it.
REM -- Jamis Buck
cl /LD /Ie:\WinSDK\Include /Ic:\ruby\lib\ruby\1.8\i386-mswin32 /Ic:\ruby\sqlite3 /Ic:\ruby\src\ruby-1.8.4_2006-04-14 sqlite3_api_wrap.c /link /LIBPATH:c:\ruby\sqlite3 /LIBPATH:e:\WinSDK\Lib /LIBPATH:c:\ruby\lib sqlite3.lib msvcrt-ruby18.lib

145
vendor/plugins/sqlite3-ruby/faq/faq.rb vendored Normal file
View file

@ -0,0 +1,145 @@
require 'yaml'
require 'redcloth'
def process_faq_list( faqs )
puts "<ul>"
faqs.each do |faq|
process_faq_list_item faq
end
puts "</ul>"
end
def process_faq_list_item( faq )
question = faq.keys.first
answer = faq.values.first
print "<li>"
question_text = RedCloth.new(question).to_html.gsub( %r{</?p>},"" )
if answer.is_a?( Array )
puts question_text
process_faq_list answer
else
print "<a href='##{question.object_id}'>#{question_text}</a>"
end
puts "</li>"
end
def process_faq_descriptions( faqs, path=nil )
faqs.each do |faq|
process_faq_description faq, path
end
end
def process_faq_description( faq, path )
question = faq.keys.first
path = ( path ? path + " " : "" ) + question
answer = faq.values.first
if answer.is_a?( Array )
process_faq_descriptions( answer, path )
else
title = RedCloth.new( path ).to_html.gsub( %r{</?p>}, "" )
answer = RedCloth.new( answer || "" )
puts "<a name='#{question.object_id}'></a>"
puts "<div class='faq-title'>#{title}</div>"
puts "<div class='faq-answer'>#{add_api_links(answer.to_html)}</div>"
end
end
API_OBJECTS = [ "Database", "Statement", "ResultSet",
"ParsedStatement", "Pragmas", "Translator" ].inject( "(" ) { |acc,name|
acc << "|" if acc.length > 1
acc << name
acc
} + ")"
def add_api_links( text )
text.gsub( /#{API_OBJECTS}(#(\w+))?/ ) do
disp_obj = obj = $1
case obj
when "Pragmas"; disp_obj = "Database"
end
method = $3
s = "<a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/#{obj}.html'>#{disp_obj}"
s << "##{method}" if method
s << "</a>"
s
end
end
faqs = YAML.load( File.read( "faq.yml" ) )
puts <<-EOF
<html>
<head>
<title>SQLite3/Ruby FAQ</title>
<style type="text/css">
a, a:visited, a:active {
color: #00F;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.faq-list {
color: #000;
font-family: vera-sans, verdana, arial, sans-serif;
}
.faq-title {
background: #007;
color: #FFF;
font-family: vera-sans, verdana, arial, sans-serif;
padding-left: 1em;
padding-top: 0.5em;
padding-bottom: 0.5em;
font-weight: bold;
font-size: large;
border: 1px solid #000;
}
.faq-answer {
margin-left: 1em;
color: #000;
font-family: vera-sans, verdana, arial, sans-serif;
}
.faq-answer pre {
margin-left: 1em;
color: #000;
background: #FFE;
font-size: normal;
border: 1px dotted #CCC;
padding: 1em;
}
h1 {
background: #005;
color: #FFF;
font-family: vera-sans, verdana, arial, sans-serif;
padding-left: 1em;
padding-top: 1em;
padding-bottom: 1em;
font-weight: bold;
font-size: x-large;
border: 1px solid #00F;
}
</style>
</head>
<body>
<h1>SQLite/Ruby FAQ</h1>
<div class="faq-list">
EOF
process_faq_list( faqs )
puts "</div>"
process_faq_descriptions( faqs )
puts "</body></html>"

426
vendor/plugins/sqlite3-ruby/faq/faq.yml vendored Normal file
View file

@ -0,0 +1,426 @@
---
- "How do I do a database query?":
- "I just want an array of the rows...": >-
Use the Database#execute method. If you don't give it a block, it will
return an array of all the rows:
<pre>
require 'sqlite3'
db = SQLite3::Database.new( "test.db" )
rows = db.execute( "select * from test" )
</pre>
- "I'd like to use a block to iterate through the rows...": >-
Use the Database#execute method. If you give it a block, each row of the
result will be yielded to the block:
<pre>
require 'sqlite3'
db = SQLite3::Database.new( "test.db" )
db.execute( "select * from test" ) do |row|
...
end
</pre>
- "I need to get the column names as well as the rows...": >-
Use the Database#execute2 method. This works just like Database#execute;
if you don't give it a block, it returns an array of rows; otherwise, it
will yield each row to the block. _However_, the first row returned is
always an array of the column names from the query:
<pre>
require 'sqlite3'
db = SQLite3::Database.new( "test.db" )
columns, *rows = db.execute2( "select * from test" )
# or use a block:
columns = nil
db.execute2( "select * from test" ) do |row|
if columns.nil?
columns = row
else
# process row
end
end
</pre>
- "I just want the first row of the result set...": >-
Easy. Just call Database#get_first_row:
<pre>
row = db.get_first_row( "select * from table" )
</pre>
This also supports bind variables, just like Database#execute
and friends.
- "I just want the first value of the first row of the result set...": >-
Also easy. Just call Database#get_first_value:
<pre>
count = db.get_first_value( "select count(*) from table" )
</pre>
This also supports bind variables, just like Database#execute
and friends.
- "How do I prepare a statement for repeated execution?": >-
If the same statement is going to be executed repeatedly, you can speed
things up a bit by _preparing_ the statement. You do this via the
Database#prepare method. It returns a Statement object, and you can
then invoke #execute on that to get the ResultSet:
<pre>
stmt = db.prepare( "select * from person" )
1000.times do
stmt.execute do |result|
...
end
end
stmt.close
# or, use a block
db.prepare( "select * from person" ) do |stmt|
1000.times do
stmt.execute do |result|
...
end
end
end
</pre>
This is made more useful by the ability to bind variables to placeholders
via the Statement#bind_param and Statement#bind_params methods. (See the
next FAQ for details.)
- "How do I use placeholders in an SQL statement?": >-
Placeholders in an SQL statement take any of the following formats:
* @?@
* @?_nnn_@
* @:_word_@
Where _n_ is an integer, and _word_ is an alpha-numeric identifier (or
number). When the placeholder is associated with a number, that number
identifies the index of the bind variable to replace it with. When it
is an identifier, it identifies the name of the correponding bind
variable. (In the instance of the first format--a single question
mark--the placeholder is assigned a number one greater than the last
index used, or 1 if it is the first.)
For example, here is a query using these placeholder formats:
<pre>
select *
from table
where ( c = ?2 or c = ? )
and d = :name
and e = :1
</pre>
This defines 5 different placeholders: 1, 2, 3, and "name".
You replace these placeholders by _binding_ them to values. This can be
accomplished in a variety of ways.
The Database#execute, and Database#execute2 methods all accept additional
arguments following the SQL statement. These arguments are assumed to be
bind parameters, and they are bound (positionally) to their corresponding
placeholders:
<pre>
db.execute( "select * from table where a = ? and b = ?",
"hello",
"world" )
</pre>
The above would replace the first question mark with 'hello' and the
second with 'world'. If the placeholders have an explicit index given, they
will be replaced with the bind parameter at that index (1-based).
If a Hash is given as a bind parameter, then its key/value pairs are bound
to the placeholders. This is how you bind by name:
<pre>
db.execute( "select * from table where a = :name and b = :value",
"name" => "bob",
"value" => "priceless" )
</pre>
You can also bind explicitly using the Statement object itself. Just pass
additional parameters to the Statement#execute statement:
<pre>
db.prepare( "select * from table where a = :name and b = ?" ) do |stmt|
stmt.execute "value", "name" => "bob"
end
</pre>
Or do a Database#prepare to get the Statement, and then use either
Statement#bind_param or Statement#bind_params:
<pre>
stmt = db.prepare( "select * from table where a = :name and b = ?" )
stmt.bind_param( "name", "bob" )
stmt.bind_param( 1, "value" )
# or
stmt.bind_params( "value", "name" => "bob" )
</pre>
- "How do I discover metadata about a query?": >-
If you ever want to know the names or types of the columns in a result
set, you can do it in several ways.
The first way is to ask the row object itself. Each row will have a
property "fields" that returns an array of the column names. The row
will also have a property "types" that returns an array of the column
types:
<pre>
rows = db.execute( "select * from table" )
p rows[0].fields
p rows[0].types
</pre>
Obviously, this approach requires you to execute a statement that actually
returns data. If you don't know if the statement will return any rows, but
you still need the metadata, you can use Database#query and ask the
ResultSet object itself:
<pre>
db.query( "select * from table" ) do |result|
p result.columns
p result.types
...
end
</pre>
Lastly, you can use Database#prepare and ask the Statement object what
the metadata are:
<pre>
stmt = db.prepare( "select * from table" )
p stmt.columns
p stmt.types
</pre>
- "I'd like the rows to be indexible by column name.": >-
By default, each row from a query is returned as an Array of values. This
means that you can only obtain values by their index. Sometimes, however,
you would like to obtain values by their column name.
The first way to do this is to set the Database property "results_as_hash"
to true. If you do this, then all rows will be returned as Hash objects,
with the column names as the keys. (In this case, the "fields" property
is unavailable on the row, although the "types" property remains.)
<pre>
db.results_as_hash = true
db.execute( "select * from table" ) do |row|
p row['column1']
p row['column2']
end
</pre>
The other way is to use Ara Howard's
"ArrayFields":http://rubyforge.org/projects/arrayfields
module. Just require "arrayfields", and all of your rows will be indexable
by column name, even though they are still arrays!
<pre>
require 'arrayfields'
...
db.execute( "select * from table" ) do |row|
p row[0] == row['column1']
p row[1] == row['column2']
end
</pre>
- "I'd like the values from a query to be the correct types, instead of String.": >-
You can turn on "type translation" by setting Database#type_translation to
true:
<pre>
db.type_translation = true
db.execute( "select * from table" ) do |row|
p row
end
</pre>
By doing this, each return value for each row will be translated to its
correct type, based on its declared column type.
You can even declare your own translation routines, if (for example) you are
using an SQL type that is not handled by default:
<pre>
# assume "objects" table has the following schema:
# create table objects (
# name varchar2(20),
# thing object
# )
db.type_translation = true
db.translator.add_translator( "object" ) do |type, value|
db.decode( value )
end
h = { :one=>:two, "three"=>"four", 5=>6 }
dump = db.encode( h )
db.execute( "insert into objects values ( ?, ? )", "bob", dump )
obj = db.get_first_value( "select thing from objects where name='bob'" )
p obj == h
</pre>
- "How do insert binary data into the database?": >-
Use blobs. Blobs are new features of SQLite3. You have to use bind
variables to make it work:
<pre>
db.execute( "insert into foo ( ?, ? )",
SQLite3::Blob.new( "\0\1\2\3\4\5" ),
SQLite3::Blob.new( "a\0b\0c\0d ) )
</pre>
The blob values must be indicated explicitly by binding each parameter to
a value of type SQLite3::Blob.
- "How do I do a DDL (insert, update, delete) statement?": >-
You can actually do inserts, updates, and deletes in exactly the same way
as selects, but in general the Database#execute method will be most
convenient:
<pre>
db.execute( "insert into table values ( ?, ? )", *bind_vars )
</pre>
- "How do I execute multiple statements in a single string?": >-
The standard query methods (Database#execute, Database#execute2,
Database#query, and Statement#execute) will only execute the first
statement in the string that is given to them. Thus, if you have a
string with multiple SQL statements, each separated by a string,
you can't use those methods to execute them all at once.
Instead, use Database#execute_batch:
<pre>
sql = <<SQL
create table the_table (
a varchar2(30),
b varchar2(30)
);
insert into the_table values ( 'one', 'two' );
insert into the_table values ( 'three', 'four' );
insert into the_table values ( 'five', 'six' );
SQL
db.execute_batch( sql )
</pre>
Unlike the other query methods, Database#execute_batch accepts no
block. It will also only ever return +nil+. Thus, it is really only
suitable for batch processing of DDL statements.
- "How do I begin/end a transaction?":
Use Database#transaction to start a transaction. If you give it a block,
the block will be automatically committed at the end of the block,
unless an exception was raised, in which case the transaction will be
rolled back. (Never explicitly call Database#commit or Database#rollback
inside of a transaction block--you'll get errors when the block
terminates!)
<pre>
database.transaction do |db|
db.execute( "insert into table values ( 'a', 'b', 'c' )" )
...
end
</pre>
Alternatively, if you don't give a block to Database#transaction, the
transaction remains open until you explicitly call Database#commit or
Database#rollback.
<pre>
db.transaction
db.execute( "insert into table values ( 'a', 'b', 'c' )" )
db.commit
</pre>
Note that SQLite does not allow nested transactions, so you'll get errors
if you try to open a new transaction while one is already active. Use
Database#transaction_active? to determine whether a transaction is
active or not.
#- "How do I discover metadata about a table/index?":
#
#- "How do I do tweak database settings?":

View file

@ -1 +1,10 @@
# support multiple ruby version (fat binaries under windows)
begin
RUBY_VERSION =~ /(\d+.\d+)/
require "sqlite3/#{$1}/sqlite3_native"
rescue LoadError
require 'sqlite3/sqlite3_native'
end
require 'sqlite3/database' require 'sqlite3/database'
require 'sqlite3/version'

View file

@ -35,7 +35,7 @@ module SQLite3
class Database class Database
include Pragmas include Pragmas
class <<self class << self
alias :open :new alias :open :new
@ -48,12 +48,6 @@ module SQLite3
end end
# The low-level opaque database handle that this object wraps.
attr_reader :handle
# A reference to the underlying SQLite3 driver used by this database.
attr_reader :driver
# A boolean that indicates whether rows in result sets should be returned # A boolean that indicates whether rows in result sets should be returned
# as hashes or not. By default, rows are returned as arrays. # as hashes or not. By default, rows are returned as arrays.
attr_accessor :results_as_hash attr_accessor :results_as_hash
@ -62,54 +56,6 @@ module SQLite3
# database. # database.
attr_accessor :type_translation attr_accessor :type_translation
# Create a new Database object that opens the given file. If utf16
# is +true+, the filename is interpreted as a UTF-16 encoded string.
#
# By default, the new database will return result rows as arrays
# (#results_as_hash) and has type translation disabled (#type_translation=).
def initialize( file_name, options={} ) # :yields: db
utf16 = options.fetch(:utf16, false)
load_driver( options[:driver] )
@statement_factory = options[:statement_factory] || Statement
result, @handle = @driver.open( file_name, utf16 )
Error.check( result, self, "could not open database" )
@closed = false
@results_as_hash = options.fetch(:results_as_hash,false)
@type_translation = options.fetch(:type_translation,false)
@translator = nil
@transaction_active = false
if block_given?
begin
yield self
ensure
self.close
end
end
end
# Return +true+ if the string is a valid (ie, parsable) SQL statement, and
# +false+ otherwise. If +utf16+ is +true+, then the string is a UTF-16
# character string.
def complete?( string, utf16=false )
@driver.complete?( string, utf16 )
end
# Return a string describing the last error to have occurred with this
# database.
def errmsg( utf16=false )
@driver.errmsg( @handle, utf16 )
end
# Return an integer representing the last error to have occurred with this
# database.
def errcode
@driver.errcode( @handle )
end
# Return the type translator employed by this database instance. Each # Return the type translator employed by this database instance. Each
# database instance has its own type translator; this allows for different # database instance has its own type translator; this allows for different
# type handlers to be installed in each instance without affecting other # type handlers to be installed in each instance without affecting other
@ -120,35 +66,12 @@ module SQLite3
@translator ||= Translator.new @translator ||= Translator.new
end end
# Closes this database.
def close
unless @closed
result = @driver.close( @handle )
Error.check( result, self )
end
@closed = true
end
# Returns +true+ if this database instance has been closed (see #close).
def closed?
@closed
end
# Installs (or removes) a block that will be invoked for every SQL
# statement executed. The block receives a two parameters: the +data+
# argument, and the SQL statement executed. If the block is +nil+,
# any existing tracer will be uninstalled.
def trace( data=nil, &block )
@driver.trace( @handle, data, &block )
end
# Installs (or removes) a block that will be invoked for every access # Installs (or removes) a block that will be invoked for every access
# to the database. If the block returns 0 (or +nil+), the statement # to the database. If the block returns 0 (or +nil+), the statement
# is allowed to proceed. Returning 1 causes an authorization error to # is allowed to proceed. Returning 1 causes an authorization error to
# occur, and returning 2 causes the access to be silently denied. # occur, and returning 2 causes the access to be silently denied.
def authorizer( data=nil, &block ) def authorizer( &block )
result = @driver.set_authorizer( @handle, data, &block ) self.authorizer = block
Error.check( result, self )
end end
# Returns a Statement object representing the given SQL. This does not # Returns a Statement object representing the given SQL. This does not
@ -156,16 +79,14 @@ module SQLite3
# #
# The Statement can then be executed using Statement#execute. # The Statement can then be executed using Statement#execute.
# #
def prepare( sql ) def prepare sql
stmt = @statement_factory.new( self, sql ) stmt = SQLite3::Statement.new( self, sql )
if block_given? return stmt unless block_given?
begin
yield stmt begin
ensure yield stmt
stmt.close ensure
end stmt.close
else
return stmt
end end
end end
@ -183,13 +104,55 @@ module SQLite3
# #
# See also #execute2, #query, and #execute_batch for additional ways of # See also #execute2, #query, and #execute_batch for additional ways of
# executing statements. # executing statements.
def execute( sql, *bind_vars ) def execute sql, bind_vars = [], *args, &block
prepare( sql ) do |stmt| # FIXME: This is a terrible hack and should be removed but is required
result = stmt.execute( *bind_vars ) # for older versions of rails
if block_given? hack = Object.const_defined?(:ActiveRecord) && sql =~ /^PRAGMA index_list/
result.each { |row| yield row }
if bind_vars.nil? || !args.empty?
if args.empty?
bind_vars = []
else else
return result.inject( [] ) { |arr,row| arr << row; arr } bind_vars = [nil] + args
end
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling SQLite3::Database#execute with nil or multiple bind params
without using an array. Please switch to passing bind parameters as an array.
eowarn
end
prepare( sql ) do |stmt|
stmt.bind_params(bind_vars)
if type_translation
stmt = ResultSet.new(self, stmt).to_a
end
if block_given?
stmt.each do |row|
if @results_as_hash
h = Hash[*stmt.columns.zip(row).flatten]
row.each_with_index { |r, i| h[i] = r }
yield h
else
yield row
end
end
else
if @results_as_hash
stmt.map { |row|
h = Hash[*stmt.columns.zip(row).flatten]
row.each_with_index { |r, i| h[i] = r }
# FIXME UGH TERRIBLE HACK!
h['unique'] = h['unique'].to_s if hack
h
}
else
stmt.to_a
end
end end
end end
end end
@ -208,10 +171,10 @@ module SQLite3
prepare( sql ) do |stmt| prepare( sql ) do |stmt|
result = stmt.execute( *bind_vars ) result = stmt.execute( *bind_vars )
if block_given? if block_given?
yield result.columns yield stmt.columns
result.each { |row| yield row } result.each { |row| yield row }
else else
return result.inject( [ result.columns ] ) { |arr,row| return result.inject( [ stmt.columns ] ) { |arr,row|
arr << row; arr } arr << row; arr }
end end
end end
@ -225,11 +188,40 @@ module SQLite3
# #
# This always returns +nil+, making it unsuitable for queries that return # This always returns +nil+, making it unsuitable for queries that return
# rows. # rows.
def execute_batch( sql, *bind_vars ) def execute_batch( sql, bind_vars = [], *args )
# FIXME: remove this stuff later
unless [Array, Hash].include?(bind_vars.class)
bind_vars = [bind_vars]
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling SQLite3::Database#execute_batch with bind parameters
that are not a list of a hash. Please switch to passing bind parameters as an
array or hash.
eowarn
end
# FIXME: remove this stuff later
if bind_vars.nil? || !args.empty?
if args.empty?
bind_vars = []
else
bind_vars = [nil] + args
end
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling SQLite3::Database#execute_batch with nil or multiple bind params
without using an array. Please switch to passing bind parameters as an array.
eowarn
end
sql = sql.strip sql = sql.strip
until sql.empty? do until sql.empty? do
prepare( sql ) do |stmt| prepare( sql ) do |stmt|
stmt.execute( *bind_vars ) # FIXME: this should probably use sqlite3's api for batch execution
# This implementation requires stepping over the results.
if bind_vars.length == stmt.bind_parameter_count
stmt.bind_params(bind_vars)
end
stmt.step
sql = stmt.remainder.strip sql = stmt.remainder.strip
end end
end end
@ -247,8 +239,22 @@ module SQLite3
# returned, or you could have problems with locks on the table. If called # returned, or you could have problems with locks on the table. If called
# with a block, +close+ will be invoked implicitly when the block # with a block, +close+ will be invoked implicitly when the block
# terminates. # terminates.
def query( sql, *bind_vars ) def query( sql, bind_vars = [], *args )
result = prepare( sql ).execute( *bind_vars )
if bind_vars.nil? || !args.empty?
if args.empty?
bind_vars = []
else
bind_vars = [nil] + args
end
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling SQLite3::Database#query with nil or multiple bind params
without using an array. Please switch to passing bind parameters as an array.
eowarn
end
result = prepare( sql ).execute( bind_vars )
if block_given? if block_given?
begin begin
yield result yield result
@ -279,55 +285,7 @@ module SQLite3
nil nil
end end
# Obtains the unique row ID of the last row to be inserted by this Database alias :busy_timeout :busy_timeout=
# instance.
def last_insert_row_id
@driver.last_insert_rowid( @handle )
end
# Returns the number of changes made to this database instance by the last
# operation performed. Note that a "delete from table" without a where
# clause will not affect this value.
def changes
@driver.changes( @handle )
end
# Returns the total number of changes made to this database instance
# since it was opened.
def total_changes
@driver.total_changes( @handle )
end
# Interrupts the currently executing operation, causing it to abort.
def interrupt
@driver.interrupt( @handle )
end
# Register a busy handler with this database instance. When a requested
# resource is busy, this handler will be invoked. If the handler returns
# +false+, the operation will be aborted; otherwise, the resource will
# be requested again.
#
# The handler will be invoked with the name of the resource that was
# busy, and the number of times it has been retried.
#
# See also the mutually exclusive #busy_timeout.
def busy_handler( data=nil, &block ) # :yields: data, retries
result = @driver.busy_handler( @handle, data, &block )
Error.check( result, self )
end
# Indicates that if a request for a resource terminates because that
# resource is busy, SQLite should sleep and retry for up to the indicated
# number of milliseconds. By default, SQLite does not retry
# busy resources. To restore the default behavior, send 0 as the
# +ms+ parameter.
#
# See also the mutually exclusive #busy_handler.
def busy_timeout( ms )
result = @driver.busy_timeout( @handle, ms )
Error.check( result, self )
end
# Creates a new function for use in SQL statements. It will be added as # Creates a new function for use in SQL statements. It will be added as
# +name+, with the given +arity+. (For variable arity functions, use # +name+, with the given +arity+. (For variable arity functions, use
@ -352,23 +310,12 @@ module SQLite3
# end # end
# #
# puts db.get_first_value( "select maim(name) from table" ) # puts db.get_first_value( "select maim(name) from table" )
def create_function( name, arity, text_rep=Constants::TextRep::ANY, def create_function name, arity, text_rep=Constants::TextRep::ANY, &block
&block ) # :yields: func, *args define_function(name) do |*args|
# begin fp = FunctionProxy.new
callback = proc do |func,*args| block.call(fp, *args)
begin fp.result
block.call( FunctionProxy.new( @driver, func ),
*args.map{|v| Value.new(self,v)} )
rescue StandardError, Exception => e
@driver.result_error( func,
"#{e.message} (#{e.class})", -1 )
end
end end
result = @driver.create_function( @handle, name, arity, text_rep, nil,
callback, nil, nil )
Error.check( result, self )
self self
end end
@ -410,47 +357,40 @@ module SQLite3
# aggregate functions. # aggregate functions.
def create_aggregate( name, arity, step=nil, finalize=nil, def create_aggregate( name, arity, step=nil, finalize=nil,
text_rep=Constants::TextRep::ANY, &block ) text_rep=Constants::TextRep::ANY, &block )
# begin
if block
proxy = AggregateDefinitionProxy.new
proxy.instance_eval(&block)
step ||= proxy.step_callback
finalize ||= proxy.finalize_callback
end
step_callback = proc do |func,*args| factory = Class.new do
ctx = @driver.aggregate_context( func ) def self.step( &block )
unless ctx[:__error] define_method(:step, &block)
begin end
step.call( FunctionProxy.new( @driver, func, ctx ),
*args.map{|v| Value.new(self,v)} ) def self.finalize( &block )
rescue Exception => e define_method(:finalize, &block)
ctx[:__error] = e
end
end end
end end
finalize_callback = proc do |func| if block_given?
ctx = @driver.aggregate_context( func ) factory.instance_eval(&block)
unless ctx[:__error] else
begin factory.class_eval do
finalize.call( FunctionProxy.new( @driver, func, ctx ) ) define_method(:step, step)
rescue Exception => e define_method(:finalize, finalize)
@driver.result_error( func,
"#{e.message} (#{e.class})", -1 )
end
else
e = ctx[:__error]
@driver.result_error( func,
"#{e.message} (#{e.class})", -1 )
end end
end end
result = @driver.create_function( @handle, name, arity, text_rep, nil, proxy = factory.new
nil, step_callback, finalize_callback ) proxy.extend(Module.new {
Error.check( result, self ) attr_accessor :ctx
self def step( *args )
super(@ctx, *args)
end
def finalize
super(@ctx)
end
})
proxy.ctx = FunctionProxy.new
define_aggregator(name, proxy)
end end
# This is another approach to creating an aggregate function (see # This is another approach to creating an aggregate function (see
@ -500,47 +440,22 @@ module SQLite3
# db.create_aggregate_handler( LengthsAggregateHandler ) # db.create_aggregate_handler( LengthsAggregateHandler )
# puts db.get_first_value( "select lengths(name) from A" ) # puts db.get_first_value( "select lengths(name) from A" )
def create_aggregate_handler( handler ) def create_aggregate_handler( handler )
arity = -1 proxy = Class.new do
text_rep = Constants::TextRep::ANY def initialize handler
@handler = handler
@fp = FunctionProxy.new
end
arity = handler.arity if handler.respond_to?(:arity) def step( *args )
text_rep = handler.text_rep if handler.respond_to?(:text_rep) @handler.step(@fp, *args)
name = handler.name end
step = proc do |func,*args| def finalize
ctx = @driver.aggregate_context( func ) @handler.finalize @fp
unless ctx[ :__error ] @fp.result
ctx[ :handler ] ||= handler.new
begin
ctx[ :handler ].step( FunctionProxy.new( @driver, func, ctx ),
*args.map{|v| Value.new(self,v)} )
rescue Exception, StandardError => e
ctx[ :__error ] = e
end
end end
end end
define_aggregator(handler.name, proxy.new(handler.new))
finalize = proc do |func|
ctx = @driver.aggregate_context( func )
unless ctx[ :__error ]
ctx[ :handler ] ||= handler.new
begin
ctx[ :handler ].finalize( FunctionProxy.new( @driver, func, ctx ) )
rescue Exception => e
ctx[ :__error ] = e
end
end
if ctx[ :__error ]
e = ctx[ :__error ]
@driver.sqlite3_result_error( func, "#{e.message} (#{e.class})", -1 )
end
end
result = @driver.create_function( @handle, name, arity, text_rep, nil,
nil, step, finalize )
Error.check( result, self )
self self
end end
@ -604,33 +519,6 @@ module SQLite3
@transaction_active @transaction_active
end end
# Loads the corresponding driver, or if it is nil, attempts to locate a
# suitable driver.
def load_driver( driver )
case driver
when Class
# do nothing--use what was given
when Symbol, String
require "sqlite3/driver/#{driver.to_s.downcase}/driver"
driver = SQLite3::Driver.const_get( driver )::Driver
else
[ "Native", "DL" ].each do |d|
begin
require "sqlite3/driver/#{d.downcase}/driver"
driver = SQLite3::Driver.const_get( d )::Driver
break
rescue SyntaxError
raise
rescue ScriptError, Exception, NameError
end
end
raise "no driver for sqlite3 found" unless driver
end
@driver = driver.new
end
private :load_driver
# A helper class for dealing with custom functions (see #create_function, # A helper class for dealing with custom functions (see #create_function,
# #create_aggregate, and #create_aggregate_handler). It encapsulates the # #create_aggregate, and #create_aggregate_handler). It encapsulates the
# opaque function object that represents the current invocation. It also # opaque function object that represents the current invocation. It also
@ -640,26 +528,15 @@ module SQLite3
# This class will almost _always_ be instantiated indirectly, by working # This class will almost _always_ be instantiated indirectly, by working
# with the create methods mentioned above. # with the create methods mentioned above.
class FunctionProxy class FunctionProxy
attr_accessor :result
# Create a new FunctionProxy that encapsulates the given +func+ object. # Create a new FunctionProxy that encapsulates the given +func+ object.
# If context is non-nil, the functions context will be set to that. If # If context is non-nil, the functions context will be set to that. If
# it is non-nil, it must quack like a Hash. If it is nil, then none of # it is non-nil, it must quack like a Hash. If it is nil, then none of
# the context functions will be available. # the context functions will be available.
def initialize( driver, func, context=nil ) def initialize
@driver = driver @result = nil
@func = func @context = {}
@context = context
end
# Calls #set_result to set the result of this function.
def result=( result )
set_result( result )
end
# Set the result of the function to the given value. The function will
# then return this value.
def set_result( result, utf16=false )
@driver.result_text( @func, result, utf16 )
end end
# Set the result of the function to the given error message. # Set the result of the function to the given error message.
@ -672,50 +549,20 @@ module SQLite3
# that the aggregate has processed so far. This will include the current # that the aggregate has processed so far. This will include the current
# row, and so will always return at least 1. # row, and so will always return at least 1.
def count def count
ensure_aggregate!
@driver.aggregate_count( @func ) @driver.aggregate_count( @func )
end end
# Returns the value with the given key from the context. This is only # Returns the value with the given key from the context. This is only
# available to aggregate functions. # available to aggregate functions.
def []( key ) def []( key )
ensure_aggregate!
@context[ key ] @context[ key ]
end end
# Sets the value with the given key in the context. This is only # Sets the value with the given key in the context. This is only
# available to aggregate functions. # available to aggregate functions.
def []=( key, value ) def []=( key, value )
ensure_aggregate!
@context[ key ] = value @context[ key ] = value
end end
# A function for performing a sanity check, to ensure that the function
# being invoked is an aggregate function. This is implied by the
# existence of the context variable.
def ensure_aggregate!
unless @context
raise MisuseException, "function is not an aggregate"
end
end
private :ensure_aggregate!
end end
# A proxy used for defining the callbacks to an aggregate function.
class AggregateDefinitionProxy # :nodoc:
attr_reader :step_callback, :finalize_callback
def step( &block )
@step_callback = block
end
def finalize( &block )
@finalize_callback = block
end
end
end end
end end

View file

@ -1,152 +0,0 @@
require 'dl/import'
module SQLite3 ; module Driver; module DL;
module API
extend ::DL::Importable
library_name = case RUBY_PLATFORM.downcase
when /darwin/
"libsqlite3.dylib"
when /linux/, /freebsd|netbsd|openbsd|dragonfly/, /solaris/
"libsqlite3.so"
when /mswin|mingw/
"sqlite3.dll"
else
abort <<-EOF
== * UNSUPPORTED PLATFORM ======================================================
The platform '#{RUBY_PLATFORM}' is unsupported. Please help the author by
editing the following file to allow your sqlite3 library to be found, and
submitting a patch to jamis_buck@byu.edu. Thanks!
#{__FILE__}
=========================================================================== * ==
EOF
end
if defined? SQLITE3_LIB_PATH
library_name = File.join( SQLITE3_LIB_PATH, library_name )
end
dlload library_name
typealias "db", "void*"
typealias "stmt", "void*"
typealias "value", "void*"
typealias "context", "void*"
# until Ruby/DL supports 64-bit ints, we'll just treat them as 32-bit ints
typealias "int64", "unsigned long"
extern "const char *sqlite3_libversion()"
extern "int sqlite3_open(const char*,db*)"
extern "int sqlite3_open16(const void*,db*)"
extern "int sqlite3_close(db)"
extern "const char* sqlite3_errmsg(db)"
extern "void* sqlite3_errmsg16(db)"
extern "int sqlite3_errcode(db)"
extern "int sqlite3_prepare(db,const char*,int,stmt*,const char**)"
extern "int sqlite3_prepare16(db,const void*,int,stmt*,const void**)"
extern "int sqlite3_finalize(stmt)"
extern "int sqlite3_reset(stmt)"
extern "int sqlite3_step(stmt)"
extern "int64 sqlite3_last_insert_rowid(db)"
extern "int sqlite3_changes(db)"
extern "int sqlite3_total_changes(db)"
extern "void sqlite3_interrupt(db)"
extern "ibool sqlite3_complete(const char*)"
extern "ibool sqlite3_complete16(const void*)"
extern "int sqlite3_busy_handler(db,void*,void*)"
extern "int sqlite3_busy_timeout(db,int)"
extern "int sqlite3_set_authorizer(db,void*,void*)"
extern "void* sqlite3_trace(db,void*,void*)"
extern "int sqlite3_bind_blob(stmt,int,const void*,int,void*)"
extern "int sqlite3_bind_double(stmt,int,double)"
extern "int sqlite3_bind_int(stmt,int,int)"
extern "int sqlite3_bind_int64(stmt,int,int64)"
extern "int sqlite3_bind_null(stmt,int)"
extern "int sqlite3_bind_text(stmt,int,const char*,int,void*)"
extern "int sqlite3_bind_text16(stmt,int,const void*,int,void*)"
#extern "int sqlite3_bind_value(stmt,int,value)"
extern "int sqlite3_bind_parameter_count(stmt)"
extern "const char* sqlite3_bind_parameter_name(stmt,int)"
extern "int sqlite3_bind_parameter_index(stmt,const char*)"
extern "int sqlite3_column_count(stmt)"
extern "int sqlite3_data_count(stmt)"
extern "const void *sqlite3_column_blob(stmt,int)"
extern "int sqlite3_column_bytes(stmt,int)"
extern "int sqlite3_column_bytes16(stmt,int)"
extern "const char *sqlite3_column_decltype(stmt,int)"
extern "void *sqlite3_column_decltype16(stmt,int)"
extern "double sqlite3_column_double(stmt,int)"
extern "int sqlite3_column_int(stmt,int)"
extern "int64 sqlite3_column_int64(stmt,int)"
extern "const char *sqlite3_column_name(stmt,int)"
extern "const void *sqlite3_column_name16(stmt,int)"
extern "const char *sqlite3_column_text(stmt,int)"
extern "const void *sqlite3_column_text16(stmt,int)"
extern "int sqlite3_column_type(stmt,int)"
extern "int sqlite3_create_function(db,const char*,int,int,void*,void*,void*,void*)"
extern "int sqlite3_create_function16(db,const void*,int,int,void*,void*,void*,void*)"
extern "int sqlite3_aggregate_count(context)"
extern "const void *sqlite3_value_blob(value)"
extern "int sqlite3_value_bytes(value)"
extern "int sqlite3_value_bytes16(value)"
extern "double sqlite3_value_double(value)"
extern "int sqlite3_value_int(value)"
extern "int64 sqlite3_value_int64(value)"
extern "const char* sqlite3_value_text(value)"
extern "const void* sqlite3_value_text16(value)"
extern "const void* sqlite3_value_text16le(value)"
extern "const void* sqlite3_value_text16be(value)"
extern "int sqlite3_value_type(value)"
extern "void *sqlite3_aggregate_context(context,int)"
extern "void *sqlite3_user_data(context)"
extern "void *sqlite3_get_auxdata(context,int)"
extern "void sqlite3_set_auxdata(context,int,void*,void*)"
extern "void sqlite3_result_blob(context,const void*,int,void*)"
extern "void sqlite3_result_double(context,double)"
extern "void sqlite3_result_error(context,const char*,int)"
extern "void sqlite3_result_error16(context,const void*,int)"
extern "void sqlite3_result_int(context,int)"
extern "void sqlite3_result_int64(context,int64)"
extern "void sqlite3_result_null(context)"
extern "void sqlite3_result_text(context,const char*,int,void*)"
extern "void sqlite3_result_text16(context,const void*,int,void*)"
extern "void sqlite3_result_text16le(context,const void*,int,void*)"
extern "void sqlite3_result_text16be(context,const void*,int,void*)"
extern "void sqlite3_result_value(context,value)"
extern "int sqlite3_create_collation(db,const char*,int,void*,void*)"
extern "int sqlite3_create_collation16(db,const char*,int,void*,void*)"
extern "int sqlite3_collation_needed(db,void*,void*)"
extern "int sqlite3_collation_needed16(db,void*,void*)"
# ==== CRYPTO (NOT IN PUBLIC RELEASE) ====
if defined?( CRYPTO_API ) && CRYPTO_API
extern "int sqlite3_key(db,void*,int)"
extern "int sqlite3_rekey(db,void*,int)"
end
# ==== EXPERIMENTAL ====
if defined?( EXPERIMENTAL_API ) && EXPERIMENTAL_API
extern "int sqlite3_progress_handler(db,int,void*,void*)"
extern "int sqlite3_commit_hook(db,void*,void*)"
end
end
end ; end ; end

View file

@ -1,307 +0,0 @@
require 'sqlite3/driver/dl/api'
warn "The DL driver for sqlite3-ruby is deprecated and will be removed"
warn "in a future release. Please update your installation to use the"
warn "Native driver."
module Kernel
# Allows arbitrary objects to be passed as a pointer to functions.
# (Probably not very GC safe, but by encapsulating it like this we
# can change the implementation later.)
def to_ptr
ptr = DL.malloc(DL.sizeof("L"))
ptr.set_object self
ptr
end
end
class DL::PtrData
# The inverse of the Kernel#to_ptr operation.
def to_object
n = to_s(4).unpack("L").first
return nil if n < 1
ObjectSpace._id2ref(n) rescue self.to_s
end
def set_object(obj)
self[0] = [obj.object_id].pack("L")
end
end
module SQLite3 ; module Driver ; module DL
class Driver
STATIC = ::DL::PtrData.new(0)
TRANSIENT = ::DL::PtrData.new(-1)
def open( filename, utf16=false )
handle = ::DL::PtrData.new(0)
result = API.send( ( utf16 ? :sqlite3_open16 : :sqlite3_open ),
filename+"\0", handle.ref )
[ result, handle ]
end
def errmsg( db, utf16=false )
if utf16
msg = API.sqlite3_errmsg16( db )
msg.free = nil
msg.to_s(utf16_length(msg))
else
API.sqlite3_errmsg( db )
end
end
def prepare( db, sql, utf16=false )
handle = ::DL::PtrData.new(0)
remainder = ::DL::PtrData.new(0)
result = API.send( ( utf16 ? :sqlite3_prepare16 : :sqlite3_prepare ),
db, sql+"\0", sql.length, handle.ref, remainder.ref )
args = utf16 ? [ utf16_length(remainder) ] : []
remainder = remainder.to_s( *args )
[ result, handle, remainder ]
end
def complete?( sql, utf16=false )
API.send( utf16 ? :sqlite3_complete16 : :sqlite3_complete, sql+"\0" )
end
def value_blob( value )
blob = API.sqlite3_value_blob( value )
blob.free = nil
blob.to_s( API.sqlite3_value_bytes( value ) )
end
def value_text( value, utf16=false )
method = case utf16
when nil, false then :sqlite3_value_text
when :le then :sqlite3_value_text16le
when :be then :sqlite3_value_text16be
else :sqlite3_value_text16
end
result = API.send( method, value )
if utf16
result.free = nil
size = API.sqlite3_value_bytes( value )
result = result.to_s( size )
end
result
end
def column_blob( stmt, column )
blob = API.sqlite3_column_blob( stmt, column )
blob.free = nil
blob.to_s( API.sqlite3_column_bytes( stmt, column ) )
end
def result_text( func, text, utf16=false )
method = case utf16
when false, nil then :sqlite3_result_text
when :le then :sqlite3_result_text16le
when :be then :sqlite3_result_text16be
else :sqlite3_result_text16
end
s = text.to_s
API.send( method, func, s, s.length, TRANSIENT )
end
def busy_handler( db, data=nil, &block )
@busy_handler = block
unless @busy_handler_callback
@busy_handler_callback = ::DL.callback( "IPI" ) do |cookie, timeout|
@busy_handler.call( cookie, timeout ) || 0
end
end
API.sqlite3_busy_handler( db, block&&@busy_handler_callback, data )
end
def set_authorizer( db, data=nil, &block )
@authorizer_handler = block
unless @authorizer_handler_callback
@authorizer_handler_callback = ::DL.callback( "IPIPPPP"
) do |cookie,mode,a,b,c,d|
@authorizer_handler.call( cookie, mode,
a&&a.to_s, b&&b.to_s, c&&c.to_s, d&&d.to_s ) || 0
end
end
API.sqlite3_set_authorizer( db, block&&@authorizer_handler_callback,
data )
end
def trace( db, data=nil, &block )
@trace_handler = block
unless @trace_handler_callback
@trace_handler_callback = ::DL.callback( "IPS" ) do |cookie,sql|
@trace_handler.call( cookie ? cookie.to_object : nil, sql ) || 0
end
end
API.sqlite3_trace( db, block&&@trace_handler_callback, data )
end
def create_function( db, name, args, text, cookie,
func, step, final )
# begin
if @func_handler_callback.nil? && func
@func_handler_callback = ::DL.callback( "0PIP" ) do |context,nargs,args|
args = args.to_s(nargs*4).unpack("L*").map {|i| ::DL::PtrData.new(i)}
data = API.sqlite3_user_data( context ).to_object
data[:func].call( context, *args )
end
end
if @step_handler_callback.nil? && step
@step_handler_callback = ::DL.callback( "0PIP" ) do |context,nargs,args|
args = args.to_s(nargs*4).unpack("L*").map {|i| ::DL::PtrData.new(i)}
data = API.sqlite3_user_data( context ).to_object
data[:step].call( context, *args )
end
end
if @final_handler_callback.nil? && final
@final_handler_callback = ::DL.callback( "0P" ) do |context|
data = API.sqlite3_user_data( context ).to_object
data[:final].call( context )
end
end
data = { :cookie => cookie,
:name => name,
:func => func,
:step => step,
:final => final }
API.sqlite3_create_function( db, name, args, text, data,
( func ? @func_handler_callback : nil ),
( step ? @step_handler_callback : nil ),
( final ? @final_handler_callback : nil ) )
end
def aggregate_context( context )
ptr = API.sqlite3_aggregate_context( context, 4 )
ptr.free = nil
obj = ( ptr ? ptr.to_object : nil )
if obj.nil?
obj = Hash.new
ptr.set_object obj
end
obj
end
def bind_blob( stmt, index, value )
s = value.to_s
API.sqlite3_bind_blob( stmt, index, s, s.length, TRANSIENT )
end
def bind_text( stmt, index, value, utf16=false )
s = value.to_s
method = ( utf16 ? :sqlite3_bind_text16 : :sqlite3_bind_text )
API.send( method, stmt, index, s, s.length, TRANSIENT )
end
def column_text( stmt, column )
result = API.sqlite3_column_text( stmt, column )
result ? result.to_s : nil
end
def column_name( stmt, column )
result = API.sqlite3_column_name( stmt, column )
result ? result.to_s : nil
end
def column_decltype( stmt, column )
result = API.sqlite3_column_decltype( stmt, column )
result ? result.to_s : nil
end
def self.api_delegate( name )
define_method( name ) { |*args| API.send( "sqlite3_#{name}", *args ) }
end
api_delegate :aggregate_count
api_delegate :bind_double
api_delegate :bind_int
api_delegate :bind_int64
api_delegate :bind_null
api_delegate :bind_parameter_index
api_delegate :bind_parameter_name
api_delegate :busy_timeout
api_delegate :changes
api_delegate :close
api_delegate :column_bytes
api_delegate :column_bytes16
api_delegate :column_count
api_delegate :column_double
api_delegate :column_int
api_delegate :column_int64
api_delegate :column_type
api_delegate :data_count
api_delegate :errcode
api_delegate :finalize
api_delegate :interrupt
api_delegate :last_insert_rowid
api_delegate :libversion
api_delegate :reset
api_delegate :result_error
api_delegate :step
api_delegate :total_changes
api_delegate :value_bytes
api_delegate :value_bytes16
api_delegate :value_double
api_delegate :value_int
api_delegate :value_int64
api_delegate :value_type
# ==== EXPERIMENTAL ====
if defined?( EXPERIMENTAL_API ) && EXPERIMENTAL_API
def progress_handler( db, n, data=nil, &block )
@progress_handler = block
unless @progress_handler_callback
@progress_handler_callback = ::DL.callback( "IP" ) do |cookie|
@progress_handler.call( cookie )
end
end
API.sqlite3_progress_handler( db, n, block&&@progress_handler_callback,
data )
end
def commit_hook( db, data=nil, &block )
@commit_hook_handler = block
unless @commit_hook_handler_callback
@commit_hook_handler_callback = ::DL.callback( "IP" ) do |cookie|
@commit_hook_handler.call( cookie )
end
end
API.sqlite3_commit_hook( db, block&&@commit_hook_handler_callback,
data )
end
end
private
def utf16_length(ptr)
len = 0
loop do
break if ptr[len,1] == "\0"
len += 2
end
len
end
end
end ; end ; end

View file

@ -1,219 +0,0 @@
# support multiple ruby version (fat binaries under windows)
begin
require 'sqlite3_api'
rescue LoadError
if RUBY_PLATFORM =~ /mingw|mswin/ then
RUBY_VERSION =~ /(\d+.\d+)/
require "#{$1}/sqlite3_api"
end
end
module SQLite3 ; module Driver ; module Native
class Driver
def initialize
@callback_data = Hash.new
@authorizer = Hash.new
@busy_handler = Hash.new
@trace = Hash.new
end
def complete?( sql, utf16=false )
API.send( utf16 ? :sqlite3_complete16 : :sqlite3_complete, sql ) != 0
end
def busy_handler( db, data=nil, &block )
if block
cb = API::CallbackData.new
cb.proc = block
cb.data = data
result = API.sqlite3_busy_handler( db, API::Sqlite3_ruby_busy_handler, cb )
# Reference the Callback object so that
# it is not deleted by the GC
@busy_handler[db] = cb
else
# Unreference the callback *after* having removed it
# from sqlite
result = API.sqlite3_busy_handler( db, nil, nil )
@busy_handler.delete(db)
end
result
end
def set_authorizer( db, data=nil, &block )
if block
cb = API::CallbackData.new
cb.proc = block
cb.data = data
result = API.sqlite3_set_authorizer( db, API::Sqlite3_ruby_authorizer, cb )
@authorizer[db] = cb # see comments in busy_handler
else
result = API.sqlite3_set_authorizer( db, nil, nil )
@authorizer.delete(db) # see comments in busy_handler
end
result
end
def trace( db, data=nil, &block )
if block
cb = API::CallbackData.new
cb.proc = block
cb.data = data
result = API.sqlite3_trace( db, API::Sqlite3_ruby_trace, cb )
@trace[db] = cb # see comments in busy_handler
else
result = API.sqlite3_trace( db, nil, nil )
@trace.delete(db) # see comments in busy_handler
end
result
end
def open( filename, utf16=false )
API.send( utf16 ? :sqlite3_open16 : :sqlite3_open, filename )
end
def errmsg( db, utf16=false )
API.send( utf16 ? :sqlite3_errmsg16 : :sqlite3_errmsg, db )
end
def prepare( db, sql, utf16=false )
API.send( ( utf16 ? :sqlite3_prepare16 : :sqlite3_prepare ),
db, sql )
end
def bind_text( stmt, index, value, utf16=false )
API.send( ( utf16 ? :sqlite3_bind_text16 : :sqlite3_bind_text ),
stmt, index, value.to_s )
end
def column_name( stmt, index, utf16=false )
API.send( ( utf16 ? :sqlite3_column_name16 : :sqlite3_column_name ),
stmt, index )
end
def column_decltype( stmt, index, utf16=false )
API.send(
( utf16 ? :sqlite3_column_decltype16 : :sqlite3_column_decltype ),
stmt, index )
end
def column_text( stmt, index, utf16=false )
API.send( ( utf16 ? :sqlite3_column_text16 : :sqlite3_column_text ),
stmt, index )
end
def create_function( db, name, args, text, cookie, func, step, final )
if func || ( step && final )
cb = API::CallbackData.new
cb.proc = cb.proc2 = nil
cb.data = cookie
end
if func
cb.proc = func
func = API::Sqlite3_ruby_function_step
step = final = nil
elsif step && final
cb.proc = step
cb.proc2 = final
func = nil
step = API::Sqlite3_ruby_function_step
final = API::Sqlite3_ruby_function_final
end
result = API.sqlite3_create_function( db, name, args, text, cb, func, step, final )
# see comments in busy_handler
if cb
@callback_data[ name ] = cb
else
@callback_data.delete( name )
end
return result
end
def value_text( value, utf16=false )
method = case utf16
when nil, false then :sqlite3_value_text
when :le then :sqlite3_value_text16le
when :be then :sqlite3_value_text16be
else :sqlite3_value_text16
end
API.send( method, value )
end
def result_text( context, result, utf16=false )
method = case utf16
when nil, false then :sqlite3_result_text
when :le then :sqlite3_result_text16le
when :be then :sqlite3_result_text16be
else :sqlite3_result_text16
end
API.send( method, context, result.to_s )
end
def result_error( context, value, utf16=false )
API.send( ( utf16 ? :sqlite3_result_error16 : :sqlite3_result_error ),
context, value )
end
def self.api_delegate( name )
eval "def #{name} (*args) API.sqlite3_#{name}( *args ) ; end"
end
api_delegate :libversion
api_delegate :close
api_delegate :last_insert_rowid
api_delegate :changes
api_delegate :total_changes
api_delegate :interrupt
api_delegate :busy_timeout
api_delegate :errcode
api_delegate :bind_blob
api_delegate :bind_double
api_delegate :bind_int
api_delegate :bind_int64
api_delegate :bind_null
api_delegate :bind_parameter_count
api_delegate :bind_parameter_name
api_delegate :bind_parameter_index
api_delegate :column_count
api_delegate :step
api_delegate :data_count
api_delegate :column_blob
api_delegate :column_bytes
api_delegate :column_bytes16
api_delegate :column_double
api_delegate :column_int
api_delegate :column_int64
api_delegate :column_type
api_delegate :finalize
api_delegate :reset
api_delegate :aggregate_count
api_delegate :value_blob
api_delegate :value_bytes
api_delegate :value_bytes16
api_delegate :value_double
api_delegate :value_int
api_delegate :value_int64
api_delegate :value_type
api_delegate :result_blob
api_delegate :result_double
api_delegate :result_int
api_delegate :result_int64
api_delegate :result_null
api_delegate :result_value
api_delegate :aggregate_context
end
end ; end ; end

View file

@ -1,7 +1,6 @@
require 'sqlite3/constants' require 'sqlite3/constants'
module SQLite3 module SQLite3
class Exception < ::StandardError class Exception < ::StandardError
@code = 0 @code = 0
@ -42,27 +41,4 @@ module SQLite3
class FormatException < Exception; end class FormatException < Exception; end
class RangeException < Exception; end class RangeException < Exception; end
class NotADatabaseException < Exception; end class NotADatabaseException < Exception; end
EXCEPTIONS = [
nil,
SQLException, InternalException, PermissionException,
AbortException, BusyException, LockedException, MemoryException,
ReadOnlyException, InterruptException, IOException, CorruptException,
NotFoundException, FullException, CantOpenException, ProtocolException,
EmptyException, SchemaChangedException, TooBigException,
ConstraintException, MismatchException, MisuseException,
UnsupportedException, AuthorizationException, FormatException,
RangeException, NotADatabaseException
].each_with_index { |e,i| e.instance_variable_set( :@code, i ) if e }
module Error
def check( result, db=nil, msg=nil )
unless result == Constants::ErrorCode::OK
msg = ( msg ? msg + ": " : "" ) + db.errmsg if db
raise(( EXCEPTIONS[result] || SQLite3::Exception ), msg)
end
end
module_function :check
end
end end

View file

@ -213,16 +213,24 @@ module SQLite3
get_query_pragma "index_list", table, &block get_query_pragma "index_list", table, &block
end end
def table_info( table, &block ) # :yields: row ###
columns, *rows = execute2("PRAGMA table_info(#{table})") # Returns information about +table+. Yields each row of table information
# if a block is provided.
def table_info table
stmt = prepare "PRAGMA table_info(#{table})"
columns = stmt.columns
needs_tweak_default = version_compare(driver.libversion, "3.3.7") > 0 needs_tweak_default =
version_compare(SQLite3.libversion.to_s, "3.3.7") > 0
result = [] unless block_given? result = [] unless block_given?
rows.each do |row| stmt.each do |row|
new_row = {} new_row = Hash[*columns.zip(row).flatten]
columns.each_with_index do |name, index|
new_row[name] = row[index] # FIXME: This should be removed but is required for older versions
# of rails
if(Object.const_defined?(:ActiveRecord))
new_row['notnull'] = new_row['notnull'].to_s
end end
tweak_default(new_row) if needs_tweak_default tweak_default(new_row) if needs_tweak_default
@ -233,6 +241,7 @@ module SQLite3
result << new_row result << new_row
end end
end end
stmt.close
result result
end end

View file

@ -31,46 +31,22 @@ module SQLite3
# Create a new ResultSet attached to the given database, using the # Create a new ResultSet attached to the given database, using the
# given sql text. # given sql text.
def initialize( db, stmt ) def initialize db, stmt
@db = db @db = db
@driver = @db.driver
@stmt = stmt @stmt = stmt
commence
end end
# A convenience method for compiling the virtual machine and stepping
# to the first row of the result set.
def commence
result = @driver.step( @stmt.handle )
if result == Constants::ErrorCode::ERROR
@driver.reset( @stmt.handle )
end
check result
@first_row = true
end
private :commence
def check( result )
@eof = ( result == Constants::ErrorCode::DONE )
found = ( result == Constants::ErrorCode::ROW )
Error.check( result, @db ) unless @eof || found
end
private :check
# Reset the cursor, so that a result set which has reached end-of-file # Reset the cursor, so that a result set which has reached end-of-file
# can be rewound and reiterated. # can be rewound and reiterated.
def reset( *bind_params ) def reset( *bind_params )
@stmt.must_be_open! @stmt.reset!
@stmt.reset!(false)
@driver.reset( @stmt.handle )
@stmt.bind_params( *bind_params ) @stmt.bind_params( *bind_params )
@eof = false @eof = false
commence
end end
# Query whether the cursor has reached the end of the result set or not. # Query whether the cursor has reached the end of the result set or not.
def eof? def eof?
@eof @stmt.done?
end end
# Obtain the next row from the cursor. If there are no more rows to be # Obtain the next row from the cursor. If there are no more rows to be
@ -87,69 +63,39 @@ module SQLite3
# For hashes, the column names are the keys of the hash, and the column # For hashes, the column names are the keys of the hash, and the column
# types are accessible via the +types+ property. # types are accessible via the +types+ property.
def next def next
return nil if @eof row = @stmt.step
return nil if @stmt.done?
@stmt.must_be_open! if @db.type_translation
row = @stmt.types.zip(row).map do |type, value|
unless @first_row @db.translator.translate( type, value )
result = @driver.step( @stmt.handle ) end
check result
end end
@first_row = false if @db.results_as_hash
new_row = HashWithTypes[*@stmt.columns.zip(row).flatten]
unless @eof row.each_with_index { |value,idx|
row = [] new_row[idx] = value
@driver.data_count( @stmt.handle ).times do |column| }
type = @driver.column_type( @stmt.handle, column ) row = new_row
else
if type == Constants::ColumnType::TEXT if row.respond_to?(:fields)
row << @driver.column_text( @stmt.handle, column ) row = ArrayWithTypes.new(row)
elsif type == Constants::ColumnType::NULL
row << nil
elsif type == Constants::ColumnType::BLOB
row << @driver.column_blob( @stmt.handle, column )
else
row << @driver.column_text( @stmt.handle, column )
end
end
if @db.type_translation
row = @stmt.types.zip( row ).map do |type, value|
@db.translator.translate( type, value )
end
end
if @db.results_as_hash
new_row = HashWithTypes[ *( @stmt.columns.zip( row ).to_a.flatten ) ]
row.each_with_index { |value,idx|
value.taint
new_row[idx] = value
}
row = new_row
else else
if row.respond_to?(:fields) row = ArrayWithTypesAndFields.new(row)
row = ArrayWithTypes.new(row)
else
row = ArrayWithTypesAndFields.new(row)
end
row.fields = @stmt.columns
row.each { |column| column.taint }
end end
row.fields = @stmt.columns
row.types = @stmt.types
return row
end end
nil row.types = @stmt.types
row
end end
# Required by the Enumerable mixin. Provides an internal iterator over the # Required by the Enumerable mixin. Provides an internal iterator over the
# rows of the result set. # rows of the result set.
def each def each( &block )
while row=self.next while node = self.next
yield row yield node
end end
end end

View file

@ -8,51 +8,17 @@ class String
end end
module SQLite3 module SQLite3
# A class for differentiating between strings and blobs, when binding them
# into statements.
class Blob < String; end
# A statement represents a prepared-but-unexecuted SQL query. It will rarely # A statement represents a prepared-but-unexecuted SQL query. It will rarely
# (if ever) be instantiated directly by a client, and is most often obtained # (if ever) be instantiated directly by a client, and is most often obtained
# via the Database#prepare method. # via the Database#prepare method.
class Statement class Statement
include Enumerable
# This is any text that followed the first valid SQL statement in the text # This is any text that followed the first valid SQL statement in the text
# with which the statement was initialized. If there was no trailing text, # with which the statement was initialized. If there was no trailing text,
# this will be the empty string. # this will be the empty string.
attr_reader :remainder attr_reader :remainder
# The underlying opaque handle used to access the SQLite @driver.
attr_reader :handle
# Create a new statement attached to the given Database instance, and which
# encapsulates the given SQL text. If the text contains more than one
# statement (i.e., separated by semicolons), then the #remainder property
# will be set to the trailing text.
def initialize( db, sql, utf16=false )
raise ArgumentError, "nil argument passed as sql text" unless sql
@db = db
@driver = @db.driver
@closed = false
@results = @columns = nil
result, @handle, @remainder = @driver.prepare( @db.handle, sql )
Error.check( result, @db )
end
# Closes the statement by finalizing the underlying statement
# handle. The statement must not be used after being closed.
def close
must_be_open!
@closed = true
@driver.finalize( @handle )
end
# Returns true if the underlying statement has been closed.
def closed?
@closed
end
# Binds the given variables to the corresponding placeholders in the SQL # Binds the given variables to the corresponding placeholders in the SQL
# text. # text.
# #
@ -78,42 +44,6 @@ module SQLite3
end end
end end
# Binds value to the named (or positional) placeholder. If +param+ is a
# Fixnum, it is treated as an index for a positional placeholder.
# Otherwise it is used as the name of the placeholder to bind to.
#
# See also #bind_params.
def bind_param( param, value )
must_be_open!
reset! if active?
if Fixnum === param
case value
when Bignum then
@driver.bind_int64( @handle, param, value )
when Integer then
if value >= (2 ** 31)
@driver.bind_int64( @handle, param, value )
else
@driver.bind_int( @handle, param, value )
end
when Numeric then
@driver.bind_double( @handle, param, value.to_f )
when Blob then
@driver.bind_blob( @handle, param, value )
when nil then
@driver.bind_null( @handle, param )
else
@driver.bind_text( @handle, param, value )
end
else
param = param.to_s
param = ":#{param}" unless param[0] == ?:
index = @driver.bind_parameter_index( @handle, param )
raise Exception, "no such bind parameter '#{param}'" if index == 0
bind_param index, value
end
end
# Execute the statement. This creates a new ResultSet object for the # Execute the statement. This creates a new ResultSet object for the
# statement's virtual machine. If a block was given, the new ResultSet will # statement's virtual machine. If a block was given, the new ResultSet will
# be yielded to it; otherwise, the ResultSet will be returned. # be yielded to it; otherwise, the ResultSet will be returned.
@ -129,17 +59,13 @@ module SQLite3
# #
# See also #bind_params, #execute!. # See also #bind_params, #execute!.
def execute( *bind_vars ) def execute( *bind_vars )
must_be_open! reset! if active? || done?
reset! if active?
bind_params(*bind_vars) unless bind_vars.empty? bind_params(*bind_vars) unless bind_vars.empty?
@results = ResultSet.new( @db, self ) @results = ResultSet.new(@connection, self)
if block_given? yield @results if block_given?
yield @results @results
else
return @results
end
end end
# Execute the statement. If no block was given, this returns an array of # Execute the statement. If no block was given, this returns an array of
@ -156,30 +82,15 @@ module SQLite3
# end # end
# #
# See also #bind_params, #execute. # See also #bind_params, #execute.
def execute!( *bind_vars ) def execute!( *bind_vars, &block )
result = execute( *bind_vars ) execute(*bind_vars)
rows = [] unless block_given? block_given? ? each(&block) : to_a
while row = result.next
if block_given?
yield row
else
rows << row
end
end
rows
end
# Resets the statement. This is typically done internally, though it might
# occassionally be necessary to manually reset the statement.
def reset!(clear_result=true)
@driver.reset(@handle)
@results = nil if clear_result
end end
# Returns true if the statement is currently active, meaning it has an # Returns true if the statement is currently active, meaning it has an
# open result set. # open result set.
def active? def active?
not @results.nil? !done?
end end
# Return an array of the column names for this statement. Note that this # Return an array of the column names for this statement. Note that this
@ -190,11 +101,20 @@ module SQLite3
return @columns return @columns
end end
def each
loop do
val = step
break self if done?
yield val
end
end
# Return an array of the data types for each column in this statement. Note # Return an array of the data types for each column in this statement. Note
# that this may execute the statement in order to obtain the metadata; this # that this may execute the statement in order to obtain the metadata; this
# makes it a (potentially) expensive operation. # makes it a (potentially) expensive operation.
def types def types
get_metadata unless defined?(@types) must_be_open!
get_metadata unless @types
@types @types
end end
@ -202,15 +122,12 @@ module SQLite3
# that this will actually execute the SQL, which means it can be a # that this will actually execute the SQL, which means it can be a
# (potentially) expensive operation. # (potentially) expensive operation.
def get_metadata def get_metadata
must_be_open!
@columns = [] @columns = []
@types = [] @types = []
column_count = @driver.column_count( @handle )
column_count.times do |column| column_count.times do |column|
@columns << @driver.column_name( @handle, column ) @columns << column_name(column)
@types << @driver.column_decltype( @handle, column ) @types << column_decltype(column)
end end
@columns.freeze @columns.freeze
@ -221,11 +138,9 @@ module SQLite3
# Performs a sanity check to ensure that the statement is not # Performs a sanity check to ensure that the statement is not
# closed. If it is, an exception is raised. # closed. If it is, an exception is raised.
def must_be_open! # :nodoc: def must_be_open! # :nodoc:
if @closed if closed?
raise SQLite3::Exception, "cannot use a closed statement" raise SQLite3::Exception, "cannot use a closed statement"
end end
end end
end end
end end

View file

@ -44,7 +44,12 @@ module SQLite3
# and are always passed straight through regardless of the type parameter. # and are always passed straight through regardless of the type parameter.
def translate( type, value ) def translate( type, value )
unless value.nil? unless value.nil?
@translators[ type_name( type ) ].call( type, value ) # FIXME: this is a hack to support Sequel
if type && %w{ datetime timestamp }.include?(type.downcase)
@translators[ type_name( type ) ].call( type, value.to_s )
else
@translators[ type_name( type ) ].call( type, value )
end
end end
end end

View file

@ -3,14 +3,14 @@ module SQLite3
module Version module Version
MAJOR = 1 MAJOR = 1
MINOR = 2 MINOR = 3
TINY = 5 TINY = 0
BUILD = nil BUILD = nil
STRING = [ MAJOR, MINOR, TINY, BUILD ].compact.join( "." ) STRING = [ MAJOR, MINOR, TINY, BUILD ].compact.join( "." )
#:beta-tag: #:beta-tag:
VERSION = '1.2.5' VERSION = '1.3.0'
end end
end end

View file

@ -481,7 +481,7 @@ module FileOperations
end end
def extdir?(dir) def extdir?(dir)
File.exist?(dir + '/MANIFEST') File.exist?(dir + '/MANIFEST') or File.exist?("#{dir}/extconf.rb")
end end
def all_files_in(dirname) def all_files_in(dirname)
@ -1149,7 +1149,7 @@ class Installer
def install_dir_ext(rel) def install_dir_ext(rel)
return unless extdir?(curr_srcdir()) return unless extdir?(curr_srcdir())
install_files ruby_extentions('.'), install_files ruby_extentions('.'),
"#{config('so-dir')}/#{File.dirname(rel)}", "#{config('so-dir')}/#{rel}",
0555 0555
end end

View file

@ -1,141 +0,0 @@
--- !ruby/object:Gem::Specification
name: sqlite3-ruby
version: !ruby/object:Gem::Version
version: 1.2.5
platform: ruby
authors:
- Jamis Buck
autorequire:
bindir: bin
cert_chain: []
date: 2009-07-25 00:00:00 -03:00
default_executable:
dependencies:
- !ruby/object:Gem::Dependency
name: mocha
type: :development
version_requirement:
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: "0"
version:
- !ruby/object:Gem::Dependency
name: rake-compiler
type: :development
version_requirement:
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ~>
- !ruby/object:Gem::Version
version: 0.5.0
version:
- !ruby/object:Gem::Dependency
name: hoe
type: :development
version_requirement:
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: 2.3.2
version:
description: |-
This module allows Ruby programs to interface with the SQLite3
database engine (http://www.sqlite.org). You must have the
SQLite engine installed in order to build this module.
Note that this module is NOT compatible with SQLite 2.x.
email:
- jamis@37signals.com
executables: []
extensions:
- ext/sqlite3_api/extconf.rb
extra_rdoc_files:
- History.txt
- Manifest.txt
- README.txt
files:
- ChangeLog.cvs
- History.txt
- LICENSE
- Manifest.txt
- README.txt
- Rakefile
- ext/sqlite3_api/extconf.rb
- ext/sqlite3_api/sqlite3_api.i
- ext/sqlite3_api/sqlite3_api_wrap.c
- faq/faq.rb
- faq/faq.yml
- lib/sqlite3.rb
- lib/sqlite3/constants.rb
- lib/sqlite3/database.rb
- lib/sqlite3/driver/dl/api.rb
- lib/sqlite3/driver/dl/driver.rb
- lib/sqlite3/driver/native/driver.rb
- lib/sqlite3/errors.rb
- lib/sqlite3/pragmas.rb
- lib/sqlite3/resultset.rb
- lib/sqlite3/statement.rb
- lib/sqlite3/translator.rb
- lib/sqlite3/value.rb
- lib/sqlite3/version.rb
- setup.rb
- tasks/benchmark.rake
- tasks/faq.rake
- tasks/gem.rake
- tasks/native.rake
- tasks/vendor_sqlite3.rake
- test/bm.rb
- test/driver/dl/tc_driver.rb
- test/helper.rb
- test/native-vs-dl.rb
- test/test_database.rb
- test/test_errors.rb
- test/test_integration.rb
- test/test_integration_open_close.rb
- test/test_integration_pending.rb
- test/test_integration_resultset.rb
- test/test_integration_statement.rb
has_rdoc: true
homepage: http://sqlite3-ruby.rubyforge.org
licenses: []
post_install_message:
rdoc_options:
- --main
- README.txt
- --main=README.txt
require_paths:
- lib
- ext
required_ruby_version: !ruby/object:Gem::Requirement
requirements:
- - ">"
- !ruby/object:Gem::Version
version: 1.8.5
version:
required_rubygems_version: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: "0"
version:
requirements: []
rubyforge_project: sqlite-ruby
rubygems_version: 1.3.5
signing_key:
specification_version: 3
summary: This module allows Ruby programs to interface with the SQLite3 database engine (http://www.sqlite.org)
test_files:
- test/test_database.rb
- test/test_errors.rb
- test/test_integration.rb
- test/test_integration_open_close.rb
- test/test_integration_pending.rb
- test/test_integration_resultset.rb
- test/test_integration_statement.rb

View file

@ -1,140 +0,0 @@
require 'benchmark'
N = 1000
$VERBOSE=nil
puts "file require"
Benchmark.bm( 7 ) do |x|
x.report('sqlite') do
N.times do
$".delete_if { |i| i =~ /sqlite/ }
require 'sqlite'
end
end
x.report('sqlite3') do
N.times do
$".delete_if { |i| i =~ /sqlite3/ }
require 'sqlite3'
end
end
end
puts
puts "database creation..."
Benchmark.bm( 7 ) do |x|
x.report('sqlite') do
N.times do
File.delete "test.db" rescue nil
SQLite::Database.open( "test.db" ).close
end
end
x.report('sqlite3') do
N.times do
File.delete "test.db" rescue nil
SQLite3::Database.open( "test.db" ).close
end
end
end
File.delete "test.db" rescue nil
SQLite::Database.open( "test.db" ).close
SQLite3::Database.open( "test3.db" ).close
puts
puts "database open..."
Benchmark.bm( 7 ) do |x|
x.report('sqlite') do
N.times do
SQLite::Database.open( "test.db" ).close
end
end
x.report('sqlite3') do
N.times do
SQLite3::Database.open( "test3.db" ).close
end
end
end
File.delete "test.db" rescue nil
File.delete "test3.db" rescue nil
db = SQLite::Database.open( "test.db" )
db3 = SQLite3::Database.open( "test3.db" )
db.execute "create table foo (a,b)"
db3.execute "create table foo (a,b)"
puts
puts "insertions"
Benchmark.bm( 7 ) do |x|
x.report('sqlite') do
db.transaction do
N.times do |i|
db.execute "insert into foo values (#{i}, #{i+1})"
end
end
end
x.report('sqlite3') do
db3.transaction do
N.times do |i|
db3.execute "insert into foo values (#{i}, #{i+1})"
end
end
end
end
puts
puts "insertions using prepared statement"
Benchmark.bm( 7 ) do |x|
x.report('sqlite') do
db.transaction do
stmt = db.prepare "insert into foo values (?,?)"
N.times { |i| stmt.execute i, i+1 }
end
end
x.report('sqlite3') do
db3.transaction do
db3.prepare( "insert into foo values (?,?)" ) do |stmt|
N.times { |i| stmt.execute i, i+1 }
end
end
end
end
db.close
db3.close
File.delete "test.db" rescue nil
File.delete "test3.db" rescue nil
db = SQLite::Database.open( "test.db" )
db3 = SQLite3::Database.open( "test3.db" )
db.execute "create table foo (a,b)"
db.execute "insert into foo values (1,2)"
db.execute "insert into foo values (3,4)"
db.execute "insert into foo values (5,6)"
db3.execute "create table foo (a,b)"
db3.execute "insert into foo values (1,2)"
db3.execute "insert into foo values (3,4)"
db3.execute "insert into foo values (5,6)"
puts
puts "queries"
Benchmark.bm( 7 ) do |x|
x.report('sqlite') do
N.times do
db.execute "select * from foo"
end
end
x.report('sqlite3') do
N.times do
db3.execute "select * from foo"
end
end
end
db.close
db3.close
File.delete "test.db" rescue nil
File.delete "test3.db" rescue nil

View file

@ -1,292 +0,0 @@
if (ENV["SQLITE3_DRIVERS"] || "Native").split(/,/).include?("DL")
$:.unshift "../../../lib"
require 'sqlite3/constants'
require 'sqlite3/driver/dl/driver'
require 'test/unit'
class TC_DL_Driver < Test::Unit::TestCase
def utf16ify( str )
chars = str.split(//)
chars.zip(["\0"] * chars.length).flatten.join
end
def setup
@driver = SQLite3::Driver::DL::Driver.new
@dbname = "test.db"
@db = nil
end
def teardown
@driver.close( @db ) rescue nil
File.delete @dbname rescue nil
end
def test_open
result, @db = @driver.open( @dbname )
assert_equal SQLite3::Constants::ErrorCode::OK, result
assert File.exist?( @dbname )
end
def test_open_utf16
name = utf16ify( @dbname )
result, @db = @driver.open( name, true )
assert_equal SQLite3::Constants::ErrorCode::OK, result
assert File.exist?( @dbname )
end
def test_errmsg
result, @db = @driver.open( @dbname )
msg = @driver.errmsg( @db )
assert_equal msg, "not an error"
end
def test_errmsg16
result, @db = @driver.open( @dbname )
msg = @driver.errmsg( @db, true )
assert_equal msg, utf16ify( "not an error" )
end
def test_prepare
result, @db = @driver.open( @dbname )
sql = "create table foo ( a, b )"
result, handle, remainder = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
assert_equal "", remainder
@driver.finalize( handle )
end
def test_prepare_error
result, @db = @driver.open( @dbname )
sql = "create tble foo ( a, b )"
result, handle, remainder = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::ERROR, result
end
def test_prepare_remainder
result, @db = @driver.open( @dbname )
sql = "create table foo ( a, b ); select * from foo"
result, handle, remainder = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
assert_equal " select * from foo", remainder
@driver.finalize( handle )
end
def test_prepare16
result, @db = @driver.open( @dbname )
sql = utf16ify( "create table foo ( a, b )" )
result, handle, remainder = @driver.prepare( @db, sql, true )
assert_equal SQLite3::Constants::ErrorCode::OK, result
assert_equal "", remainder
@driver.finalize( handle )
end
def test_prepare16_remainder
result, @db = @driver.open( @dbname )
sql = utf16ify( "create table foo ( a, b ); select * from foo" )
result, handle, remainder = @driver.prepare( @db, sql, true )
assert_equal SQLite3::Constants::ErrorCode::OK, result
assert_equal utf16ify( " select * from foo" ), remainder
@driver.finalize( handle )
end
def test_complete
assert @driver.complete?( "select * from foo;" )
end
def test_complete_fail
assert !@driver.complete?( "select * from foo" )
end
def test_complete16
assert @driver.complete?( utf16ify("select * from foo;"), true )
end
def create_foo
result, @db = @driver.open( @dbname )
sql = "create table foo ( a, b )"
result, handle, = @driver.prepare( @db, sql )
@driver.step( handle )
@driver.finalize( handle )
end
def populate_foo
create_foo
sql = "insert into foo values ( 100, 200 )"
result, handle, = @driver.prepare( @db, sql )
@driver.step( handle )
@driver.finalize( handle )
end
def test_step
populate_foo
sql = "select * from foo"
result, handle, = @driver.prepare( @db, sql )
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::ROW, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::DONE, result
@driver.finalize( handle )
end
def test_step_fail
populate_foo
sql = "select * from"
result, handle, = @driver.prepare( @db, sql )
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::MISUSE, result
@driver.finalize( handle )
end
def test_bind_blob
create_foo
sql = "insert into foo (b) values (?)"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.bind_blob( handle, 1, "a\0b\1c\2d\0e" )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::DONE, result
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
sql = "select b from foo"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::ROW, result
assert_equal "a\0b\1c\2d\0e", @driver.column_blob( handle, 0 )
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
end
def test_bind_double
create_foo
sql = "insert into foo (b) values (?)"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.bind_double( handle, 1, 3.14 )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::DONE, result
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
sql = "select b from foo"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::ROW, result
assert_equal 3.14, @driver.column_double( handle, 0 )
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
end
def test_bind_int
create_foo
sql = "insert into foo (b) values (?)"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.bind_int( handle, 1, 14 )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::DONE, result
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
sql = "select b from foo"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::ROW, result
assert_equal 14, @driver.column_int( handle, 0 )
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
end
def test_bind_null
create_foo
sql = "insert into foo (b) values (?)"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.bind_null( handle, 1 )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::DONE, result
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
sql = "select b from foo"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::ROW, result
assert_equal SQLite3::Constants::ColumnType::NULL,
@driver.column_type( handle, 0 )
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
end
def test_bind_text
create_foo
sql = "insert into foo (b) values (?)"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.bind_text( handle, 1, "hello, world" )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::DONE, result
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
sql = "select b from foo"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::ROW, result
assert_equal "hello, world", @driver.column_text( handle, 0 )
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
end
def test_bind_text16
create_foo
sql = "insert into foo (b) values (?)"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.bind_text( handle, 1, utf16ify("hello, world"), true )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::DONE, result
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
sql = "select b from foo"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.step( handle )
assert_equal SQLite3::Constants::ErrorCode::ROW, result
assert_equal "hello, world", @driver.column_text( handle, 0 )
result = @driver.finalize( handle )
assert_equal SQLite3::Constants::ErrorCode::OK, result
end
def test_bind_parameter_index
create_foo
sql = "insert into foo (b) values (:hello)"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
result = @driver.bind_parameter_index( handle, ":hello" )
assert_equal 1, result
result = @driver.bind_parameter_index( handle, ":foo" )
assert_equal 0, result
@driver.finalize( handle )
end
def test_bind_parameter_name
create_foo
sql = "insert into foo (a,b) values (?,:foo)"
result, handle, = @driver.prepare( @db, sql )
assert_equal SQLite3::Constants::ErrorCode::OK, result
assert_nil nil, @driver.bind_parameter_name(handle,1)
assert_equal ":foo", @driver.bind_parameter_name(handle,2)
@driver.finalize( handle )
end
end
end

View file

@ -1,67 +1,3 @@
# add lib folder to the path
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'sqlite3' require 'sqlite3'
require 'rubygems'
require 'test/unit' require 'test/unit'
require 'iconv'
# define mocks to be used
require 'mocha'
class Driver < Mocha::Mock
def initialize
super
stubs( :open ).returns([0, 'cookie'])
stubs( :close ).returns(0)
stubs( :complete? ).returns(0)
stubs( :errmsg ).returns('')
stubs( :errcode ).returns(0)
stubs( :trace ).returns(nil)
stubs( :set_authorizer ).returns(0)
stubs( :prepare ).returns([0, 'stmt', 'remainder'])
stubs( :finalize ).returns(0)
stubs( :changes ).returns(14)
stubs( :total_changes ).returns(28)
stubs( :interrupt ).returns(0)
end
end
class MockResultSet < Mocha::Mock
def initialize
super
stubs( :each ).yields(['foo'])
stubs( :columns ).returns(['name'])
end
end
class Statement < Mocha::Mock
attr_reader :handle
attr_reader :sql
attr_reader :last_result
def initialize( handle, sql )
super()
@handle = handle
@sql = sql
stubs( :close ).returns(0)
stubs( :remainder ).returns('')
stubs( :execute ).returns(MockResultSet.new)
end
end
# UTF conversion extensions
class String
def to_utf16(terminate=false)
self.split(//).map { |c| c[0] }.pack("v*") +
(terminate ? "\0\0" : "")
end
def from_utf16
result = ""
length.times do |i|
result << self[i,1] if i % 2 == 0 && self[i] != 0
end
result
end
end

View file

@ -1,126 +0,0 @@
$:.unshift "../lib", "../ext/sqlite3_api"
require 'sqlite3'
require 'benchmark'
N = 1000
$VERBOSE=nil
puts "database creation..."
Benchmark.bm( 7 ) do |x|
x.report('dl') do
N.times do
File.delete "test.db" rescue nil
SQLite3::Database.open( "test.db", :driver => "DL" ).close
end
end
x.report('native') do
N.times do
File.delete "test.db" rescue nil
SQLite3::Database.open( "test.db", :driver => "Native" ).close
end
end
end
File.delete "test.db" rescue nil
SQLite3::Database.open( "test.db" ).close
puts
puts "database open..."
Benchmark.bm( 7 ) do |x|
x.report('dl') do
N.times do
SQLite3::Database.open( "test.db", :driver => "DL" ).close
end
end
x.report('native') do
N.times do
SQLite3::Database.open( "test.db", :driver => "Native" ).close
end
end
end
File.delete "test.db" rescue nil
dl = SQLite3::Database.open( "test-dl.db", :driver => "DL" )
native = SQLite3::Database.open( "test-native.db", :driver => "Native" )
dl.execute "create table foo (a,b)"
native.execute "create table foo (a,b)"
puts
puts "insertions"
Benchmark.bm( 7 ) do |x|
x.report('dl') do
dl.transaction do
N.times do |i|
dl.execute "insert into foo values (#{i}, #{i+1})"
end
end
end
x.report('native') do
native.transaction do
N.times do |i|
native.execute "insert into foo values (#{i}, #{i+1})"
end
end
end
end
puts
puts "insertions using prepared statement"
Benchmark.bm( 7 ) do |x|
x.report('dl') do
dl.transaction do
dl.prepare "insert into foo values (?,?)" do |stmt|
N.times { |i| stmt.execute i, i+1 }
end
end
end
x.report('native') do
native.transaction do
native.prepare( "insert into foo values (?,?)" ) do |stmt|
N.times { |i| stmt.execute i, i+1 }
end
end
end
end
dl.close
native.close
File.delete "test-dl.db" rescue nil
File.delete "test-native.db" rescue nil
dl = SQLite3::Database.open( "test-dl.db", :driver => "DL" )
native = SQLite3::Database.open( "test-native.db", :driver => "Native" )
dl.execute "create table foo (a,b)"
dl.execute "insert into foo values (1,2)"
dl.execute "insert into foo values (3,4)"
dl.execute "insert into foo values (5,6)"
native.execute "create table foo (a,b)"
native.execute "insert into foo values (1,2)"
native.execute "insert into foo values (3,4)"
native.execute "insert into foo values (5,6)"
puts
puts "queries"
Benchmark.bm( 7 ) do |x|
x.report('dl') do
N.times do
dl.execute "select * from foo"
end
end
x.report('native') do
N.times do
native.execute "select * from foo"
end
end
end
dl.close
native.close
File.delete "test-dl.db" rescue nil
File.delete "test-native.db" rescue nil

View file

@ -1,217 +1,291 @@
require File.join(File.dirname(__FILE__), 'helper') require 'helper'
require 'iconv'
class TC_Database_Init < Test::Unit::TestCase module SQLite3
def test_new class TestDatabase < Test::Unit::TestCase
# any_instance fails here... def setup
driver = Driver.new @db = SQLite3::Database.new(':memory:')
driver.expects(:open).once.with('foo.db', false).returns([0, 'cookie'])
Driver.stubs(:new).returns(driver)
db = SQLite3::Database.new( 'foo.db', :driver => Driver )
assert !db.closed?
assert !db.results_as_hash
assert !db.type_translation
end
def test_new_with_block
driver = Driver.new
driver.expects(:open).once.with('foo.db', false).returns([0, 'cookie'])
Driver.stubs(:new).returns(driver)
returned_db = SQLite3::Database.new( "foo.db", :driver => Driver ) do |db|
assert !db.closed?
assert !db.results_as_hash
assert !db.type_translation
end end
assert returned_db.closed?
end
def test_open def test_changes
driver = Driver.new @db.execute("CREATE TABLE items (id integer PRIMARY KEY AUTOINCREMENT, number integer)")
driver.expects(:open).once.with('foo.db', false).returns([0, 'cookie']) assert_equal 0, @db.changes
Driver.stubs(:new).returns(driver) @db.execute("INSERT INTO items (number) VALUES (10)")
db = SQLite3::Database.open( "foo.db", :driver => Driver ) assert_equal 1, @db.changes
assert !db.closed? @db.execute_batch(
assert !db.results_as_hash "UPDATE items SET number = (number + :nn) WHERE (number = :n)",
assert !db.type_translation {"nn" => 20, "n" => 10})
end assert_equal 1, @db.changes
assert_equal [[30]], @db.execute("select number from items")
def test_open_with_block end
driver = Driver.new
driver.expects(:open).once.with('foo.db', false).returns([0, 'cookie']) def test_new
Driver.stubs(:new).returns(driver) db = SQLite3::Database.new(':memory:')
returned_db = SQLite3::Database.open( "foo.db", :driver => Driver ) do |db| assert db
assert !db.closed? end
assert !db.results_as_hash
assert !db.type_translation def test_new_yields_self
thing = nil
SQLite3::Database.new(':memory:') do |db|
thing = db
end
assert_instance_of(SQLite3::Database, thing)
end
def test_new_with_options
db = SQLite3::Database.new(Iconv.conv('UTF-16LE', 'UTF-8', ':memory:'),
:utf16 => true)
assert db
end
def test_close
db = SQLite3::Database.new(':memory:')
db.close
assert db.closed?
end
def test_block_closes_self
thing = nil
SQLite3::Database.new(':memory:') do |db|
thing = db
assert !thing.closed?
end
assert thing.closed?
end
def test_prepare
db = SQLite3::Database.new(':memory:')
stmt = db.prepare('select "hello world"')
assert_instance_of(SQLite3::Statement, stmt)
end
def test_total_changes
db = SQLite3::Database.new(':memory:')
db.execute("create table foo ( a integer primary key, b text )")
db.execute("insert into foo (b) values ('hello')")
assert_equal 1, db.total_changes
end
def test_execute_returns_list_of_hash
db = SQLite3::Database.new(':memory:', :results_as_hash => true)
db.execute("create table foo ( a integer primary key, b text )")
db.execute("insert into foo (b) values ('hello')")
rows = db.execute("select * from foo")
assert_equal [{0=>1, "a"=>1, "b"=>"hello", 1=>"hello"}], rows
end
def test_execute_yields_hash
db = SQLite3::Database.new(':memory:', :results_as_hash => true)
db.execute("create table foo ( a integer primary key, b text )")
db.execute("insert into foo (b) values ('hello')")
db.execute("select * from foo") do |row|
assert_equal({0=>1, "a"=>1, "b"=>"hello", 1=>"hello"}, row)
end
end
def test_table_info
db = SQLite3::Database.new(':memory:', :results_as_hash => true)
db.execute("create table foo ( a integer primary key, b text )")
info = [{
"name" => "a",
"pk" => 1,
"notnull" => 0,
"type" => "integer",
"dflt_value" => nil,
"cid" => 0
},
{
"name" => "b",
"pk" => 0,
"notnull" => 0,
"type" => "text",
"dflt_value" => nil,
"cid" => 1
}]
assert_equal info, db.table_info('foo')
end
def test_total_changes_closed
db = SQLite3::Database.new(':memory:')
db.close
assert_raise(SQLite3::Exception) do
db.total_changes
end
end
def test_trace_requires_opendb
@db.close
assert_raise(SQLite3::Exception) do
@db.trace { |x| }
end
end
def test_trace_with_block
result = nil
@db.trace { |sql| result = sql }
@db.execute "select 'foo'"
assert_equal "select 'foo'", result
end
def test_trace_with_object
obj = Class.new {
attr_accessor :result
def call sql; @result = sql end
}.new
@db.trace(obj)
@db.execute "select 'foo'"
assert_equal "select 'foo'", obj.result
end
def test_trace_takes_nil
@db.trace(nil)
@db.execute "select 'foo'"
end
def test_last_insert_row_id_closed
@db.close
assert_raise(SQLite3::Exception) do
@db.last_insert_row_id
end
end
def test_define_function
called_with = nil
@db.define_function("hello") do |value|
called_with = value
end
@db.execute("select hello(10)")
assert_equal 10, called_with
end
def test_call_func_arg_type
called_with = nil
@db.define_function("hello") do |b, c, d|
called_with = [b, c, d]
nil
end
@db.execute("select hello(2.2, 'foo', NULL)")
assert_equal [2.2, 'foo', nil], called_with
end
def test_define_varargs
called_with = nil
@db.define_function("hello") do |*args|
called_with = args
nil
end
@db.execute("select hello(2.2, 'foo', NULL)")
assert_equal [2.2, 'foo', nil], called_with
end
def test_function_return
@db.define_function("hello") { |a| 10 }
assert_equal [10], @db.execute("select hello('world')").first
end
def test_function_return_types
[10, 2.2, nil, "foo"].each do |thing|
@db.define_function("hello") { |a| thing }
assert_equal [thing], @db.execute("select hello('world')").first
end
end
def test_define_function_closed
@db.close
assert_raise(SQLite3::Exception) do
@db.define_function('foo') { }
end
end
def test_inerrupt_closed
@db.close
assert_raise(SQLite3::Exception) do
@db.interrupt
end
end
def test_define_aggregate
@db.execute "create table foo ( a integer primary key, b text )"
@db.execute "insert into foo ( b ) values ( 'foo' )"
@db.execute "insert into foo ( b ) values ( 'bar' )"
@db.execute "insert into foo ( b ) values ( 'baz' )"
acc = Class.new {
attr_reader :sum
alias :finalize :sum
def initialize
@sum = 0
end
def step a
@sum += a
end
}.new
@db.define_aggregator("accumulate", acc)
value = @db.get_first_value( "select accumulate(a) from foo" )
assert_equal 6, value
end
def test_authorizer_ok
@db.authorizer = Class.new {
def call action, a, b, c, d; true end
}.new
@db.prepare("select 'fooooo'")
@db.authorizer = Class.new {
def call action, a, b, c, d; 0 end
}.new
@db.prepare("select 'fooooo'")
end
def test_authorizer_ignore
@db.authorizer = Class.new {
def call action, a, b, c, d; nil end
}.new
stmt = @db.prepare("select 'fooooo'")
assert_equal nil, stmt.step
end
def test_authorizer_fail
@db.authorizer = Class.new {
def call action, a, b, c, d; false end
}.new
assert_raises(SQLite3::AuthorizationException) do
@db.prepare("select 'fooooo'")
end
end
def test_remove_auth
@db.authorizer = Class.new {
def call action, a, b, c, d; false end
}.new
assert_raises(SQLite3::AuthorizationException) do
@db.prepare("select 'fooooo'")
end
@db.authorizer = nil
@db.prepare("select 'fooooo'")
end
def test_close_with_open_statements
stmt = @db.prepare("select 'foo'")
assert_raises(SQLite3::BusyException) do
@db.close
end
end
def test_execute_with_empty_bind_params
assert_equal [['foo']], @db.execute("select 'foo'", [])
end
def test_query_with_named_bind_params
assert_equal [['foo']], @db.query("select :n", {'n' => 'foo'}).to_a
end
def test_execute_with_named_bind_params
assert_equal [['foo']], @db.execute("select :n", {'n' => 'foo'})
end end
assert returned_db.closed?
end
def test_with_type_translation
db = SQLite3::Database.open( "foo.db", :driver => Driver,
:type_translation => true )
assert db.type_translation
end
def test_with_results_as_hash
db = SQLite3::Database.open( "foo.db", :driver => Driver,
:results_as_hash => true )
assert db.results_as_hash
end
def test_with_type_translation_and_results_as_hash
db = SQLite3::Database.open( "foo.db", :driver => Driver,
:results_as_hash => true,
:type_translation => true )
assert db.results_as_hash
assert db.type_translation
end
end
class TC_Database < Test::Unit::TestCase
def setup
@db = SQLite3::Database.open( "foo.db",
:driver => Driver, :statement_factory => Statement )
end
def test_quote
assert_equal "''one''two''three''", SQLite3::Database.quote(
"'one'two'three'" )
end
def test_complete
Driver.any_instance.expects(:complete?)
@db.complete? "foo"
end
def test_errmsg
Driver.any_instance.expects(:errmsg)
@db.errmsg
end
def test_errcode
Driver.any_instance.expects(:errcode)
@db.errcode
end
def test_translator
translator = @db.translator
assert_instance_of SQLite3::Translator, translator
end
def test_close
Driver.any_instance.expects(:close).returns(0)
@db.close
assert @db.closed?
Driver.any_instance.expects(:close).never
@db.close
end
def test_trace
Driver.any_instance.expects(:trace).with('cookie', 15)
@db.trace( 15 ) { "foo" }
# assert_equal 1, driver.mock_blocks[:trace].length
end
def test_authorizer
Driver.any_instance.expects(:set_authorizer).with('cookie', 15).returns(0)
@db.authorizer( 15 ) { "foo" }
# assert_equal 1, driver.mock_blocks[:set_authorizer].length
end
def test_prepare_no_block
Statement.any_instance.expects(:close).never
assert_nothing_raised { @db.prepare( "foo" ) }
end
def test_prepare_with_block
called = false
# any_instance fails here...
statement = Statement.new('cookie', 'foo')
statement.expects(:close).once
Statement.stubs(:new).returns(statement)
@db.prepare( "foo" ) { |stmt| called = true }
assert called
end
def test_execute_no_block
# any_instance fails here...
statement = Statement.new('cookie', 'foo')
statement.expects(:execute).with('bar', 'baz').returns(MockResultSet.new)
Statement.stubs(:new).returns(statement)
MockResultSet.any_instance.stubs(:inject).returns([['foo']])
result = @db.execute( "foo", "bar", "baz" )
assert_equal [["foo"]], result
end
def test_execute_with_block
called = false
# any_instance fails here...
statement = Statement.new('cookie', 'foo')
statement.expects(:execute).with('bar', 'baz').returns(MockResultSet.new)
Statement.stubs(:new).returns(statement)
@db.execute( "foo", "bar", "baz" ) do |row|
called = true
assert_equal ["foo"], row
end
assert called
end
def test_execute2_no_block
# any_instance fails here...
statement = Statement.new('cookie', 'foo')
statement.expects(:execute).with('bar', 'baz').returns(MockResultSet.new)
Statement.stubs(:new).returns(statement)
MockResultSet.any_instance.stubs(:inject).returns([['name'], ['foo']])
result = @db.execute2( "foo", "bar", "baz" )
assert_equal [["name"],["foo"]], result
end
def test_execute2_with_block
called = false
parts = [ ["name"],["foo"] ]
# any_instance fails here...
statement = Statement.new('cookie', 'foo')
statement.expects(:execute).with('bar', 'baz').returns(MockResultSet.new)
Statement.stubs(:new).returns(statement)
@db.execute2( "foo", "bar", "baz" ) do |row|
called = true
assert_equal parts.shift, row
end
assert called
end
def test_execute_batch
# any_instance fails here...
statement = Statement.new('cookie', 'foo')
statement.expects(:execute).with('bar', 'baz').returns(MockResultSet.new)
Statement.stubs(:new).returns(statement)
@db.execute_batch( "foo", "bar", "baz" )
end
def test_get_first_row
result = @db.get_first_row( "foo", "bar", "baz" )
assert_equal ["foo"], result
end
def test_get_first_value
result = @db.get_first_value( "foo", "bar", "baz" )
assert_equal "foo", result
end
def test_changes
Driver.any_instance.expects(:changes).returns(14)
assert_equal 14, @db.changes
end
def test_total_changes
Driver.any_instance.expects(:total_changes).returns(28)
assert_equal 28, @db.total_changes
end
def test_interrupt
Driver.any_instance.expects(:interrupt)
@db.interrupt
end end
end end

View file

@ -0,0 +1,25 @@
require 'helper'
module SQLite3
class TestDeprecated < Test::Unit::TestCase
def setup
@db = SQLite3::Database.new(':memory:')
end
def test_query_with_many_bind_params
assert_equal [[nil, 1]], @db.query("select ?, ?", nil, 1).to_a
end
def test_query_with_nil_bind_params
assert_equal [['foo']], @db.query("select 'foo'", nil).to_a
end
def test_execute_with_many_bind_params
assert_equal [[nil, 1]], @db.execute("select ?, ?", nil, 1)
end
def test_execute_with_nil_bind_params
assert_equal [['foo']], @db.execute("select 'foo'", nil)
end
end
end

View file

@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
require 'helper'
module SQLite3
class TestEncoding < Test::Unit::TestCase
def setup
@db = SQLite3::Database.new(':memory:')
@create = "create table ex(id int, data string)"
@insert = "insert into ex(id, data) values (?, ?)"
@db.execute(@create);
end
def test_default_internal_is_honored
before_enc = Encoding.default_internal
str = "壁に耳あり、障子に目あり"
stmt = @db.prepare('insert into ex(data) values (?)')
stmt.bind_param 1, str
stmt.step
Encoding.default_internal = 'EUC-JP'
string = @db.execute('select data from ex').first.first
assert_equal Encoding.default_internal, string.encoding
assert_equal str.encode('EUC-JP'), string
assert_equal str, string.encode(str.encoding)
ensure
Encoding.default_internal = before_enc
end
def test_blob_is_binary
str = "猫舌"
@db.execute('create table foo(data text)')
stmt = @db.prepare('insert into foo(data) values (?)')
stmt.bind_param(1, SQLite3::Blob.new(str))
stmt.step
string = @db.execute('select data from foo').first.first
assert_equal Encoding.find('ASCII-8BIT'), string.encoding
assert_equal str, string.force_encoding('UTF-8')
end
def test_blob_is_ascii8bit
str = "猫舌"
@db.execute('create table foo(data text)')
stmt = @db.prepare('insert into foo(data) values (?)')
stmt.bind_param(1, str.dup.force_encoding("ASCII-8BIT"))
stmt.step
string = @db.execute('select data from foo').first.first
assert_equal Encoding.find('ASCII-8BIT'), string.encoding
assert_equal str, string.force_encoding('UTF-8')
end
def test_blob_with_eucjp
str = "猫舌".encode("EUC-JP")
@db.execute('create table foo(data text)')
stmt = @db.prepare('insert into foo(data) values (?)')
stmt.bind_param(1, SQLite3::Blob.new(str))
stmt.step
string = @db.execute('select data from foo').first.first
assert_equal Encoding.find('ASCII-8BIT'), string.encoding
assert_equal str, string.force_encoding('EUC-JP')
end
def test_db_with_eucjp
db = SQLite3::Database.new(':memory:'.encode('EUC-JP'))
assert_equal(Encoding.find('UTF-8'), db.encoding)
end
def test_db_with_utf16
db = SQLite3::Database.new(':memory:'.encode('UTF-16LE'))
assert_equal(Encoding.find('UTF-16LE'), db.encoding)
end
def test_statement_eucjp
str = "猫舌"
@db.execute("insert into ex(data) values ('#{str}')".encode('EUC-JP'))
row = @db.execute("select data from ex")
assert_equal @db.encoding, row.first.first.encoding
assert_equal str, row.first.first
end
def test_statement_utf8
str = "猫舌"
@db.execute("insert into ex(data) values ('#{str}')")
row = @db.execute("select data from ex")
assert_equal @db.encoding, row.first.first.encoding
assert_equal str, row.first.first
end
def test_encoding
assert_equal Encoding.find("UTF-8"), @db.encoding
end
def test_utf_8
str = "猫舌"
@db.execute(@insert, 10, str)
row = @db.execute("select data from ex")
assert_equal @db.encoding, row.first.first.encoding
assert_equal str, row.first.first
end
def test_euc_jp
str = "猫舌".encode('EUC-JP')
@db.execute(@insert, 10, str)
row = @db.execute("select data from ex")
assert_equal @db.encoding, row.first.first.encoding
assert_equal str.encode('UTF-8'), row.first.first
end
end if RUBY_VERSION >= '1.9.1'
end

View file

@ -1,17 +0,0 @@
require File.join(File.dirname(__FILE__), 'helper')
class TC_Errors < Test::Unit::TestCase
(1..26).each do |code|
define_method( "test_error_code_%02d" % code ) do
db = stub('database', :errmsg => 'message')
begin
SQLite3::Error.check( code, db )
rescue SQLite3::Exception => e
assert_instance_of SQLite3::EXCEPTIONS[code], e
assert_equal code, e.code
assert_equal code, e.class.code
assert_equal "message", e.message
end
end
end
end

View file

@ -1,8 +1,8 @@
require File.join(File.dirname(__FILE__), 'helper') require 'helper'
class TC_Database_Integration < Test::Unit::TestCase class TC_Database_Integration < Test::Unit::TestCase
def setup def setup
@db = SQLite3::Database.new( "test.db" ) @db = SQLite3::Database.new(":memory:")
@db.transaction do @db.transaction do
@db.execute "create table foo ( a integer primary key, b text )" @db.execute "create table foo ( a integer primary key, b text )"
@db.execute "insert into foo ( b ) values ( 'foo' )" @db.execute "insert into foo ( b ) values ( 'foo' )"
@ -13,7 +13,6 @@ class TC_Database_Integration < Test::Unit::TestCase
def teardown def teardown
@db.close @db.close
File.delete( "test.db" )
end end
def test_table_info_with_type_translation_active def test_table_info_with_type_translation_active
@ -25,9 +24,9 @@ class TC_Database_Integration < Test::Unit::TestCase
@db.transaction do @db.transaction do
@db.execute "create table defaults_test ( a string default NULL, b string default 'Hello' )" @db.execute "create table defaults_test ( a string default NULL, b string default 'Hello' )"
data = @db.table_info( "defaults_test" ) data = @db.table_info( "defaults_test" )
assert_equal({"name" => "a", "type" => "string", "dflt_value" => nil, "notnull" => "0", "cid" => "0", "pk" => "0"}, assert_equal({"name" => "a", "type" => "string", "dflt_value" => nil, "notnull" => 0, "cid" => 0, "pk" => 0},
data[0]) data[0])
assert_equal({"name" => "b", "type" => "string", "dflt_value" => "Hello", "notnull" => "0", "cid" => "1", "pk" => "0"}, assert_equal({"name" => "b", "type" => "string", "dflt_value" => "Hello", "notnull" => 0, "cid" => 1, "pk" => 0},
data[1]) data[1])
end end
end end
@ -36,9 +35,9 @@ class TC_Database_Integration < Test::Unit::TestCase
@db.transaction do @db.transaction do
@db.execute "create table no_defaults_test ( a integer default 1, b integer )" @db.execute "create table no_defaults_test ( a integer default 1, b integer )"
data = @db.table_info( "no_defaults_test" ) data = @db.table_info( "no_defaults_test" )
assert_equal({"name" => "a", "type" => "integer", "dflt_value" => "1", "notnull" => "0", "cid" => "0", "pk" => "0"}, assert_equal({"name" => "a", "type" => "integer", "dflt_value" => "1", "notnull" => 0, "cid" => 0, "pk" => 0},
data[0]) data[0])
assert_equal({"name" => "b", "type" => "integer", "dflt_value" => nil, "notnull" => "0", "cid" => "1", "pk" => "0"}, assert_equal({"name" => "b", "type" => "integer", "dflt_value" => nil, "notnull" => 0, "cid" => 1, "pk" => 0},
data[1]) data[1])
end end
end end
@ -50,21 +49,25 @@ class TC_Database_Integration < Test::Unit::TestCase
assert @db.complete?( "select * from foo;" ) assert @db.complete?( "select * from foo;" )
end end
def test_complete_fail_utf16 # FIXME: do people really need UTF16 sql statements?
assert !@db.complete?( "select * from foo".to_utf16(false), true ) #def test_complete_fail_utf16
end # assert !@db.complete?( "select * from foo".to_utf16(false), true )
#end
def test_complete_success_utf16 # FIXME: do people really need UTF16 sql statements?
assert @db.complete?( "select * from foo;".to_utf16(true), true ) #def test_complete_success_utf16
end # assert @db.complete?( "select * from foo;".to_utf16(true), true )
#end
def test_errmsg def test_errmsg
assert_equal "not an error", @db.errmsg assert_equal "not an error", @db.errmsg
end end
def test_errmsg_utf16 # FIXME: do people really need UTF16 error messages?
assert_equal "not an error".to_utf16, @db.errmsg(true) #def test_errmsg_utf16
end # msg = Iconv.conv('UTF-16', 'UTF-8', 'not an error')
# assert_equal msg, @db.errmsg(true)
#end
def test_errcode def test_errcode
assert_equal 0, @db.errcode assert_equal 0, @db.errcode
@ -72,26 +75,26 @@ class TC_Database_Integration < Test::Unit::TestCase
def test_trace def test_trace
result = nil result = nil
@db.trace( "data" ) { |data,sql| result = [ data, sql ]; 0 } @db.trace { |sql| result = sql }
@db.execute "select * from foo" @db.execute "select * from foo"
assert_equal ["data","select * from foo"], result assert_equal "select * from foo", result
end end
def test_authorizer_okay def test_authorizer_okay
@db.authorizer( "data" ) { |data,type,a,b,c,d| 0 } @db.authorizer { |type,a,b,c,d| 0 }
rows = @db.execute "select * from foo" rows = @db.execute "select * from foo"
assert_equal 3, rows.length assert_equal 3, rows.length
end end
def test_authorizer_error def test_authorizer_error
@db.authorizer( "data" ) { |data,type,a,b,c,d| 1 } @db.authorizer { |type,a,b,c,d| 1 }
assert_raise( SQLite3::AuthorizationException ) do assert_raise( SQLite3::AuthorizationException ) do
@db.execute "select * from foo" @db.execute "select * from foo"
end end
end end
def test_authorizer_silent def test_authorizer_silent
@db.authorizer( "data" ) { |data,type,a,b,c,d| 2 } @db.authorizer { |type,a,b,c,d| 2 }
rows = @db.execute "select * from foo" rows = @db.execute "select * from foo"
assert rows.empty? assert rows.empty?
end end
@ -184,7 +187,7 @@ class TC_Database_Integration < Test::Unit::TestCase
def test_execute2_no_block_no_bind_no_match def test_execute2_no_block_no_bind_no_match
columns, *rows = @db.execute2( "select * from foo where a > 100" ) columns, *rows = @db.execute2( "select * from foo where a > 100" )
assert rows.empty? assert rows.empty?
assert [ "a", "b" ], columns assert_equal [ "a", "b" ], columns
end end
def test_execute2_with_block_no_bind_no_match def test_execute2_with_block_no_bind_no_match
@ -199,13 +202,13 @@ class TC_Database_Integration < Test::Unit::TestCase
def test_execute2_no_block_with_bind_no_match def test_execute2_no_block_with_bind_no_match
columns, *rows = @db.execute2( "select * from foo where a > ?", 100 ) columns, *rows = @db.execute2( "select * from foo where a > ?", 100 )
assert rows.empty? assert rows.empty?
assert [ "a", "b" ], columns assert_equal [ "a", "b" ], columns
end end
def test_execute2_with_block_with_bind_no_match def test_execute2_with_block_with_bind_no_match
called = 0 called = 0
@db.execute2( "select * from foo where a > ?", 100 ) do |row| @db.execute2( "select * from foo where a > ?", 100 ) do |row|
assert [ "a", "b" ], row unless called == 0 assert_equal [ "a", "b" ], row unless called == 0
called += 1 called += 1
end end
assert_equal 1, called assert_equal 1, called
@ -214,13 +217,13 @@ class TC_Database_Integration < Test::Unit::TestCase
def test_execute2_no_block_no_bind_with_match def test_execute2_no_block_no_bind_with_match
columns, *rows = @db.execute2( "select * from foo where a = 1" ) columns, *rows = @db.execute2( "select * from foo where a = 1" )
assert_equal 1, rows.length assert_equal 1, rows.length
assert [ "a", "b" ], columns assert_equal [ "a", "b" ], columns
end end
def test_execute2_with_block_no_bind_with_match def test_execute2_with_block_no_bind_with_match
called = 0 called = 0
@db.execute2( "select * from foo where a = 1" ) do |row| @db.execute2( "select * from foo where a = 1" ) do |row|
assert [ "a", "b" ], row unless called == 0 assert_equal [ 1, "foo" ], row unless called == 0
called += 1 called += 1
end end
assert_equal 2, called assert_equal 2, called
@ -229,7 +232,7 @@ class TC_Database_Integration < Test::Unit::TestCase
def test_execute2_no_block_with_bind_with_match def test_execute2_no_block_with_bind_with_match
columns, *rows = @db.execute2( "select * from foo where a = ?", 1 ) columns, *rows = @db.execute2( "select * from foo where a = ?", 1 )
assert_equal 1, rows.length assert_equal 1, rows.length
assert [ "a", "b" ], columns assert_equal [ "a", "b" ], columns
end end
def test_execute2_with_block_with_bind_with_match def test_execute2_with_block_with_bind_with_match
@ -265,7 +268,7 @@ class TC_Database_Integration < Test::Unit::TestCase
insert into bar values ( 'seven', 8, ? ); insert into bar values ( 'seven', 8, ? );
SQL SQL
rows = @db.execute( "select * from bar" ).map { |a,b,c| c } rows = @db.execute( "select * from bar" ).map { |a,b,c| c }
assert_equal %w{1 1 1}, rows assert_equal [1, 1, 1], rows
end end
def test_query_no_block_no_bind_no_match def test_query_no_block_no_bind_no_match
@ -339,7 +342,7 @@ class TC_Database_Integration < Test::Unit::TestCase
def test_get_first_row_no_bind_with_match def test_get_first_row_no_bind_with_match
result = @db.get_first_row( "select * from foo where a=1" ) result = @db.get_first_row( "select * from foo where a=1" )
assert_equal [ "1", "foo" ], result assert_equal [ 1, "foo" ], result
end end
def test_get_first_row_with_bind_no_match def test_get_first_row_with_bind_no_match
@ -349,7 +352,7 @@ class TC_Database_Integration < Test::Unit::TestCase
def test_get_first_row_with_bind_with_match def test_get_first_row_with_bind_with_match
result = @db.get_first_row( "select * from foo where a=?", 1 ) result = @db.get_first_row( "select * from foo where a=?", 1 )
assert_equal [ "1", "foo" ], result assert_equal [ 1, "foo" ], result
end end
def test_get_first_value_no_bind_no_match def test_get_first_value_no_bind_no_match
@ -458,7 +461,7 @@ class TC_Database_Integration < Test::Unit::TestCase
func.result = x func.result = x
end end
assert_raise( SQLite3::SQLException ) do assert_raise( SQLite3::InterruptException ) do
@db.execute "select abort(a) from foo" @db.execute "select abort(a) from foo"
end end
end end
@ -483,7 +486,7 @@ class TC_Database_Integration < Test::Unit::TestCase
@db.create_aggregate( "accumulate", 1, step, final ) @db.create_aggregate( "accumulate", 1, step, final )
value = @db.get_first_value( "select accumulate(a) from foo" ) value = @db.get_first_value( "select accumulate(a) from foo" )
assert_equal "6", value assert_equal 6, value
end end
def test_create_aggregate_with_block def test_create_aggregate_with_block
@ -497,7 +500,7 @@ class TC_Database_Integration < Test::Unit::TestCase
end end
value = @db.get_first_value( "select accumulate(a) from foo" ) value = @db.get_first_value( "select accumulate(a) from foo" )
assert_equal "6", value assert_equal 6, value
end end
def test_create_aggregate_with_no_data def test_create_aggregate_with_no_data
@ -512,7 +515,7 @@ class TC_Database_Integration < Test::Unit::TestCase
value = @db.get_first_value( value = @db.get_first_value(
"select accumulate(a) from foo where a = 100" ) "select accumulate(a) from foo where a = 100" )
assert_equal "0", value assert_equal 0, value
end end
def test_create_aggregate_handler def test_create_aggregate_handler
@ -531,7 +534,7 @@ class TC_Database_Integration < Test::Unit::TestCase
@db.create_aggregate_handler( handler ) @db.create_aggregate_handler( handler )
value = @db.get_first_value( "select multiply(a) from foo" ) value = @db.get_first_value( "select multiply(a) from foo" )
assert_equal "6", value assert_equal 6, value
end end
def test_bind_array_parameter def test_bind_array_parameter

View file

@ -1,4 +1,4 @@
require File.join(File.dirname(__FILE__), 'helper') require 'helper'
class TC_OpenClose < Test::Unit::TestCase class TC_OpenClose < Test::Unit::TestCase
def test_create_close def test_create_close

View file

@ -1,11 +1,11 @@
require File.join(File.dirname(__FILE__), 'helper') require 'helper'
require 'thread' require 'thread'
require 'benchmark' require 'benchmark'
class TC_Integration_Pending < Test::Unit::TestCase class TC_Integration_Pending < Test::Unit::TestCase
def setup def setup
@db = SQLite3::Database.new( "test.db" ) @db = SQLite3::Database.new("test.db")
@db.transaction do @db.transaction do
@db.execute "create table foo ( a integer primary key, b text )" @db.execute "create table foo ( a integer primary key, b text )"
@db.execute "insert into foo ( b ) values ( 'foo' )" @db.execute "insert into foo ( b ) values ( 'foo' )"
@ -65,8 +65,9 @@ class TC_Integration_Pending < Test::Unit::TestCase
db2.close if db2 db2.close if db2
end end
end end
sleep 1
@db.busy_handler do |data, count| @db.busy_handler do |count|
handler_call_count += 1 handler_call_count += 1
false false
end end
@ -97,6 +98,7 @@ class TC_Integration_Pending < Test::Unit::TestCase
end end
end end
sleep 1
time = Benchmark.measure do time = Benchmark.measure do
assert_raise( SQLite3::BusyException ) do assert_raise( SQLite3::BusyException ) do
@db.execute "insert into foo (b) values ( 'from 2' )" @db.execute "insert into foo (b) values ( 'from 2' )"
@ -108,4 +110,4 @@ class TC_Integration_Pending < Test::Unit::TestCase
assert time.real*1000 >= 1000 assert time.real*1000 >= 1000
end end
end end

View file

@ -1,8 +1,8 @@
require File.join(File.dirname(__FILE__), 'helper') require 'helper'
class TC_ResultSet < Test::Unit::TestCase class TC_ResultSet < Test::Unit::TestCase
def setup def setup
@db = SQLite3::Database.new( "test.db" ) @db = SQLite3::Database.new(":memory:")
@db.transaction do @db.transaction do
@db.execute "create table foo ( a integer primary key, b text )" @db.execute "create table foo ( a integer primary key, b text )"
@db.execute "insert into foo ( b ) values ( 'foo' )" @db.execute "insert into foo ( b ) values ( 'foo' )"
@ -16,7 +16,6 @@ class TC_ResultSet < Test::Unit::TestCase
def teardown def teardown
@stmt.close @stmt.close
@db.close @db.close
File.delete( "test.db" )
end end
def test_reset_unused def test_reset_unused
@ -56,7 +55,7 @@ class TC_ResultSet < Test::Unit::TestCase
def test_next_no_type_translation_no_hash def test_next_no_type_translation_no_hash
@result.reset( 1 ) @result.reset( 1 )
assert_equal [ "1", "foo" ], @result.next assert_equal [ 1, "foo" ], @result.next
end end
def test_next_type_translation def test_next_type_translation
@ -68,10 +67,22 @@ class TC_ResultSet < Test::Unit::TestCase
def test_next_type_translation_with_untyped_column def test_next_type_translation_with_untyped_column
@db.type_translation = true @db.type_translation = true
@db.query( "select count(*) from foo" ) do |result| @db.query( "select count(*) from foo" ) do |result|
assert_equal ["3"], result.next assert_equal [3], result.next
end end
end end
def test_type_translation_execute
@db.type_translation = true
@db.execute "create table bar ( a integer, b america )"
@db.execute "insert into bar (a, b) values (NULL, '1974-07-25 14:39:00')"
@db.translator.add_translator('america') do |type, thing|
'america'
end
assert_equal [[nil, 'america']], @db.execute("select * from bar")
end
def test_type_translation_with_null_column def test_type_translation_with_null_column
@db.type_translation = true @db.type_translation = true
@db.execute "create table bar ( a integer, b time, c string )" @db.execute "create table bar ( a integer, b time, c string )"
@ -98,10 +109,23 @@ class TC_ResultSet < Test::Unit::TestCase
end end
end end
def test_real_translation
@db.type_translation = true
@db.execute('create table foo_real(a real)')
@db.execute('insert into foo_real values (42)' )
@db.query('select a, sum(a), typeof(a), typeof(sum(a)) from foo_real') do |result|
result = result.next
assert result[0].is_a?(Float)
assert result[1].is_a?(Float)
assert result[2].is_a?(String)
assert result[3].is_a?(String)
end
end
def test_next_results_as_hash def test_next_results_as_hash
@db.results_as_hash = true @db.results_as_hash = true
@result.reset( 1 ) @result.reset( 1 )
assert_equal( { "a" => "1", "b" => "foo", 0 => "1", 1 => "foo" }, assert_equal( { "a" => 1, "b" => "foo", 0 => 1, 1 => "foo" },
@result.next ) @result.next )
end end
@ -110,7 +134,7 @@ class TC_ResultSet < Test::Unit::TestCase
@result.reset( 1 ) @result.reset( 1 )
row = @result.next row = @result.next
row.each do |_, v| row.each do |_, v|
assert_equal true, v.tainted? assert(v.tainted?) if String === v
end end
end end
@ -118,7 +142,7 @@ class TC_ResultSet < Test::Unit::TestCase
@result.reset( 1 ) @result.reset( 1 )
row = @result.next row = @result.next
row.each do |v| row.each do |v|
assert_equal true, v.tainted? assert(v.tainted?) if String === v
end end
end end

View file

@ -2,7 +2,7 @@ require File.join(File.dirname(__FILE__), 'helper')
class TC_Statement < Test::Unit::TestCase class TC_Statement < Test::Unit::TestCase
def setup def setup
@db = SQLite3::Database.new( "test.db" ) @db = SQLite3::Database.new(":memory:")
@db.transaction do @db.transaction do
@db.execute "create table foo ( a integer primary key, b text )" @db.execute "create table foo ( a integer primary key, b text )"
@db.execute "insert into foo ( b ) values ( 'foo' )" @db.execute "insert into foo ( b ) values ( 'foo' )"
@ -15,7 +15,6 @@ class TC_Statement < Test::Unit::TestCase
def teardown def teardown
@stmt.close @stmt.close
@db.close @db.close
File.delete( "test.db" )
end end
def test_remainder_empty def test_remainder_empty

View file

@ -0,0 +1,9 @@
require 'helper'
module SQLite3
class TestSQLite3 < Test::Unit::TestCase
def test_libversion
assert_not_nil SQLite3.libversion
end
end
end

View file

@ -0,0 +1,207 @@
require 'helper'
module SQLite3
class TestStatement < Test::Unit::TestCase
def setup
@db = SQLite3::Database.new(':memory:')
@stmt = SQLite3::Statement.new(@db, "select 'foo'")
end
###
# This method may not exist depending on how sqlite3 was compiled
def test_database_name
@db.execute('create table foo(text BLOB)')
@db.execute('insert into foo(text) values (?)',SQLite3::Blob.new('hello'))
stmt = @db.prepare('select text from foo')
if stmt.respond_to?(:database_name)
assert_equal 'main', stmt.database_name(0)
end
end
def test_prepare_blob
@db.execute('create table foo(text BLOB)')
stmt = @db.prepare('insert into foo(text) values (?)')
stmt.bind_param(1, SQLite3::Blob.new('hello'))
stmt.step
stmt.close
end
def test_select_blob
@db.execute('create table foo(text BLOB)')
@db.execute('insert into foo(text) values (?)',SQLite3::Blob.new('hello'))
assert_equal 'hello', @db.execute('select * from foo').first.first
end
def test_new
assert @stmt
end
def test_new_closed_handle
@db = SQLite3::Database.new(':memory:')
@db.close
assert_raises(ArgumentError) do
SQLite3::Statement.new(@db, 'select "foo"')
end
end
def test_new_with_remainder
stmt = SQLite3::Statement.new(@db, "select 'foo';bar")
assert_equal 'bar', stmt.remainder
end
def test_empty_remainder
assert_equal '', @stmt.remainder
end
def test_close
@stmt.close
assert @stmt.closed?
end
def test_double_close
@stmt.close
assert_raises(SQLite3::Exception) do
@stmt.close
end
end
def test_bind_param_string
stmt = SQLite3::Statement.new(@db, "select ?")
stmt.bind_param(1, "hello")
result = nil
stmt.each { |x| result = x }
assert_equal ['hello'], result
end
def test_bind_param_int
stmt = SQLite3::Statement.new(@db, "select ?")
stmt.bind_param(1, 10)
result = nil
stmt.each { |x| result = x }
assert_equal [10], result
end
def test_bind_nil
stmt = SQLite3::Statement.new(@db, "select ?")
stmt.bind_param(1, nil)
result = nil
stmt.each { |x| result = x }
assert_equal [nil], result
end
def test_bind_blobs
end
def test_bind_64
stmt = SQLite3::Statement.new(@db, "select ?")
stmt.bind_param(1, 2 ** 31)
result = nil
stmt.each { |x| result = x }
assert_equal [2 ** 31], result
end
def test_bind_double
stmt = SQLite3::Statement.new(@db, "select ?")
stmt.bind_param(1, 2.2)
result = nil
stmt.each { |x| result = x }
assert_equal [2.2], result
end
def test_named_bind
stmt = SQLite3::Statement.new(@db, "select :foo")
stmt.bind_param(':foo', 'hello')
result = nil
stmt.each { |x| result = x }
assert_equal ['hello'], result
end
def test_named_bind_no_colon
stmt = SQLite3::Statement.new(@db, "select :foo")
stmt.bind_param('foo', 'hello')
result = nil
stmt.each { |x| result = x }
assert_equal ['hello'], result
end
def test_named_bind_symbol
stmt = SQLite3::Statement.new(@db, "select :foo")
stmt.bind_param(:foo, 'hello')
result = nil
stmt.each { |x| result = x }
assert_equal ['hello'], result
end
def test_named_bind_not_found
stmt = SQLite3::Statement.new(@db, "select :foo")
assert_raises(SQLite3::Exception) do
stmt.bind_param('bar', 'hello')
end
end
def test_each
r = nil
@stmt.each do |row|
r = row
end
assert_equal(['foo'], r)
end
def test_reset!
r = []
@stmt.each { |row| r << row }
@stmt.reset!
@stmt.each { |row| r << row }
assert_equal [['foo'], ['foo']], r
end
def test_step
r = @stmt.step
assert_equal ['foo'], r
end
def test_tainted
r = @stmt.step
assert r.first.tainted?
end
def test_step_twice
assert_not_nil @stmt.step
assert !@stmt.done?
assert_nil @stmt.step
assert @stmt.done?
@stmt.reset!
assert !@stmt.done?
end
def test_step_never_moves_past_done
10.times { @stmt.step }
@stmt.done?
end
def test_column_count
assert_equal 1, @stmt.column_count
end
def test_column_name
assert_equal "'foo'", @stmt.column_name(0)
assert_equal nil, @stmt.column_name(10)
end
def test_bind_parameter_count
stmt = SQLite3::Statement.new(@db, "select ?, ?, ?")
assert_equal 3, stmt.bind_parameter_count
end
def test_execute_with_varargs
stmt = @db.prepare('select ?, ?')
assert_equal [[nil, nil]], stmt.execute(nil, nil).to_a
end
def test_execute_with_hash
stmt = @db.prepare('select :n, :h')
assert_equal [[10, nil]], stmt.execute('n' => 10, 'h' => nil).to_a
end
end
end