sqlite3-ruby 1.2.5-x86-mswin32 → 1.3.0.beta.1-x86-mswin32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/API_CHANGES.rdoc +48 -0
- data/{History.txt → CHANGELOG.rdoc} +24 -0
- data/Manifest.txt +14 -14
- data/{README.txt → README.rdoc} +1 -6
- data/Rakefile +8 -3
- data/ext/sqlite3/database.c +687 -0
- data/ext/sqlite3/database.h +15 -0
- data/ext/sqlite3/exception.c +94 -0
- data/ext/sqlite3/exception.h +8 -0
- data/ext/sqlite3/extconf.rb +26 -0
- data/ext/sqlite3/sqlite3.c +33 -0
- data/ext/sqlite3/sqlite3_ruby.h +43 -0
- data/ext/sqlite3/statement.c +412 -0
- data/ext/sqlite3/statement.h +16 -0
- data/lib/sqlite3.rb +9 -0
- data/lib/sqlite3/1.8/sqlite3_native.so +0 -0
- data/lib/sqlite3/1.9/sqlite3_native.so +0 -0
- data/lib/sqlite3/database.rb +94 -302
- data/lib/sqlite3/errors.rb +0 -24
- data/lib/sqlite3/pragmas.rb +16 -7
- data/lib/sqlite3/resultset.rb +25 -81
- data/lib/sqlite3/statement.rb +22 -107
- data/lib/sqlite3/version.rb +4 -4
- data/setup.rb +2 -2
- data/tasks/native.rake +13 -17
- data/tasks/vendor_sqlite3.rake +10 -7
- data/test/helper.rb +1 -65
- data/test/test_database.rb +239 -189
- data/test/test_encoding.rb +115 -0
- data/test/test_integration.rb +38 -35
- data/test/test_integration_open_close.rb +1 -1
- data/test/test_integration_pending.rb +6 -4
- data/test/test_integration_resultset.rb +20 -8
- data/test/test_integration_statement.rb +1 -2
- data/test/test_sqlite3.rb +9 -0
- data/test/test_statement.rb +193 -0
- metadata +84 -49
- data/ext/sqlite3_api/extconf.rb +0 -10
- data/ext/sqlite3_api/sqlite3_api.i +0 -362
- data/ext/sqlite3_api/sqlite3_api_wrap.c +0 -5018
- data/lib/1.8/sqlite3_api.so +0 -0
- data/lib/1.9/sqlite3_api.so +0 -0
- data/lib/sqlite3/driver/dl/api.rb +0 -152
- data/lib/sqlite3/driver/dl/driver.rb +0 -307
- data/lib/sqlite3/driver/native/driver.rb +0 -219
- data/tasks/benchmark.rake +0 -9
- data/tasks/gem.rake +0 -32
- data/test/bm.rb +0 -140
- data/test/driver/dl/tc_driver.rb +0 -292
- data/test/native-vs-dl.rb +0 -126
- data/test/test_errors.rb +0 -17
@@ -0,0 +1,15 @@
|
|
1
|
+
#ifndef SQLITE3_DATABASE_RUBY
|
2
|
+
#define SQLITE3_DATABASE_RUBY
|
3
|
+
|
4
|
+
#include <sqlite3_ruby.h>
|
5
|
+
|
6
|
+
struct _sqlite3Ruby {
|
7
|
+
sqlite3 *db;
|
8
|
+
};
|
9
|
+
|
10
|
+
typedef struct _sqlite3Ruby sqlite3Ruby;
|
11
|
+
typedef sqlite3Ruby * sqlite3RubyPtr;
|
12
|
+
|
13
|
+
void init_sqlite3_database();
|
14
|
+
|
15
|
+
#endif
|
@@ -0,0 +1,94 @@
|
|
1
|
+
#include <sqlite3_ruby.h>
|
2
|
+
|
3
|
+
void rb_sqlite3_raise(sqlite3 * db, int status)
|
4
|
+
{
|
5
|
+
VALUE klass = Qnil;
|
6
|
+
|
7
|
+
switch(status) {
|
8
|
+
case SQLITE_OK:
|
9
|
+
return;
|
10
|
+
break;
|
11
|
+
case SQLITE_ERROR:
|
12
|
+
klass = rb_path2class("SQLite3::SQLException");
|
13
|
+
break;
|
14
|
+
case SQLITE_INTERNAL:
|
15
|
+
klass = rb_path2class("SQLite3::InternalException");
|
16
|
+
break;
|
17
|
+
case SQLITE_PERM:
|
18
|
+
klass = rb_path2class("SQLite3::PermissionException");
|
19
|
+
break;
|
20
|
+
case SQLITE_ABORT:
|
21
|
+
klass = rb_path2class("SQLite3::AbortException");
|
22
|
+
break;
|
23
|
+
case SQLITE_BUSY:
|
24
|
+
klass = rb_path2class("SQLite3::BusyException");
|
25
|
+
break;
|
26
|
+
case SQLITE_LOCKED:
|
27
|
+
klass = rb_path2class("SQLite3::LockedException");
|
28
|
+
break;
|
29
|
+
case SQLITE_NOMEM:
|
30
|
+
klass = rb_path2class("SQLite3::MemoryException");
|
31
|
+
break;
|
32
|
+
case SQLITE_READONLY:
|
33
|
+
klass = rb_path2class("SQLite3::ReadOnlyException");
|
34
|
+
break;
|
35
|
+
case SQLITE_INTERRUPT:
|
36
|
+
klass = rb_path2class("SQLite3::InterruptException");
|
37
|
+
break;
|
38
|
+
case SQLITE_IOERR:
|
39
|
+
klass = rb_path2class("SQLite3::IOException");
|
40
|
+
break;
|
41
|
+
case SQLITE_CORRUPT:
|
42
|
+
klass = rb_path2class("SQLite3::CorruptException");
|
43
|
+
break;
|
44
|
+
case SQLITE_NOTFOUND:
|
45
|
+
klass = rb_path2class("SQLite3::NotFoundException");
|
46
|
+
break;
|
47
|
+
case SQLITE_FULL:
|
48
|
+
klass = rb_path2class("SQLite3::FullException");
|
49
|
+
break;
|
50
|
+
case SQLITE_CANTOPEN:
|
51
|
+
klass = rb_path2class("SQLite3::CantOpenException");
|
52
|
+
break;
|
53
|
+
case SQLITE_PROTOCOL:
|
54
|
+
klass = rb_path2class("SQLite3::ProtocolException");
|
55
|
+
break;
|
56
|
+
case SQLITE_EMPTY:
|
57
|
+
klass = rb_path2class("SQLite3::EmptyException");
|
58
|
+
break;
|
59
|
+
case SQLITE_SCHEMA:
|
60
|
+
klass = rb_path2class("SQLite3::SchemaChangedException");
|
61
|
+
break;
|
62
|
+
case SQLITE_TOOBIG:
|
63
|
+
klass = rb_path2class("SQLite3::TooBigException");
|
64
|
+
break;
|
65
|
+
case SQLITE_CONSTRAINT:
|
66
|
+
klass = rb_path2class("SQLite3::ConstraintException");
|
67
|
+
break;
|
68
|
+
case SQLITE_MISMATCH:
|
69
|
+
klass = rb_path2class("SQLite3::MismatchException");
|
70
|
+
break;
|
71
|
+
case SQLITE_MISUSE:
|
72
|
+
klass = rb_path2class("SQLite3::MisuseException");
|
73
|
+
break;
|
74
|
+
case SQLITE_NOLFS:
|
75
|
+
klass = rb_path2class("SQLite3::UnsupportedException");
|
76
|
+
break;
|
77
|
+
case SQLITE_AUTH:
|
78
|
+
klass = rb_path2class("SQLite3::AuthorizationException");
|
79
|
+
break;
|
80
|
+
case SQLITE_FORMAT:
|
81
|
+
klass = rb_path2class("SQLite3::FormatException");
|
82
|
+
break;
|
83
|
+
case SQLITE_RANGE:
|
84
|
+
klass = rb_path2class("SQLite3::RangeException");
|
85
|
+
break;
|
86
|
+
case SQLITE_NOTADB:
|
87
|
+
klass = rb_path2class("SQLite3::NotADatabaseException");
|
88
|
+
break;
|
89
|
+
default:
|
90
|
+
klass = rb_eRuntimeError;
|
91
|
+
}
|
92
|
+
|
93
|
+
rb_raise(klass, "%s", sqlite3_errmsg(db));
|
94
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
|
2
|
+
|
3
|
+
require 'mkmf'
|
4
|
+
|
5
|
+
# :stopdoc:
|
6
|
+
|
7
|
+
RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
|
8
|
+
|
9
|
+
$CFLAGS << ' -O3 -Wall -Wcast-qual -Wwrite-strings -Wconversion' <<
|
10
|
+
' -Wmissing-noreturn -Winline'
|
11
|
+
|
12
|
+
sqlite = dir_config 'sqlite3', '/opt/local/include', '/opt/local/lib'
|
13
|
+
|
14
|
+
def asplode missing
|
15
|
+
abort "#{missing} is missing. Try 'port install sqlite3 +universal' " +
|
16
|
+
"or 'yum install sqlite3-devel'"
|
17
|
+
end
|
18
|
+
|
19
|
+
asplode('sqlite3.h') unless find_header 'sqlite3.h'
|
20
|
+
asplode('sqlite3') unless find_library 'sqlite3', 'sqlite3_libversion_number'
|
21
|
+
|
22
|
+
# Functions defined in 1.9 but not 1.8
|
23
|
+
have_func('rb_proc_arity')
|
24
|
+
have_func('rb_obj_method_arity')
|
25
|
+
|
26
|
+
create_makefile('sqlite3/sqlite3_native')
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#include <sqlite3_ruby.h>
|
2
|
+
|
3
|
+
VALUE mSqlite3;
|
4
|
+
VALUE cSqlite3Blob;
|
5
|
+
|
6
|
+
static VALUE libversion(VALUE UNUSED(klass))
|
7
|
+
{
|
8
|
+
return INT2NUM(sqlite3_libversion_number());
|
9
|
+
}
|
10
|
+
|
11
|
+
void Init_sqlite3_native()
|
12
|
+
{
|
13
|
+
/*
|
14
|
+
* SQLite3 is a wrapper around the popular database
|
15
|
+
* sqlite[http://sqlite.org].
|
16
|
+
*
|
17
|
+
* For an example of usage, see SQLite3::Database.
|
18
|
+
*/
|
19
|
+
mSqlite3 = rb_define_module("SQLite3");
|
20
|
+
|
21
|
+
/* A class for differentiating between strings and blobs, when binding them
|
22
|
+
* into statements.
|
23
|
+
*/
|
24
|
+
cSqlite3Blob = rb_define_class_under(mSqlite3, "Blob", rb_cString);
|
25
|
+
|
26
|
+
/* Initialize the sqlite3 library */
|
27
|
+
sqlite3_initialize();
|
28
|
+
|
29
|
+
init_sqlite3_database();
|
30
|
+
init_sqlite3_statement();
|
31
|
+
|
32
|
+
rb_define_singleton_method(mSqlite3, "libversion", libversion, 0);
|
33
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#ifndef SQLITE3_RUBY
|
2
|
+
#define SQLITE3_RUBY
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
|
6
|
+
#ifdef UNUSED
|
7
|
+
#elif defined(__GNUC__)
|
8
|
+
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
|
9
|
+
#elif defined(__LCLINT__)
|
10
|
+
# define UNUSED(x) /*@unused@*/ x
|
11
|
+
#else
|
12
|
+
# define UNUSED(x) x
|
13
|
+
#endif
|
14
|
+
|
15
|
+
#ifndef RBIGNUM_LEN
|
16
|
+
#define RBIGNUM_LEN(x) RBIGNUM(x)->len
|
17
|
+
#endif
|
18
|
+
|
19
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
20
|
+
#include <ruby/encoding.h>
|
21
|
+
|
22
|
+
#define UTF8_P(_obj) (rb_enc_get_index(_obj) == rb_utf8_encindex())
|
23
|
+
#define UTF16_LE_P(_obj) (rb_enc_get_index(_obj) == rb_enc_find_index("UTF-16LE"))
|
24
|
+
#define SQLITE3_UTF8_STR_NEW2(_obj) \
|
25
|
+
(rb_enc_associate_index(rb_str_new2(_obj), rb_utf8_encindex()))
|
26
|
+
|
27
|
+
#else
|
28
|
+
|
29
|
+
#define SQLITE3_UTF8_STR_NEW2(_obj) (rb_str_new2(_obj))
|
30
|
+
|
31
|
+
#endif
|
32
|
+
|
33
|
+
|
34
|
+
#include <sqlite3.h>
|
35
|
+
|
36
|
+
extern VALUE mSqlite3;
|
37
|
+
extern VALUE cSqlite3Blob;
|
38
|
+
|
39
|
+
#include <database.h>
|
40
|
+
#include <statement.h>
|
41
|
+
#include <exception.h>
|
42
|
+
|
43
|
+
#endif
|
@@ -0,0 +1,412 @@
|
|
1
|
+
#include <sqlite3_ruby.h>
|
2
|
+
|
3
|
+
#define REQUIRE_OPEN_STMT(_ctxt) \
|
4
|
+
if(!_ctxt->st) \
|
5
|
+
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed statement");
|
6
|
+
|
7
|
+
VALUE cSqlite3Statement;
|
8
|
+
|
9
|
+
static void deallocate(void * ctx)
|
10
|
+
{
|
11
|
+
sqlite3StmtRubyPtr c = (sqlite3StmtRubyPtr)ctx;
|
12
|
+
xfree(c);
|
13
|
+
}
|
14
|
+
|
15
|
+
static VALUE allocate(VALUE klass)
|
16
|
+
{
|
17
|
+
sqlite3StmtRubyPtr ctx = xcalloc((size_t)1, sizeof(sqlite3StmtRuby));
|
18
|
+
ctx->st = NULL;
|
19
|
+
ctx->done_p = 0;
|
20
|
+
|
21
|
+
return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
|
22
|
+
}
|
23
|
+
|
24
|
+
/* call-seq: SQLite3::Statement.new(db, sql)
|
25
|
+
*
|
26
|
+
* Create a new statement attached to the given Database instance, and which
|
27
|
+
* encapsulates the given SQL text. If the text contains more than one
|
28
|
+
* statement (i.e., separated by semicolons), then the #remainder property
|
29
|
+
* will be set to the trailing text.
|
30
|
+
*/
|
31
|
+
static VALUE initialize(VALUE self, VALUE db, VALUE sql)
|
32
|
+
{
|
33
|
+
sqlite3RubyPtr db_ctx;
|
34
|
+
sqlite3StmtRubyPtr ctx;
|
35
|
+
const char *tail = NULL;
|
36
|
+
int status;
|
37
|
+
|
38
|
+
Data_Get_Struct(db, sqlite3Ruby, db_ctx);
|
39
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
40
|
+
|
41
|
+
if(!db_ctx->db)
|
42
|
+
rb_raise(rb_eArgError, "prepare called on a closed database");
|
43
|
+
|
44
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
45
|
+
if(!UTF8_P(sql)) {
|
46
|
+
VALUE encoding = rb_funcall(db, rb_intern("encoding"), 0);
|
47
|
+
rb_encoding * enc = NIL_P(encoding) ? rb_utf8_encoding() :
|
48
|
+
rb_to_encoding(encoding);
|
49
|
+
sql = rb_str_export_to_enc(sql, enc);
|
50
|
+
}
|
51
|
+
#endif
|
52
|
+
|
53
|
+
status = sqlite3_prepare_v2(
|
54
|
+
db_ctx->db,
|
55
|
+
(const char *)StringValuePtr(sql),
|
56
|
+
(int)RSTRING_LEN(sql),
|
57
|
+
&ctx->st,
|
58
|
+
&tail
|
59
|
+
);
|
60
|
+
|
61
|
+
CHECK(db_ctx->db, status);
|
62
|
+
|
63
|
+
rb_iv_set(self, "@connection", db);
|
64
|
+
rb_iv_set(self, "@remainder", rb_str_new2(tail));
|
65
|
+
rb_iv_set(self, "@columns", Qnil);
|
66
|
+
rb_iv_set(self, "@types", Qnil);
|
67
|
+
|
68
|
+
return self;
|
69
|
+
}
|
70
|
+
|
71
|
+
/* call-seq: stmt.close
|
72
|
+
*
|
73
|
+
* Closes the statement by finalizing the underlying statement
|
74
|
+
* handle. The statement must not be used after being closed.
|
75
|
+
*/
|
76
|
+
static VALUE sqlite3_rb_close(VALUE self)
|
77
|
+
{
|
78
|
+
sqlite3StmtRubyPtr ctx;
|
79
|
+
sqlite3 * db;
|
80
|
+
|
81
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
82
|
+
|
83
|
+
REQUIRE_OPEN_STMT(ctx);
|
84
|
+
|
85
|
+
db = sqlite3_db_handle(ctx->st);
|
86
|
+
CHECK(db, sqlite3_finalize(ctx->st));
|
87
|
+
|
88
|
+
ctx->st = NULL;
|
89
|
+
|
90
|
+
return self;
|
91
|
+
}
|
92
|
+
|
93
|
+
/* call-seq: stmt.closed?
|
94
|
+
*
|
95
|
+
* Returns true if the statement has been closed.
|
96
|
+
*/
|
97
|
+
static VALUE closed_p(VALUE self)
|
98
|
+
{
|
99
|
+
sqlite3StmtRubyPtr ctx;
|
100
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
101
|
+
|
102
|
+
if(!ctx->st) return Qtrue;
|
103
|
+
|
104
|
+
return Qfalse;
|
105
|
+
}
|
106
|
+
|
107
|
+
static VALUE step(VALUE self)
|
108
|
+
{
|
109
|
+
sqlite3StmtRubyPtr ctx;
|
110
|
+
sqlite3_stmt *stmt;
|
111
|
+
int value, length;
|
112
|
+
VALUE list;
|
113
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
114
|
+
rb_encoding * internal_encoding;
|
115
|
+
int enc_index;
|
116
|
+
#endif
|
117
|
+
|
118
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
119
|
+
|
120
|
+
REQUIRE_OPEN_STMT(ctx);
|
121
|
+
|
122
|
+
if(ctx->done_p) return Qnil;
|
123
|
+
|
124
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
125
|
+
{
|
126
|
+
VALUE db = rb_iv_get(self, "@connection");
|
127
|
+
VALUE encoding = rb_funcall(db, rb_intern("encoding"), 0);
|
128
|
+
enc_index = NIL_P(encoding) ? rb_utf8_encindex() : rb_to_encoding_index(encoding);
|
129
|
+
internal_encoding = rb_default_internal_encoding();
|
130
|
+
}
|
131
|
+
#endif
|
132
|
+
|
133
|
+
stmt = ctx->st;
|
134
|
+
|
135
|
+
value = sqlite3_step(stmt);
|
136
|
+
length = sqlite3_column_count(stmt);
|
137
|
+
list = rb_ary_new2((long)length);
|
138
|
+
|
139
|
+
switch(value) {
|
140
|
+
case SQLITE_ROW:
|
141
|
+
{
|
142
|
+
int i;
|
143
|
+
for(i = 0; i < length; i++) {
|
144
|
+
switch(sqlite3_column_type(stmt, i)) {
|
145
|
+
case SQLITE_INTEGER:
|
146
|
+
rb_ary_push(list, LL2NUM(sqlite3_column_int64(stmt, i)));
|
147
|
+
break;
|
148
|
+
case SQLITE_FLOAT:
|
149
|
+
rb_ary_push(list, rb_float_new(sqlite3_column_double(stmt, i)));
|
150
|
+
break;
|
151
|
+
case SQLITE_TEXT:
|
152
|
+
{
|
153
|
+
VALUE str = rb_tainted_str_new(
|
154
|
+
(const char *)sqlite3_column_text(stmt, i),
|
155
|
+
(long)sqlite3_column_bytes(stmt, i)
|
156
|
+
);
|
157
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
158
|
+
rb_enc_associate_index(str, enc_index);
|
159
|
+
if(internal_encoding)
|
160
|
+
str = rb_str_export_to_enc(str, internal_encoding);
|
161
|
+
#endif
|
162
|
+
rb_ary_push(list, str);
|
163
|
+
}
|
164
|
+
break;
|
165
|
+
case SQLITE_BLOB:
|
166
|
+
{
|
167
|
+
VALUE str = rb_tainted_str_new(
|
168
|
+
(const char *)sqlite3_column_blob(stmt, i),
|
169
|
+
(long)sqlite3_column_bytes(stmt, i)
|
170
|
+
);
|
171
|
+
rb_ary_push(list, str);
|
172
|
+
}
|
173
|
+
break;
|
174
|
+
case SQLITE_NULL:
|
175
|
+
rb_ary_push(list, Qnil);
|
176
|
+
break;
|
177
|
+
default:
|
178
|
+
rb_raise(rb_eRuntimeError, "bad type");
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
break;
|
183
|
+
case SQLITE_DONE:
|
184
|
+
ctx->done_p = 1;
|
185
|
+
return Qnil;
|
186
|
+
break;
|
187
|
+
default:
|
188
|
+
CHECK(sqlite3_db_handle(ctx->st), value);
|
189
|
+
}
|
190
|
+
|
191
|
+
return list;
|
192
|
+
}
|
193
|
+
|
194
|
+
/* call-seq: stmt.bind_param(key, value)
|
195
|
+
*
|
196
|
+
* Binds value to the named (or positional) placeholder. If +param+ is a
|
197
|
+
* Fixnum, it is treated as an index for a positional placeholder.
|
198
|
+
* Otherwise it is used as the name of the placeholder to bind to.
|
199
|
+
*
|
200
|
+
* See also #bind_params.
|
201
|
+
*/
|
202
|
+
static VALUE bind_param(VALUE self, VALUE key, VALUE value)
|
203
|
+
{
|
204
|
+
sqlite3StmtRubyPtr ctx;
|
205
|
+
int status;
|
206
|
+
int index;
|
207
|
+
|
208
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
209
|
+
REQUIRE_OPEN_STMT(ctx);
|
210
|
+
|
211
|
+
switch(TYPE(key)) {
|
212
|
+
case T_SYMBOL:
|
213
|
+
key = rb_funcall(key, rb_intern("to_s"), 0);
|
214
|
+
case T_STRING:
|
215
|
+
if(RSTRING_PTR(key)[0] != ':') key = rb_str_plus(rb_str_new2(":"), key);
|
216
|
+
index = sqlite3_bind_parameter_index(ctx->st, StringValuePtr(key));
|
217
|
+
break;
|
218
|
+
default:
|
219
|
+
index = (int)NUM2INT(key);
|
220
|
+
}
|
221
|
+
|
222
|
+
if(index == 0)
|
223
|
+
rb_raise(rb_path2class("SQLite3::Exception"), "no such bind parameter");
|
224
|
+
|
225
|
+
switch(TYPE(value)) {
|
226
|
+
case T_STRING:
|
227
|
+
if(CLASS_OF(value) == cSqlite3Blob
|
228
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
229
|
+
|| rb_enc_get_index(value) == rb_ascii8bit_encindex()
|
230
|
+
#endif
|
231
|
+
) {
|
232
|
+
status = sqlite3_bind_blob(
|
233
|
+
ctx->st,
|
234
|
+
index,
|
235
|
+
(const char *)StringValuePtr(value),
|
236
|
+
(int)RSTRING_LEN(value),
|
237
|
+
SQLITE_TRANSIENT
|
238
|
+
);
|
239
|
+
} else {
|
240
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
241
|
+
if(!UTF8_P(value)) {
|
242
|
+
VALUE db = rb_iv_get(self, "@connection");
|
243
|
+
VALUE encoding = rb_funcall(db, rb_intern("encoding"), 0);
|
244
|
+
rb_encoding * enc = rb_to_encoding(encoding);
|
245
|
+
value = rb_str_export_to_enc(value, enc);
|
246
|
+
}
|
247
|
+
#endif
|
248
|
+
|
249
|
+
status = sqlite3_bind_text(
|
250
|
+
ctx->st,
|
251
|
+
index,
|
252
|
+
(const char *)StringValuePtr(value),
|
253
|
+
(int)RSTRING_LEN(value),
|
254
|
+
SQLITE_TRANSIENT
|
255
|
+
);
|
256
|
+
}
|
257
|
+
break;
|
258
|
+
case T_BIGNUM:
|
259
|
+
#if SIZEOF_LONG < 8
|
260
|
+
if (RBIGNUM_LEN(value) * SIZEOF_BDIGITS <= 8) {
|
261
|
+
status = sqlite3_bind_int64(ctx->st, index, (sqlite3_int64)NUM2LL(value));
|
262
|
+
break;
|
263
|
+
}
|
264
|
+
#endif
|
265
|
+
case T_FLOAT:
|
266
|
+
status = sqlite3_bind_double(ctx->st, index, NUM2DBL(value));
|
267
|
+
break;
|
268
|
+
case T_FIXNUM:
|
269
|
+
status = sqlite3_bind_int64(ctx->st, index, (sqlite3_int64)FIX2LONG(value));
|
270
|
+
break;
|
271
|
+
case T_NIL:
|
272
|
+
status = sqlite3_bind_null(ctx->st, index);
|
273
|
+
break;
|
274
|
+
default:
|
275
|
+
rb_raise(rb_eRuntimeError, "can't prepare %s",
|
276
|
+
rb_class2name(CLASS_OF(value)));
|
277
|
+
break;
|
278
|
+
}
|
279
|
+
|
280
|
+
CHECK(sqlite3_db_handle(ctx->st), status);
|
281
|
+
|
282
|
+
return self;
|
283
|
+
}
|
284
|
+
|
285
|
+
/* call-seq: stmt.reset!
|
286
|
+
*
|
287
|
+
* Resets the statement. This is typically done internally, though it might
|
288
|
+
* occassionally be necessary to manually reset the statement.
|
289
|
+
*/
|
290
|
+
static VALUE reset_bang(VALUE self)
|
291
|
+
{
|
292
|
+
sqlite3StmtRubyPtr ctx;
|
293
|
+
int status;
|
294
|
+
|
295
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
296
|
+
REQUIRE_OPEN_STMT(ctx);
|
297
|
+
|
298
|
+
status = sqlite3_reset(ctx->st);
|
299
|
+
CHECK(sqlite3_db_handle(ctx->st), status);
|
300
|
+
|
301
|
+
ctx->done_p = 0;
|
302
|
+
|
303
|
+
return self;
|
304
|
+
}
|
305
|
+
|
306
|
+
/* call-seq: stmt.done?
|
307
|
+
*
|
308
|
+
* returns true if all rows have been returned.
|
309
|
+
*/
|
310
|
+
static VALUE done_p(VALUE self)
|
311
|
+
{
|
312
|
+
sqlite3StmtRubyPtr ctx;
|
313
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
314
|
+
|
315
|
+
if(ctx->done_p) return Qtrue;
|
316
|
+
return Qfalse;
|
317
|
+
}
|
318
|
+
|
319
|
+
/* call-seq: stmt.column_count
|
320
|
+
*
|
321
|
+
* Returns the number of columns to be returned for this statement
|
322
|
+
*/
|
323
|
+
static VALUE column_count(VALUE self)
|
324
|
+
{
|
325
|
+
sqlite3StmtRubyPtr ctx;
|
326
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
327
|
+
REQUIRE_OPEN_STMT(ctx);
|
328
|
+
|
329
|
+
return INT2NUM((long)sqlite3_column_count(ctx->st));
|
330
|
+
}
|
331
|
+
|
332
|
+
/* call-seq: stmt.column_name(index)
|
333
|
+
*
|
334
|
+
* Get the column name at +index+. 0 based.
|
335
|
+
*/
|
336
|
+
static VALUE column_name(VALUE self, VALUE index)
|
337
|
+
{
|
338
|
+
sqlite3StmtRubyPtr ctx;
|
339
|
+
const char * name;
|
340
|
+
|
341
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
342
|
+
REQUIRE_OPEN_STMT(ctx);
|
343
|
+
|
344
|
+
name = sqlite3_column_name(ctx->st, (int)NUM2INT(index));
|
345
|
+
|
346
|
+
if(name) return rb_str_new2(name);
|
347
|
+
return Qnil;
|
348
|
+
}
|
349
|
+
|
350
|
+
/* call-seq: stmt.column_decltype(index)
|
351
|
+
*
|
352
|
+
* Get the column type at +index+. 0 based.
|
353
|
+
*/
|
354
|
+
static VALUE column_decltype(VALUE self, VALUE index)
|
355
|
+
{
|
356
|
+
sqlite3StmtRubyPtr ctx;
|
357
|
+
const char * name;
|
358
|
+
|
359
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
360
|
+
REQUIRE_OPEN_STMT(ctx);
|
361
|
+
|
362
|
+
name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
|
363
|
+
|
364
|
+
if(name) return rb_str_new2(name);
|
365
|
+
return Qnil;
|
366
|
+
}
|
367
|
+
|
368
|
+
/* call-seq: stmt.bind_parameter_count
|
369
|
+
*
|
370
|
+
* Return the number of bind parameters
|
371
|
+
*/
|
372
|
+
static VALUE bind_parameter_count(VALUE self)
|
373
|
+
{
|
374
|
+
sqlite3StmtRubyPtr ctx;
|
375
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
376
|
+
REQUIRE_OPEN_STMT(ctx);
|
377
|
+
|
378
|
+
return INT2NUM((long)sqlite3_bind_parameter_count(ctx->st));
|
379
|
+
}
|
380
|
+
|
381
|
+
/* call-seq: stmt.database_name(column_index)
|
382
|
+
*
|
383
|
+
* Return the database name for the column at +column_index+
|
384
|
+
*/
|
385
|
+
static VALUE database_name(VALUE self, VALUE index)
|
386
|
+
{
|
387
|
+
sqlite3StmtRubyPtr ctx;
|
388
|
+
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
|
389
|
+
REQUIRE_OPEN_STMT(ctx);
|
390
|
+
|
391
|
+
return SQLITE3_UTF8_STR_NEW2(
|
392
|
+
sqlite3_column_database_name(ctx->st, NUM2INT(index)));
|
393
|
+
}
|
394
|
+
|
395
|
+
void init_sqlite3_statement()
|
396
|
+
{
|
397
|
+
cSqlite3Statement = rb_define_class_under(mSqlite3, "Statement", rb_cObject);
|
398
|
+
|
399
|
+
rb_define_alloc_func(cSqlite3Statement, allocate);
|
400
|
+
rb_define_method(cSqlite3Statement, "initialize", initialize, 2);
|
401
|
+
rb_define_method(cSqlite3Statement, "close", sqlite3_rb_close, 0);
|
402
|
+
rb_define_method(cSqlite3Statement, "closed?", closed_p, 0);
|
403
|
+
rb_define_method(cSqlite3Statement, "bind_param", bind_param, 2);
|
404
|
+
rb_define_method(cSqlite3Statement, "reset!", reset_bang, 0);
|
405
|
+
rb_define_method(cSqlite3Statement, "step", step, 0);
|
406
|
+
rb_define_method(cSqlite3Statement, "done?", done_p, 0);
|
407
|
+
rb_define_method(cSqlite3Statement, "column_count", column_count, 0);
|
408
|
+
rb_define_method(cSqlite3Statement, "column_name", column_name, 1);
|
409
|
+
rb_define_method(cSqlite3Statement, "column_decltype", column_decltype, 1);
|
410
|
+
rb_define_method(cSqlite3Statement, "bind_parameter_count", bind_parameter_count, 0);
|
411
|
+
rb_define_method(cSqlite3Statement, "database_name", database_name, 1);
|
412
|
+
}
|