sqlite3-ruby 1.2.5 → 1.3.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/API_CHANGES.rdoc +48 -0
  2. data/{History.txt → CHANGELOG.rdoc} +24 -0
  3. data/Manifest.txt +14 -14
  4. data/{README.txt → README.rdoc} +1 -6
  5. data/Rakefile +8 -3
  6. data/ext/sqlite3/database.c +687 -0
  7. data/ext/sqlite3/database.h +15 -0
  8. data/ext/sqlite3/exception.c +94 -0
  9. data/ext/sqlite3/exception.h +8 -0
  10. data/ext/sqlite3/extconf.rb +26 -0
  11. data/ext/sqlite3/sqlite3.c +33 -0
  12. data/ext/sqlite3/sqlite3_ruby.h +43 -0
  13. data/ext/sqlite3/statement.c +412 -0
  14. data/ext/sqlite3/statement.h +16 -0
  15. data/lib/sqlite3.rb +9 -0
  16. data/lib/sqlite3/database.rb +94 -302
  17. data/lib/sqlite3/errors.rb +0 -24
  18. data/lib/sqlite3/pragmas.rb +16 -7
  19. data/lib/sqlite3/resultset.rb +25 -81
  20. data/lib/sqlite3/statement.rb +22 -107
  21. data/lib/sqlite3/version.rb +4 -4
  22. data/setup.rb +2 -2
  23. data/tasks/native.rake +13 -17
  24. data/tasks/vendor_sqlite3.rake +10 -7
  25. data/test/helper.rb +1 -65
  26. data/test/test_database.rb +239 -189
  27. data/test/test_encoding.rb +115 -0
  28. data/test/test_integration.rb +38 -35
  29. data/test/test_integration_open_close.rb +1 -1
  30. data/test/test_integration_pending.rb +6 -4
  31. data/test/test_integration_resultset.rb +20 -8
  32. data/test/test_integration_statement.rb +1 -2
  33. data/test/test_sqlite3.rb +9 -0
  34. data/test/test_statement.rb +193 -0
  35. metadata +82 -48
  36. data/ext/sqlite3_api/extconf.rb +0 -10
  37. data/ext/sqlite3_api/sqlite3_api.i +0 -362
  38. data/ext/sqlite3_api/sqlite3_api_wrap.c +0 -5018
  39. data/lib/sqlite3/driver/dl/api.rb +0 -152
  40. data/lib/sqlite3/driver/dl/driver.rb +0 -307
  41. data/lib/sqlite3/driver/native/driver.rb +0 -219
  42. data/tasks/benchmark.rake +0 -9
  43. data/tasks/gem.rake +0 -32
  44. data/test/bm.rb +0 -140
  45. data/test/driver/dl/tc_driver.rb +0 -292
  46. data/test/native-vs-dl.rb +0 -126
  47. data/test/test_errors.rb +0 -17
@@ -0,0 +1,48 @@
1
+ = API Changes
2
+
3
+ * SQLite3::ResultSet used to query the database for the first row, regardless
4
+ of whether the user asked for it or not. I have removed that so that rows
5
+ will not be returned until the user asks for them. This is a subtle but
6
+ sometimes important change in behavior.
7
+
8
+ 83882d2208ed189361617d5ab8532a325aaf729d
9
+
10
+ * SQLite3::Database#trace now takes either a block or an object that responds
11
+ to "call". The previous implementation passed around a VALUE that was cast
12
+ to a void *. This is dangerous because the value could get garbage collected
13
+ before the proc was called. If the user wants data passed around with the
14
+ block, they should use variables available to the closure or create an
15
+ object.
16
+
17
+ * SQLite3::Statement#step automatically converts to ruby types, where before
18
+ all values were automatically yielded as strings. This will only be a
19
+ problem for people who were accessing information about the database that
20
+ wasn't previously passed through the pure ruby conversion code.
21
+
22
+ * SQLite3::Database#errmsg no longer takes a parameter to return error
23
+ messages as UTF-16. Do people even use that? I opt for staying UTF-8 when
24
+ possible. See test_integration.rb test_errmsg_utf16
25
+
26
+ * SQLite3::Database#authorize same changes as trace
27
+
28
+ * test/test_tc_database.rb was removed because we no longer use the Driver
29
+ design pattern.
30
+
31
+ = Garbage Collection Strategy
32
+
33
+ All statements keep pointers back to their respective database connections.
34
+ The @connection instance variable on the Statement handle keeps the database
35
+ connection alive. Memory allocated for a statement handler will be freed in
36
+ two cases:
37
+
38
+ * close is called on the statement
39
+ * The SQLite3::Database object gets garbage collected
40
+
41
+ We can't free the memory for the statement in the garbage collection function
42
+ for the statement handler. The reason is because there exists a race
43
+ condition. We cannot guarantee the order in which objects will be garbage
44
+ collected. So, it is possible that a connection and a statement are up for
45
+ garbage collection. If the database connection were to be free'd before the
46
+ statement, then boom. Instead we'll be conservative and free unclosed
47
+ statements when the connection is terminated.
48
+
@@ -1,3 +1,26 @@
1
+ === 1.3.0.beta.1 / 2010-05-10
2
+
3
+ * Enhancements
4
+ * Complete rewrite of C-based adapter from SWIG to hand-crafted one [tenderlove]
5
+ See API_CHANGES document for details.
6
+ This closes: Bug #27300, Bug #27241, Patch #16020
7
+ * Improved UTF, Unicode, M17N, all that handling and proper BLOB handling [tenderlove, nurse]
8
+
9
+ * Experimental
10
+ * Added API to access and load extensions. [kashif]
11
+ These functions maps directly into SQLite3 own enable_load_extension()
12
+ and load_extension() C-API functions. See SQLite3::Database API documentation for details.
13
+ This closes: Patches #9178
14
+
15
+ * Bugfixes
16
+ * Corrected gem dependencies (runtime and development)
17
+ * Fixed threaded tests [Alexey Borzenkov]
18
+ * Removed GitHub gemspec
19
+ * Fixed "No definition for" warnings from RDoc
20
+ * Generate zip and tgz files for releases
21
+ * Added Luis Lavena as gem Author (maintainer)
22
+ * Prevent mkmf interfere with Mighty Snow Leopard
23
+
1
24
  === 1.2.5 / 25 Jul 2009
2
25
 
3
26
  * Check for illegal nil before executing SQL [Erik Veenstra]
@@ -6,6 +29,7 @@
6
29
  * Build gem binaries for Windows.
7
30
  * Improved Ruby 1.9 support compatibility.
8
31
  * Taint returned values. Patch #20325.
32
+ * Database.open and Database.new now take an optional block [Gerrit Kaiser]
9
33
 
10
34
 
11
35
  === 1.2.4.1 (internal) / 5 Jul 2009
@@ -1,20 +1,24 @@
1
+ API_CHANGES.rdoc
2
+ CHANGELOG.rdoc
1
3
  ChangeLog.cvs
2
- History.txt
3
4
  LICENSE
4
5
  Manifest.txt
5
- README.txt
6
+ README.rdoc
6
7
  Rakefile
7
- ext/sqlite3_api/extconf.rb
8
- ext/sqlite3_api/sqlite3_api.i
9
- ext/sqlite3_api/sqlite3_api_wrap.c
8
+ ext/sqlite3/database.c
9
+ ext/sqlite3/database.h
10
+ ext/sqlite3/exception.c
11
+ ext/sqlite3/exception.h
12
+ ext/sqlite3/extconf.rb
13
+ ext/sqlite3/sqlite3.c
14
+ ext/sqlite3/sqlite3_ruby.h
15
+ ext/sqlite3/statement.c
16
+ ext/sqlite3/statement.h
10
17
  faq/faq.rb
11
18
  faq/faq.yml
12
19
  lib/sqlite3.rb
13
20
  lib/sqlite3/constants.rb
14
21
  lib/sqlite3/database.rb
15
- lib/sqlite3/driver/dl/api.rb
16
- lib/sqlite3/driver/dl/driver.rb
17
- lib/sqlite3/driver/native/driver.rb
18
22
  lib/sqlite3/errors.rb
19
23
  lib/sqlite3/pragmas.rb
20
24
  lib/sqlite3/resultset.rb
@@ -23,19 +27,15 @@ lib/sqlite3/translator.rb
23
27
  lib/sqlite3/value.rb
24
28
  lib/sqlite3/version.rb
25
29
  setup.rb
26
- tasks/benchmark.rake
27
30
  tasks/faq.rake
28
- tasks/gem.rake
29
31
  tasks/native.rake
30
32
  tasks/vendor_sqlite3.rake
31
- test/bm.rb
32
- test/driver/dl/tc_driver.rb
33
33
  test/helper.rb
34
- test/native-vs-dl.rb
35
34
  test/test_database.rb
36
- test/test_errors.rb
37
35
  test/test_integration.rb
38
36
  test/test_integration_open_close.rb
39
37
  test/test_integration_pending.rb
40
38
  test/test_integration_resultset.rb
41
39
  test/test_integration_statement.rb
40
+ test/test_sqlite3.rb
41
+ test/test_statement.rb
@@ -2,7 +2,7 @@
2
2
 
3
3
  * http://sqlite3-ruby.rubyforge.org
4
4
  * http://rubyforge.org/projects/sqlite3-ruby
5
- * http://github.com/jamis/sqlite3-ruby
5
+ * http://github.com/luislavena/sqlite3-ruby
6
6
 
7
7
  == DESCRIPTION
8
8
 
@@ -30,11 +30,6 @@ If you have sqlite3 installed in a non-standard location, you can specify the lo
30
30
  gem install sqlite3-ruby -- --with-sqlite3-include=/opt/local/include \
31
31
  --with-sqlite3-lib=/opt/local/lib
32
32
 
33
- Also, the gem ships with the C source-code pre-built, so (as of version 1.1.1)
34
- you no longer need to have SWIG installed. However, if you have SWIG installed
35
- and you want to generate the C file yourself, you can specify the
36
- <code>--with-swig</code> option.
37
-
38
33
  == Usage
39
34
 
40
35
  For help figuring out the SQLite3/Ruby interface, check out the
data/Rakefile CHANGED
@@ -1,5 +1,10 @@
1
- require 'rubygems'
2
- require 'rake'
1
+ #
2
+ # NOTE: Keep this file clean.
3
+ # Add your customizations inside tasks directory.
4
+ # Thank You.
5
+ #
3
6
 
4
7
  # load rakefile extensions (tasks)
5
- Dir['tasks/*.rake'].each { |f| import f }
8
+ Dir['tasks/*.rake'].sort.each { |f| load f }
9
+
10
+ # vim: syntax=ruby
@@ -0,0 +1,687 @@
1
+ #include <sqlite3_ruby.h>
2
+
3
+ #define REQUIRE_OPEN_DB(_ctxt) \
4
+ if(!_ctxt->db) \
5
+ rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed database");
6
+
7
+ VALUE cSqlite3Database;
8
+ static VALUE sym_utf16, sym_results_as_hash, sym_type_translation;
9
+
10
+ static void deallocate(void * ctx)
11
+ {
12
+ sqlite3RubyPtr c = (sqlite3RubyPtr)ctx;
13
+ sqlite3 * db = c->db;
14
+ sqlite3_stmt * stmt;
15
+
16
+ if(db) {
17
+ while((stmt = sqlite3_next_stmt(db, NULL)) != NULL) {
18
+ sqlite3_finalize(stmt);
19
+ }
20
+ sqlite3_close(db);
21
+ }
22
+ xfree(c);
23
+ }
24
+
25
+ static VALUE allocate(VALUE klass)
26
+ {
27
+ sqlite3RubyPtr ctx = xcalloc((size_t)1, sizeof(sqlite3Ruby));
28
+ return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
29
+ }
30
+
31
+ static char *
32
+ utf16_string_value_ptr(VALUE str)
33
+ {
34
+ StringValue(str);
35
+ rb_str_buf_cat(str, "\x00", 1L);
36
+ return RSTRING_PTR(str);
37
+ }
38
+
39
+ /* call-seq: SQLite3::Database.new(file, options = {})
40
+ *
41
+ * Create a new Database object that opens the given file. If utf16
42
+ * is +true+, the filename is interpreted as a UTF-16 encoded string.
43
+ *
44
+ * By default, the new database will return result rows as arrays
45
+ * (#results_as_hash) and has type translation disabled (#type_translation=).
46
+ */
47
+ static VALUE initialize(int argc, VALUE *argv, VALUE self)
48
+ {
49
+ sqlite3RubyPtr ctx;
50
+ VALUE file;
51
+ VALUE opts;
52
+ VALUE zvfs;
53
+ int status;
54
+
55
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
56
+
57
+ rb_scan_args(argc, argv, "12", &file, &opts, &zvfs);
58
+ if(NIL_P(opts)) opts = rb_hash_new();
59
+
60
+ #ifdef HAVE_RUBY_ENCODING_H
61
+ if(UTF16_LE_P(file)) {
62
+ status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
63
+ } else {
64
+ #endif
65
+
66
+ if(Qtrue == rb_hash_aref(opts, sym_utf16)) {
67
+ status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
68
+ } else {
69
+
70
+ #ifdef HAVE_RUBY_ENCODING_H
71
+ if(!UTF8_P(file)) {
72
+ file = rb_str_export_to_enc(file, rb_utf8_encoding());
73
+ }
74
+ #endif
75
+
76
+ status = sqlite3_open_v2(
77
+ StringValuePtr(file),
78
+ &ctx->db,
79
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
80
+ NIL_P(zvfs) ? NULL : StringValuePtr(zvfs)
81
+ );
82
+ }
83
+
84
+ #ifdef HAVE_RUBY_ENCODING_H
85
+ }
86
+ #endif
87
+
88
+ CHECK(ctx->db, status)
89
+
90
+ rb_iv_set(self, "@tracefunc", Qnil);
91
+ rb_iv_set(self, "@authorizer", Qnil);
92
+ rb_iv_set(self, "@encoding", Qnil);
93
+ rb_iv_set(self, "@busy_handler", Qnil);
94
+ rb_iv_set(self, "@results_as_hash", rb_hash_aref(opts, sym_results_as_hash));
95
+ rb_iv_set(self, "@type_translation", rb_hash_aref(opts, sym_type_translation));
96
+
97
+ if(rb_block_given_p()) {
98
+ rb_yield(self);
99
+ rb_funcall(self, rb_intern("close"), 0);
100
+ }
101
+
102
+ return self;
103
+ }
104
+
105
+ /* call-seq: db.close
106
+ *
107
+ * Closes this database.
108
+ */
109
+ static VALUE sqlite3_rb_close(VALUE self)
110
+ {
111
+ sqlite3RubyPtr ctx;
112
+ sqlite3 * db;
113
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
114
+
115
+ db = ctx->db;
116
+ CHECK(db, sqlite3_close(ctx->db));
117
+
118
+ ctx->db = NULL;
119
+
120
+ return self;
121
+ }
122
+
123
+ /* call-seq: db.closed?
124
+ *
125
+ * Returns +true+ if this database instance has been closed (see #close).
126
+ */
127
+ static VALUE closed_p(VALUE self)
128
+ {
129
+ sqlite3RubyPtr ctx;
130
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
131
+
132
+ if(!ctx->db) return Qtrue;
133
+
134
+ return Qfalse;
135
+ }
136
+
137
+ /* call-seq: total_changes
138
+ *
139
+ * Returns the total number of changes made to this database instance
140
+ * since it was opened.
141
+ */
142
+ static VALUE total_changes(VALUE self)
143
+ {
144
+ sqlite3RubyPtr ctx;
145
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
146
+ REQUIRE_OPEN_DB(ctx);
147
+
148
+ return INT2NUM((long)sqlite3_total_changes(ctx->db));
149
+ }
150
+
151
+ static void tracefunc(void * data, const char *sql)
152
+ {
153
+ VALUE self = (VALUE)data;
154
+ VALUE thing = rb_iv_get(self, "@tracefunc");
155
+ rb_funcall(thing, rb_intern("call"), 1, rb_str_new2(sql));
156
+ }
157
+
158
+ /* call-seq:
159
+ * trace { |sql| ... }
160
+ * trace(Class.new { def call sql; end }.new)
161
+ *
162
+ * Installs (or removes) a block that will be invoked for every SQL
163
+ * statement executed. The block receives one parameter: the SQL statement
164
+ * executed. If the block is +nil+, any existing tracer will be uninstalled.
165
+ */
166
+ static VALUE trace(int argc, VALUE *argv, VALUE self)
167
+ {
168
+ sqlite3RubyPtr ctx;
169
+ VALUE block;
170
+
171
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
172
+ REQUIRE_OPEN_DB(ctx);
173
+
174
+ rb_scan_args(argc, argv, "01", &block);
175
+
176
+ if(NIL_P(block) && rb_block_given_p()) block = rb_block_proc();
177
+
178
+ rb_iv_set(self, "@tracefunc", block);
179
+
180
+ sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)self);
181
+
182
+ return self;
183
+ }
184
+
185
+ static int rb_sqlite3_busy_handler(void * ctx, int count)
186
+ {
187
+ VALUE self = (VALUE)(ctx);
188
+ VALUE handle = rb_iv_get(self, "@busy_handler");
189
+ VALUE result = rb_funcall(handle, rb_intern("call"), 1, INT2NUM((long)count));
190
+
191
+ if(Qfalse == result) return 0;
192
+
193
+ return 1;
194
+ }
195
+
196
+ /* call-seq:
197
+ * busy_handler { |count| ... }
198
+ * busy_handler(Class.new { def call count; end }.new)
199
+ *
200
+ * Register a busy handler with this database instance. When a requested
201
+ * resource is busy, this handler will be invoked. If the handler returns
202
+ * +false+, the operation will be aborted; otherwise, the resource will
203
+ * be requested again.
204
+ *
205
+ * The handler will be invoked with the name of the resource that was
206
+ * busy, and the number of times it has been retried.
207
+ *
208
+ * See also the mutually exclusive #busy_timeout.
209
+ */
210
+ static VALUE busy_handler(int argc, VALUE *argv, VALUE self)
211
+ {
212
+ sqlite3RubyPtr ctx;
213
+ VALUE block;
214
+ int status;
215
+
216
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
217
+ REQUIRE_OPEN_DB(ctx);
218
+
219
+ rb_scan_args(argc, argv, "01", &block);
220
+
221
+ if(NIL_P(block) && rb_block_given_p()) block = rb_block_proc();
222
+
223
+ rb_iv_set(self, "@busy_handler", block);
224
+
225
+ status = sqlite3_busy_handler(
226
+ ctx->db, NIL_P(block) ? NULL : rb_sqlite3_busy_handler, (void *)self);
227
+
228
+ CHECK(ctx->db, status);
229
+
230
+ return self;
231
+ }
232
+
233
+ /* call-seq: last_insert_row_id
234
+ *
235
+ * Obtains the unique row ID of the last row to be inserted by this Database
236
+ * instance.
237
+ */
238
+ static VALUE last_insert_row_id(VALUE self)
239
+ {
240
+ sqlite3RubyPtr ctx;
241
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
242
+ REQUIRE_OPEN_DB(ctx);
243
+
244
+ return LL2NUM(sqlite3_last_insert_rowid(ctx->db));
245
+ }
246
+
247
+ static VALUE sqlite3val2rb(sqlite3_value * val)
248
+ {
249
+ switch(sqlite3_value_type(val)) {
250
+ case SQLITE_INTEGER:
251
+ return LL2NUM(sqlite3_value_int64(val));
252
+ break;
253
+ case SQLITE_FLOAT:
254
+ return rb_float_new(sqlite3_value_double(val));
255
+ break;
256
+ case SQLITE_TEXT:
257
+ return rb_tainted_str_new2((const char *)sqlite3_value_text(val));
258
+ break;
259
+ case SQLITE_BLOB:
260
+ return rb_tainted_str_new2((const char *)sqlite3_value_blob(val));
261
+ break;
262
+ case SQLITE_NULL:
263
+ return Qnil;
264
+ break;
265
+ default:
266
+ rb_raise(rb_eRuntimeError, "bad type"); /* FIXME */
267
+ }
268
+ }
269
+
270
+ static void set_sqlite3_func_result(sqlite3_context * ctx, VALUE result)
271
+ {
272
+ switch(TYPE(result)) {
273
+ case T_NIL:
274
+ sqlite3_result_null(ctx);
275
+ break;
276
+ case T_FIXNUM:
277
+ sqlite3_result_int64(ctx, (sqlite3_int64)FIX2LONG(result));
278
+ break;
279
+ case T_BIGNUM:
280
+ #if SIZEOF_LONG < 8
281
+ if (RBIGNUM_LEN(result) * SIZEOF_BDIGITS <= 8) {
282
+ sqlite3_result_int64(ctx, NUM2LL(result));
283
+ break;
284
+ }
285
+ #endif
286
+ case T_FLOAT:
287
+ sqlite3_result_double(ctx, NUM2DBL(result));
288
+ break;
289
+ case T_STRING:
290
+ sqlite3_result_text(
291
+ ctx,
292
+ (const char *)StringValuePtr(result),
293
+ (int)RSTRING_LEN(result),
294
+ SQLITE_TRANSIENT
295
+ );
296
+ break;
297
+ default:
298
+ rb_raise(rb_eRuntimeError, "can't return %s",
299
+ rb_class2name(CLASS_OF(result)));
300
+ }
301
+ }
302
+
303
+ static void rb_sqlite3_func(sqlite3_context * ctx, int argc, sqlite3_value **argv)
304
+ {
305
+ VALUE callable = (VALUE)sqlite3_user_data(ctx);
306
+ VALUE * params = NULL;
307
+ VALUE result;
308
+ int i;
309
+
310
+ if (argc > 0) {
311
+ params = xcalloc((size_t)argc, sizeof(VALUE *));
312
+
313
+ for(i = 0; i < argc; i++) {
314
+ params[i] = sqlite3val2rb(argv[i]);
315
+ }
316
+ }
317
+
318
+ result = rb_funcall2(callable, rb_intern("call"), argc, params);
319
+ xfree(params);
320
+
321
+ set_sqlite3_func_result(ctx, result);
322
+ }
323
+
324
+ #ifndef HAVE_RB_PROC_ARITY
325
+ int rb_proc_arity(VALUE self)
326
+ {
327
+ return (int)NUM2INT(rb_funcall(self, rb_intern("arity"), 0));
328
+ }
329
+ #endif
330
+
331
+ /* call-seq: define_function(name) { |args,...| }
332
+ *
333
+ * Define a function named +name+ with +args+. The arity of the block
334
+ * will be used as the arity for the function defined.
335
+ */
336
+ static VALUE define_function(VALUE self, VALUE name)
337
+ {
338
+ sqlite3RubyPtr ctx;
339
+ VALUE block;
340
+ int status;
341
+
342
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
343
+ REQUIRE_OPEN_DB(ctx);
344
+
345
+ block = rb_block_proc();
346
+
347
+ status = sqlite3_create_function(
348
+ ctx->db,
349
+ StringValuePtr(name),
350
+ rb_proc_arity(block),
351
+ SQLITE_UTF8,
352
+ (void *)block,
353
+ rb_sqlite3_func,
354
+ NULL,
355
+ NULL
356
+ );
357
+
358
+ CHECK(ctx->db, status);
359
+
360
+ return self;
361
+ }
362
+
363
+ static int sqlite3_obj_method_arity(VALUE obj, ID id)
364
+ {
365
+ VALUE method = rb_funcall(obj, rb_intern("method"), 1, ID2SYM(id));
366
+ VALUE arity = rb_funcall(method, rb_intern("arity"), 0);
367
+
368
+ return (int)NUM2INT(arity);
369
+ }
370
+
371
+ static void rb_sqlite3_step(sqlite3_context * ctx, int argc, sqlite3_value **argv)
372
+ {
373
+ VALUE callable = (VALUE)sqlite3_user_data(ctx);
374
+ VALUE * params = NULL;
375
+ int i;
376
+
377
+ if (argc > 0) {
378
+ params = xcalloc((size_t)argc, sizeof(VALUE *));
379
+ for(i = 0; i < argc; i++) {
380
+ params[i] = sqlite3val2rb(argv[i]);
381
+ }
382
+ }
383
+ rb_funcall2(callable, rb_intern("step"), argc, params);
384
+ xfree(params);
385
+ }
386
+
387
+ static void rb_sqlite3_final(sqlite3_context * ctx)
388
+ {
389
+ VALUE callable = (VALUE)sqlite3_user_data(ctx);
390
+ VALUE result = rb_funcall(callable, rb_intern("finalize"), 0);
391
+ set_sqlite3_func_result(ctx, result);
392
+ }
393
+
394
+ /* call-seq: define_aggregator(name, aggregator)
395
+ *
396
+ * Define an aggregate function named +name+ using the object +aggregator+.
397
+ * +aggregator+ must respond to +step+ and +finalize+. +step+ will be called
398
+ * with row information and +finalize+ must return the return value for the
399
+ * aggregator function.
400
+ */
401
+ static VALUE define_aggregator(VALUE self, VALUE name, VALUE aggregator)
402
+ {
403
+ sqlite3RubyPtr ctx;
404
+ int arity, status;
405
+
406
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
407
+ REQUIRE_OPEN_DB(ctx);
408
+
409
+ arity = sqlite3_obj_method_arity(aggregator, rb_intern("step"));
410
+
411
+ status = sqlite3_create_function(
412
+ ctx->db,
413
+ StringValuePtr(name),
414
+ arity,
415
+ SQLITE_UTF8,
416
+ (void *)aggregator,
417
+ NULL,
418
+ rb_sqlite3_step,
419
+ rb_sqlite3_final
420
+ );
421
+
422
+ rb_iv_set(self, "@agregator", aggregator);
423
+
424
+ CHECK(ctx->db, status);
425
+
426
+ return self;
427
+ }
428
+
429
+ /* call-seq: interrupt
430
+ *
431
+ * Interrupts the currently executing operation, causing it to abort.
432
+ */
433
+ static VALUE interrupt(VALUE self)
434
+ {
435
+ sqlite3RubyPtr ctx;
436
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
437
+ REQUIRE_OPEN_DB(ctx);
438
+
439
+ sqlite3_interrupt(ctx->db);
440
+
441
+ return self;
442
+ }
443
+
444
+ /* call-seq: errmsg
445
+ *
446
+ * Return a string describing the last error to have occurred with this
447
+ * database.
448
+ */
449
+ static VALUE errmsg(VALUE self)
450
+ {
451
+ sqlite3RubyPtr ctx;
452
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
453
+ REQUIRE_OPEN_DB(ctx);
454
+
455
+ return rb_str_new2(sqlite3_errmsg(ctx->db));
456
+ }
457
+
458
+ /* call-seq: errcode
459
+ *
460
+ * Return an integer representing the last error to have occurred with this
461
+ * database.
462
+ */
463
+ static VALUE errcode(VALUE self)
464
+ {
465
+ sqlite3RubyPtr ctx;
466
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
467
+ REQUIRE_OPEN_DB(ctx);
468
+
469
+ return INT2NUM((long)sqlite3_errcode(ctx->db));
470
+ }
471
+
472
+ /* call-seq: complete?(sql)
473
+ *
474
+ * Return +true+ if the string is a valid (ie, parsable) SQL statement, and
475
+ * +false+ otherwise.
476
+ */
477
+ static VALUE complete_p(VALUE UNUSED(self), VALUE sql)
478
+ {
479
+ if(sqlite3_complete(StringValuePtr(sql)))
480
+ return Qtrue;
481
+
482
+ return Qfalse;
483
+ }
484
+
485
+ /* call-seq: changes
486
+ *
487
+ * Returns the number of changes made to this database instance by the last
488
+ * operation performed. Note that a "delete from table" without a where
489
+ * clause will not affect this value.
490
+ */
491
+ static VALUE changes(VALUE self)
492
+ {
493
+ sqlite3RubyPtr ctx;
494
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
495
+ REQUIRE_OPEN_DB(ctx);
496
+
497
+ return INT2NUM(sqlite3_changes(ctx->db));
498
+ }
499
+
500
+ static int rb_sqlite3_auth(
501
+ void *ctx,
502
+ int _action,
503
+ const char * _a,
504
+ const char * _b,
505
+ const char * _c,
506
+ const char * _d)
507
+ {
508
+ VALUE self = (VALUE)ctx;
509
+ VALUE action = INT2NUM(_action);
510
+ VALUE a = _a ? rb_str_new2(_a) : Qnil;
511
+ VALUE b = _b ? rb_str_new2(_b) : Qnil;
512
+ VALUE c = _c ? rb_str_new2(_c) : Qnil;
513
+ VALUE d = _d ? rb_str_new2(_d) : Qnil;
514
+ VALUE callback = rb_iv_get(self, "@authorizer");
515
+ VALUE result = rb_funcall(callback, rb_intern("call"), 5, action, a, b, c, d);
516
+
517
+ if(T_FIXNUM == TYPE(result)) return (int)NUM2INT(result);
518
+ if(Qtrue == result) return SQLITE_OK;
519
+ if(Qfalse == result) return SQLITE_DENY;
520
+
521
+ return SQLITE_IGNORE;
522
+ }
523
+
524
+ /* call-seq: set_authorizer = auth
525
+ *
526
+ * Set the authorizer for this database. +auth+ must respond to +call+, and
527
+ * +call+ must take 5 arguments.
528
+ *
529
+ * Installs (or removes) a block that will be invoked for every access
530
+ * to the database. If the block returns 0 (or +true+), the statement
531
+ * is allowed to proceed. Returning 1 or false causes an authorization error to
532
+ * occur, and returning 2 or nil causes the access to be silently denied.
533
+ */
534
+ static VALUE set_authorizer(VALUE self, VALUE authorizer)
535
+ {
536
+ sqlite3RubyPtr ctx;
537
+ int status;
538
+
539
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
540
+ REQUIRE_OPEN_DB(ctx);
541
+
542
+ status = sqlite3_set_authorizer(
543
+ ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)self
544
+ );
545
+
546
+ CHECK(ctx->db, status);
547
+
548
+ rb_iv_set(self, "@authorizer", authorizer);
549
+
550
+ return self;
551
+ }
552
+
553
+ /* call-seq: db.busy_timeout = ms
554
+ *
555
+ * Indicates that if a request for a resource terminates because that
556
+ * resource is busy, SQLite should sleep and retry for up to the indicated
557
+ * number of milliseconds. By default, SQLite does not retry
558
+ * busy resources. To restore the default behavior, send 0 as the
559
+ * +ms+ parameter.
560
+ *
561
+ * See also the mutually exclusive #busy_handler.
562
+ */
563
+ static VALUE set_busy_timeout(VALUE self, VALUE timeout)
564
+ {
565
+ sqlite3RubyPtr ctx;
566
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
567
+ REQUIRE_OPEN_DB(ctx);
568
+
569
+ CHECK(ctx->db, sqlite3_busy_timeout(ctx->db, (int)NUM2INT(timeout)));
570
+
571
+ return self;
572
+ }
573
+
574
+ /* call-seq: db.load_extension(file)
575
+ *
576
+ * Loads an SQLite extension library from the named file. Extension
577
+ * loading must be enabled using db.enable_load_extension(1) prior
578
+ * to calling this API.
579
+ */
580
+ static VALUE load_extension(VALUE self, VALUE file)
581
+ {
582
+ sqlite3RubyPtr ctx;
583
+ int status;
584
+ char *errMsg;
585
+ VALUE errexp;
586
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
587
+ REQUIRE_OPEN_DB(ctx);
588
+
589
+ status = sqlite3_load_extension(ctx->db, RSTRING_PTR(file), 0, &errMsg);
590
+ if (status != SQLITE_OK)
591
+ {
592
+ errexp = rb_exc_new2(rb_eRuntimeError, errMsg);
593
+ sqlite3_free(errMsg);
594
+ rb_exc_raise(errexp);
595
+ }
596
+
597
+ return self;
598
+ }
599
+
600
+ /* call-seq: db.enable_load_extension(onoff)
601
+ *
602
+ * Enable or disable extension loading.
603
+ */
604
+ static VALUE enable_load_extension(VALUE self, VALUE onoff)
605
+ {
606
+ sqlite3RubyPtr ctx;
607
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
608
+ REQUIRE_OPEN_DB(ctx);
609
+
610
+ CHECK(ctx->db, sqlite3_enable_load_extension(ctx->db, (int)NUM2INT(onoff)));
611
+
612
+ return self;
613
+ }
614
+
615
+ #ifdef HAVE_RUBY_ENCODING_H
616
+ static int enc_cb(void * _self, int UNUSED(columns), char **data, char **UNUSED(names))
617
+ {
618
+ VALUE self = (VALUE)_self;
619
+
620
+ int index = rb_enc_find_index(data[0]);
621
+ rb_encoding * e = rb_enc_from_index(index);
622
+ rb_iv_set(self, "@encoding", rb_enc_from_encoding(e));
623
+
624
+ return 0;
625
+ }
626
+
627
+ /* call-seq: db.encoding
628
+ *
629
+ * Fetch the encoding set on this database
630
+ */
631
+ static VALUE db_encoding(VALUE self)
632
+ {
633
+ sqlite3RubyPtr ctx;
634
+ VALUE enc;
635
+
636
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
637
+ REQUIRE_OPEN_DB(ctx);
638
+
639
+ enc = rb_iv_get(self, "@encoding");
640
+
641
+ if(NIL_P(enc)) {
642
+ sqlite3_exec(ctx->db, "PRAGMA encoding", enc_cb, (void *)self, NULL);
643
+ }
644
+
645
+ return rb_iv_get(self, "@encoding");
646
+ }
647
+ #endif
648
+
649
+ void init_sqlite3_database()
650
+ {
651
+ ID id_utf16, id_results_as_hash, id_type_translation;
652
+ #if 0
653
+ VALUE mSqlite3 = rb_define_module("SQLite3");
654
+ #endif
655
+ cSqlite3Database = rb_define_class_under(mSqlite3, "Database", rb_cObject);
656
+
657
+ rb_define_alloc_func(cSqlite3Database, allocate);
658
+ rb_define_method(cSqlite3Database, "initialize", initialize, -1);
659
+ rb_define_method(cSqlite3Database, "close", sqlite3_rb_close, 0);
660
+ rb_define_method(cSqlite3Database, "closed?", closed_p, 0);
661
+ rb_define_method(cSqlite3Database, "total_changes", total_changes, 0);
662
+ rb_define_method(cSqlite3Database, "trace", trace, -1);
663
+ rb_define_method(cSqlite3Database, "last_insert_row_id", last_insert_row_id, 0);
664
+ rb_define_method(cSqlite3Database, "define_function", define_function, 1);
665
+ rb_define_method(cSqlite3Database, "define_aggregator", define_aggregator, 2);
666
+ rb_define_method(cSqlite3Database, "interrupt", interrupt, 0);
667
+ rb_define_method(cSqlite3Database, "errmsg", errmsg, 0);
668
+ rb_define_method(cSqlite3Database, "errcode", errcode, 0);
669
+ rb_define_method(cSqlite3Database, "complete?", complete_p, 1);
670
+ rb_define_method(cSqlite3Database, "changes", changes, 0);
671
+ rb_define_method(cSqlite3Database, "authorizer=", set_authorizer, 1);
672
+ rb_define_method(cSqlite3Database, "busy_handler", busy_handler, -1);
673
+ rb_define_method(cSqlite3Database, "busy_timeout=", set_busy_timeout, 1);
674
+ rb_define_method(cSqlite3Database, "load_extension", load_extension, 1);
675
+ rb_define_method(cSqlite3Database, "enable_load_extension", enable_load_extension, 1);
676
+
677
+ #ifdef HAVE_RUBY_ENCODING_H
678
+ rb_define_method(cSqlite3Database, "encoding", db_encoding, 0);
679
+ #endif
680
+
681
+ id_utf16 = rb_intern("utf16");
682
+ sym_utf16 = ID2SYM(id_utf16);
683
+ id_results_as_hash = rb_intern("results_as_hash");
684
+ sym_results_as_hash = ID2SYM(id_results_as_hash);
685
+ id_type_translation = rb_intern("type_translation");
686
+ sym_type_translation = ID2SYM(id_type_translation);
687
+ }