sqlite3 0.1.1 → 1.3.3.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/API_CHANGES.rdoc +50 -0
  2. data/CHANGELOG.rdoc +177 -0
  3. data/ChangeLog.cvs +88 -0
  4. data/LICENSE +24 -17
  5. data/Manifest.txt +50 -0
  6. data/README.rdoc +100 -4
  7. data/Rakefile +10 -0
  8. data/ext/sqlite3/backup.c +164 -0
  9. data/ext/sqlite3/backup.h +15 -0
  10. data/ext/sqlite3/database.c +762 -0
  11. data/ext/sqlite3/database.h +15 -0
  12. data/ext/sqlite3/exception.c +94 -0
  13. data/ext/sqlite3/exception.h +8 -0
  14. data/ext/sqlite3/extconf.rb +47 -0
  15. data/ext/sqlite3/sqlite3.c +36 -0
  16. data/ext/sqlite3/sqlite3_ruby.h +44 -0
  17. data/ext/sqlite3/statement.c +419 -0
  18. data/ext/sqlite3/statement.h +16 -0
  19. data/faq/faq.rb +145 -0
  20. data/faq/faq.yml +426 -0
  21. data/lib/sqlite3.rb +9 -12
  22. data/lib/sqlite3/constants.rb +46 -72
  23. data/lib/sqlite3/database.rb +415 -140
  24. data/lib/sqlite3/errors.rb +1 -56
  25. data/lib/sqlite3/pragmas.rb +246 -63
  26. data/lib/sqlite3/resultset.rb +30 -112
  27. data/lib/sqlite3/statement.rb +42 -108
  28. data/lib/sqlite3/translator.rb +118 -0
  29. data/lib/sqlite3/value.rb +57 -0
  30. data/lib/sqlite3/version.rb +23 -1
  31. data/setup.rb +1333 -0
  32. data/tasks/faq.rake +9 -0
  33. data/tasks/gem.rake +31 -0
  34. data/tasks/native.rake +61 -0
  35. data/tasks/vendor_sqlite3.rake +104 -0
  36. data/test/helper.rb +3 -0
  37. data/test/test_backup.rb +33 -0
  38. data/test/test_collation.rb +82 -0
  39. data/test/test_database.rb +312 -0
  40. data/test/test_database_readonly.rb +29 -0
  41. data/test/test_deprecated.rb +37 -0
  42. data/test/test_encoding.rb +119 -0
  43. data/test/test_integration.rb +544 -0
  44. data/test/test_integration_open_close.rb +30 -0
  45. data/test/test_integration_pending.rb +115 -0
  46. data/test/test_integration_resultset.rb +156 -0
  47. data/test/test_integration_statement.rb +194 -0
  48. data/test/test_sqlite3.rb +9 -0
  49. data/test/test_statement.rb +207 -0
  50. data/test/test_statement_execute.rb +35 -0
  51. metadata +124 -67
  52. data/lib/sqlite3/api.rb +0 -52
  53. data/lib/sqlite3/driver.rb +0 -138
  54. data/lib/sqlite3/encoding.rb +0 -43
  55. data/lib/sqlite3/extensions.rb +0 -27
@@ -0,0 +1,10 @@
1
+ #
2
+ # NOTE: Keep this file clean.
3
+ # Add your customizations inside tasks directory.
4
+ # Thank You.
5
+ #
6
+
7
+ # load rakefile extensions (tasks)
8
+ Dir['tasks/*.rake'].sort.each { |f| load f }
9
+
10
+ # vim: syntax=ruby
@@ -0,0 +1,164 @@
1
+ #include <sqlite3_ruby.h>
2
+
3
+ #define REQUIRE_OPEN_BACKUP(_ctxt) \
4
+ if(!_ctxt->p) \
5
+ rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed backup");
6
+
7
+ VALUE cSqlite3Backup;
8
+
9
+ static void deallocate(void * ctx)
10
+ {
11
+ sqlite3BackupRubyPtr c = (sqlite3BackupRubyPtr)ctx;
12
+ xfree(c);
13
+ }
14
+
15
+ static VALUE allocate(VALUE klass)
16
+ {
17
+ sqlite3BackupRubyPtr ctx = xcalloc((size_t)1, sizeof(sqlite3BackupRuby));
18
+ return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
19
+ }
20
+
21
+ /* call-seq: SQLite3::Backup.new(dstdb, dstname, srcdb, srcname)
22
+ *
23
+ * Initialize backup the backup.
24
+ *
25
+ * dstdb:
26
+ * the destination SQLite3::Database object.
27
+ * dstname:
28
+ * the destination's database name.
29
+ * srcdb:
30
+ * the source SQLite3::Database object.
31
+ * srcname:
32
+ * the source's database name.
33
+ *
34
+ * The database name is "main", "temp", or the name specified in an
35
+ * ATTACH statement.
36
+ *
37
+ * This feature requires SQLite 3.6.11 or later.
38
+ *
39
+ * require 'sqlite3'
40
+ * sdb = SQLite3::Database.new('src.sqlite3')
41
+ *
42
+ * ddb = SQLite3::Database.new(':memory:')
43
+ * b = SQLite3::Backup.new(ddb, 'main', sdb, 'main')
44
+ * p [b.remaining, b.pagecount] # invalid value; for example [0, 0]
45
+ * begin
46
+ * p b.step(1) #=> OK or DONE
47
+ * p [b.remaining, b.pagecount]
48
+ * end while b.remaining > 0
49
+ * b.finish
50
+ *
51
+ * ddb = SQLite3::Database.new(':memory:')
52
+ * b = SQLite3::Backup.new(ddb, 'main', sdb, 'main')
53
+ * b.step(-1) #=> DONE
54
+ * b.finish
55
+ *
56
+ */
57
+ static VALUE initialize(VALUE self, VALUE dstdb, VALUE dstname, VALUE srcdb, VALUE srcname)
58
+ {
59
+ sqlite3BackupRubyPtr ctx;
60
+ sqlite3RubyPtr ddb_ctx, sdb_ctx;
61
+ sqlite3_backup *pBackup;
62
+
63
+ Data_Get_Struct(self, sqlite3BackupRuby, ctx);
64
+ Data_Get_Struct(dstdb, sqlite3Ruby, ddb_ctx);
65
+ Data_Get_Struct(srcdb, sqlite3Ruby, sdb_ctx);
66
+
67
+ if(!sdb_ctx->db)
68
+ rb_raise(rb_eArgError, "cannot backup from a closed database");
69
+ if(!ddb_ctx->db)
70
+ rb_raise(rb_eArgError, "cannot backup to a closed database");
71
+
72
+ pBackup = sqlite3_backup_init(ddb_ctx->db, StringValuePtr(dstname),
73
+ sdb_ctx->db, StringValuePtr(srcname));
74
+ if( pBackup ){
75
+ ctx->p = pBackup;
76
+ }
77
+ else {
78
+ CHECK(ddb_ctx->db, sqlite3_errcode(ddb_ctx->db));
79
+ }
80
+
81
+ return self;
82
+ }
83
+
84
+ /* call-seq: SQLite3::Backup#step(nPage)
85
+ *
86
+ * Copy database pages up to +nPage+.
87
+ * If negative, copy all remaining source pages.
88
+ *
89
+ * If all pages are copied, it returns SQLite3::Constants::ErrorCode::DONE.
90
+ * When coping is not done, it returns SQLite3::Constants::ErrorCode::OK.
91
+ * When some errors occur, it returns the error code.
92
+ */
93
+ static VALUE step(VALUE self, VALUE nPage)
94
+ {
95
+ sqlite3BackupRubyPtr ctx;
96
+ int status;
97
+
98
+ Data_Get_Struct(self, sqlite3BackupRuby, ctx);
99
+ REQUIRE_OPEN_BACKUP(ctx);
100
+ status = sqlite3_backup_step(ctx->p, NUM2INT(nPage));
101
+ return INT2NUM(status);
102
+ }
103
+
104
+ /* call-seq: SQLite3::Backup#finish
105
+ *
106
+ * Destroy the backup object.
107
+ */
108
+ static VALUE finish(VALUE self)
109
+ {
110
+ sqlite3BackupRubyPtr ctx;
111
+
112
+ Data_Get_Struct(self, sqlite3BackupRuby, ctx);
113
+ REQUIRE_OPEN_BACKUP(ctx);
114
+ (void)sqlite3_backup_finish(ctx->p);
115
+ ctx->p = NULL;
116
+ return Qnil;
117
+ }
118
+
119
+ /* call-seq: SQLite3::Backup#remaining
120
+ *
121
+ * Returns the number of pages still to be backed up.
122
+ *
123
+ * Note that the value is only updated after step() is called,
124
+ * so before calling step() returned value is invalid.
125
+ */
126
+ static VALUE remaining(VALUE self)
127
+ {
128
+ sqlite3BackupRubyPtr ctx;
129
+
130
+ Data_Get_Struct(self, sqlite3BackupRuby, ctx);
131
+ REQUIRE_OPEN_BACKUP(ctx);
132
+ return INT2NUM(sqlite3_backup_remaining(ctx->p));
133
+ }
134
+
135
+ /* call-seq: SQLite3::Backup#pagecount
136
+ *
137
+ * Returns the total number of pages in the source database file.
138
+ *
139
+ * Note that the value is only updated after step() is called,
140
+ * so before calling step() returned value is invalid.
141
+ */
142
+ static VALUE pagecount(VALUE self)
143
+ {
144
+ sqlite3BackupRubyPtr ctx;
145
+
146
+ Data_Get_Struct(self, sqlite3BackupRuby, ctx);
147
+ REQUIRE_OPEN_BACKUP(ctx);
148
+ return INT2NUM(sqlite3_backup_pagecount(ctx->p));
149
+ }
150
+
151
+ void init_sqlite3_backup()
152
+ {
153
+ #if 0
154
+ VALUE mSqlite3 = rb_define_module("SQLite3");
155
+ #endif
156
+ cSqlite3Backup = rb_define_class_under(mSqlite3, "Backup", rb_cObject);
157
+
158
+ rb_define_alloc_func(cSqlite3Backup, allocate);
159
+ rb_define_method(cSqlite3Backup, "initialize", initialize, 4);
160
+ rb_define_method(cSqlite3Backup, "step", step, 1);
161
+ rb_define_method(cSqlite3Backup, "finish", finish, 0);
162
+ rb_define_method(cSqlite3Backup, "remaining", remaining, 0);
163
+ rb_define_method(cSqlite3Backup, "pagecount", pagecount, 0);
164
+ }
@@ -0,0 +1,15 @@
1
+ #ifndef SQLITE3_BACKUP_RUBY
2
+ #define SQLITE3_BACKUP_RUBY
3
+
4
+ #include <sqlite3_ruby.h>
5
+
6
+ struct _sqlite3BackupRuby {
7
+ sqlite3_backup *p;
8
+ };
9
+
10
+ typedef struct _sqlite3BackupRuby sqlite3BackupRuby;
11
+ typedef sqlite3BackupRuby * sqlite3BackupRubyPtr;
12
+
13
+ void init_sqlite3_backup();
14
+
15
+ #endif
@@ -0,0 +1,762 @@
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
+
15
+ if(db) sqlite3_close(db);
16
+ xfree(c);
17
+ }
18
+
19
+ static VALUE allocate(VALUE klass)
20
+ {
21
+ sqlite3RubyPtr ctx = xcalloc((size_t)1, sizeof(sqlite3Ruby));
22
+ return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
23
+ }
24
+
25
+ static char *
26
+ utf16_string_value_ptr(VALUE str)
27
+ {
28
+ StringValue(str);
29
+ rb_str_buf_cat(str, "\x00", 1L);
30
+ return RSTRING_PTR(str);
31
+ }
32
+
33
+ /* call-seq: SQLite3::Database.new(file, options = {})
34
+ *
35
+ * Create a new Database object that opens the given file. If utf16
36
+ * is +true+, the filename is interpreted as a UTF-16 encoded string.
37
+ *
38
+ * By default, the new database will return result rows as arrays
39
+ * (#results_as_hash) and has type translation disabled (#type_translation=).
40
+ */
41
+ static VALUE initialize(int argc, VALUE *argv, VALUE self)
42
+ {
43
+ sqlite3RubyPtr ctx;
44
+ VALUE file;
45
+ VALUE opts;
46
+ VALUE zvfs;
47
+ int mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
48
+ int status;
49
+
50
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
51
+
52
+ rb_scan_args(argc, argv, "12", &file, &opts, &zvfs);
53
+ if(NIL_P(opts)) opts = rb_hash_new();
54
+
55
+ #ifdef HAVE_RUBY_ENCODING_H
56
+ if(UTF16_LE_P(file)) {
57
+ status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
58
+ } else {
59
+ #endif
60
+
61
+ if(Qtrue == rb_hash_aref(opts, sym_utf16)) {
62
+ status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
63
+ } else {
64
+
65
+ #ifdef HAVE_RUBY_ENCODING_H
66
+ if(!UTF8_P(file)) {
67
+ file = rb_str_export_to_enc(file, rb_utf8_encoding());
68
+ }
69
+ #endif
70
+
71
+ if (Qtrue == rb_hash_aref(opts, ID2SYM(rb_intern("readonly")))) {
72
+ mode = SQLITE_OPEN_READONLY;
73
+ }
74
+ status = sqlite3_open_v2(
75
+ StringValuePtr(file),
76
+ &ctx->db,
77
+ mode,
78
+ NIL_P(zvfs) ? NULL : StringValuePtr(zvfs)
79
+ );
80
+ }
81
+
82
+ #ifdef HAVE_RUBY_ENCODING_H
83
+ }
84
+ #endif
85
+
86
+ CHECK(ctx->db, status)
87
+
88
+ rb_iv_set(self, "@tracefunc", Qnil);
89
+ rb_iv_set(self, "@authorizer", Qnil);
90
+ rb_iv_set(self, "@encoding", Qnil);
91
+ rb_iv_set(self, "@busy_handler", Qnil);
92
+ rb_iv_set(self, "@collations", rb_hash_new());
93
+ rb_iv_set(self, "@functions", rb_hash_new());
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
+ rb_iv_set(self, "@readonly", mode == SQLITE_OPEN_READONLY ? Qtrue : Qfalse);
97
+
98
+ if(rb_block_given_p()) {
99
+ rb_yield(self);
100
+ rb_funcall(self, rb_intern("close"), 0);
101
+ }
102
+
103
+ return self;
104
+ }
105
+
106
+ /* call-seq: db.close
107
+ *
108
+ * Closes this database.
109
+ */
110
+ static VALUE sqlite3_rb_close(VALUE self)
111
+ {
112
+ sqlite3RubyPtr ctx;
113
+ sqlite3 * db;
114
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
115
+
116
+ db = ctx->db;
117
+ CHECK(db, sqlite3_close(ctx->db));
118
+
119
+ ctx->db = NULL;
120
+
121
+ return self;
122
+ }
123
+
124
+ /* call-seq: db.closed?
125
+ *
126
+ * Returns +true+ if this database instance has been closed (see #close).
127
+ */
128
+ static VALUE closed_p(VALUE self)
129
+ {
130
+ sqlite3RubyPtr ctx;
131
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
132
+
133
+ if(!ctx->db) return Qtrue;
134
+
135
+ return Qfalse;
136
+ }
137
+
138
+ /* call-seq: total_changes
139
+ *
140
+ * Returns the total number of changes made to this database instance
141
+ * since it was opened.
142
+ */
143
+ static VALUE total_changes(VALUE self)
144
+ {
145
+ sqlite3RubyPtr ctx;
146
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
147
+ REQUIRE_OPEN_DB(ctx);
148
+
149
+ return INT2NUM((long)sqlite3_total_changes(ctx->db));
150
+ }
151
+
152
+ static void tracefunc(void * data, const char *sql)
153
+ {
154
+ VALUE self = (VALUE)data;
155
+ VALUE thing = rb_iv_get(self, "@tracefunc");
156
+ rb_funcall(thing, rb_intern("call"), 1, rb_str_new2(sql));
157
+ }
158
+
159
+ /* call-seq:
160
+ * trace { |sql| ... }
161
+ * trace(Class.new { def call sql; end }.new)
162
+ *
163
+ * Installs (or removes) a block that will be invoked for every SQL
164
+ * statement executed. The block receives one parameter: the SQL statement
165
+ * executed. If the block is +nil+, any existing tracer will be uninstalled.
166
+ */
167
+ static VALUE trace(int argc, VALUE *argv, VALUE self)
168
+ {
169
+ sqlite3RubyPtr ctx;
170
+ VALUE block;
171
+
172
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
173
+ REQUIRE_OPEN_DB(ctx);
174
+
175
+ rb_scan_args(argc, argv, "01", &block);
176
+
177
+ if(NIL_P(block) && rb_block_given_p()) block = rb_block_proc();
178
+
179
+ rb_iv_set(self, "@tracefunc", block);
180
+
181
+ sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)self);
182
+
183
+ return self;
184
+ }
185
+
186
+ static int rb_sqlite3_busy_handler(void * ctx, int count)
187
+ {
188
+ VALUE self = (VALUE)(ctx);
189
+ VALUE handle = rb_iv_get(self, "@busy_handler");
190
+ VALUE result = rb_funcall(handle, rb_intern("call"), 1, INT2NUM((long)count));
191
+
192
+ if(Qfalse == result) return 0;
193
+
194
+ return 1;
195
+ }
196
+
197
+ /* call-seq:
198
+ * busy_handler { |count| ... }
199
+ * busy_handler(Class.new { def call count; end }.new)
200
+ *
201
+ * Register a busy handler with this database instance. When a requested
202
+ * resource is busy, this handler will be invoked. If the handler returns
203
+ * +false+, the operation will be aborted; otherwise, the resource will
204
+ * be requested again.
205
+ *
206
+ * The handler will be invoked with the name of the resource that was
207
+ * busy, and the number of times it has been retried.
208
+ *
209
+ * See also the mutually exclusive #busy_timeout.
210
+ */
211
+ static VALUE busy_handler(int argc, VALUE *argv, VALUE self)
212
+ {
213
+ sqlite3RubyPtr ctx;
214
+ VALUE block;
215
+ int status;
216
+
217
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
218
+ REQUIRE_OPEN_DB(ctx);
219
+
220
+ rb_scan_args(argc, argv, "01", &block);
221
+
222
+ if(NIL_P(block) && rb_block_given_p()) block = rb_block_proc();
223
+
224
+ rb_iv_set(self, "@busy_handler", block);
225
+
226
+ status = sqlite3_busy_handler(
227
+ ctx->db, NIL_P(block) ? NULL : rb_sqlite3_busy_handler, (void *)self);
228
+
229
+ CHECK(ctx->db, status);
230
+
231
+ return self;
232
+ }
233
+
234
+ /* call-seq: last_insert_row_id
235
+ *
236
+ * Obtains the unique row ID of the last row to be inserted by this Database
237
+ * instance.
238
+ */
239
+ static VALUE last_insert_row_id(VALUE self)
240
+ {
241
+ sqlite3RubyPtr ctx;
242
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
243
+ REQUIRE_OPEN_DB(ctx);
244
+
245
+ return LL2NUM(sqlite3_last_insert_rowid(ctx->db));
246
+ }
247
+
248
+ static VALUE sqlite3val2rb(sqlite3_value * val)
249
+ {
250
+ switch(sqlite3_value_type(val)) {
251
+ case SQLITE_INTEGER:
252
+ return LL2NUM(sqlite3_value_int64(val));
253
+ break;
254
+ case SQLITE_FLOAT:
255
+ return rb_float_new(sqlite3_value_double(val));
256
+ break;
257
+ case SQLITE_TEXT:
258
+ return rb_tainted_str_new2((const char *)sqlite3_value_text(val));
259
+ break;
260
+ case SQLITE_BLOB:
261
+ return rb_tainted_str_new2((const char *)sqlite3_value_blob(val));
262
+ break;
263
+ case SQLITE_NULL:
264
+ return Qnil;
265
+ break;
266
+ default:
267
+ rb_raise(rb_eRuntimeError, "bad type"); /* FIXME */
268
+ }
269
+ }
270
+
271
+ static void set_sqlite3_func_result(sqlite3_context * ctx, VALUE result)
272
+ {
273
+ switch(TYPE(result)) {
274
+ case T_NIL:
275
+ sqlite3_result_null(ctx);
276
+ break;
277
+ case T_FIXNUM:
278
+ sqlite3_result_int64(ctx, (sqlite3_int64)FIX2LONG(result));
279
+ break;
280
+ case T_BIGNUM:
281
+ #if SIZEOF_LONG < 8
282
+ if (RBIGNUM_LEN(result) * SIZEOF_BDIGITS <= 8) {
283
+ sqlite3_result_int64(ctx, NUM2LL(result));
284
+ break;
285
+ }
286
+ #endif
287
+ case T_FLOAT:
288
+ sqlite3_result_double(ctx, NUM2DBL(result));
289
+ break;
290
+ case T_STRING:
291
+ sqlite3_result_text(
292
+ ctx,
293
+ (const char *)StringValuePtr(result),
294
+ (int)RSTRING_LEN(result),
295
+ SQLITE_TRANSIENT
296
+ );
297
+ break;
298
+ default:
299
+ rb_raise(rb_eRuntimeError, "can't return %s",
300
+ rb_class2name(CLASS_OF(result)));
301
+ }
302
+ }
303
+
304
+ static void rb_sqlite3_func(sqlite3_context * ctx, int argc, sqlite3_value **argv)
305
+ {
306
+ VALUE callable = (VALUE)sqlite3_user_data(ctx);
307
+ VALUE * params = NULL;
308
+ VALUE result;
309
+ int i;
310
+
311
+ if (argc > 0) {
312
+ params = xcalloc((size_t)argc, sizeof(VALUE *));
313
+
314
+ for(i = 0; i < argc; i++) {
315
+ params[i] = sqlite3val2rb(argv[i]);
316
+ }
317
+ }
318
+
319
+ result = rb_funcall2(callable, rb_intern("call"), argc, params);
320
+ xfree(params);
321
+
322
+ set_sqlite3_func_result(ctx, result);
323
+ }
324
+
325
+ #ifndef HAVE_RB_PROC_ARITY
326
+ int rb_proc_arity(VALUE self)
327
+ {
328
+ return (int)NUM2INT(rb_funcall(self, rb_intern("arity"), 0));
329
+ }
330
+ #endif
331
+
332
+ /* call-seq: define_function(name) { |args,...| }
333
+ *
334
+ * Define a function named +name+ with +args+. The arity of the block
335
+ * will be used as the arity for the function defined.
336
+ */
337
+ static VALUE define_function(VALUE self, VALUE name)
338
+ {
339
+ sqlite3RubyPtr ctx;
340
+ VALUE block;
341
+ int status;
342
+
343
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
344
+ REQUIRE_OPEN_DB(ctx);
345
+
346
+ block = rb_block_proc();
347
+
348
+ status = sqlite3_create_function(
349
+ ctx->db,
350
+ StringValuePtr(name),
351
+ rb_proc_arity(block),
352
+ SQLITE_UTF8,
353
+ (void *)block,
354
+ rb_sqlite3_func,
355
+ NULL,
356
+ NULL
357
+ );
358
+
359
+ CHECK(ctx->db, status);
360
+
361
+ rb_hash_aset(rb_iv_get(self, "@functions"), name, block);
362
+
363
+ return self;
364
+ }
365
+
366
+ static int sqlite3_obj_method_arity(VALUE obj, ID id)
367
+ {
368
+ VALUE method = rb_funcall(obj, rb_intern("method"), 1, ID2SYM(id));
369
+ VALUE arity = rb_funcall(method, rb_intern("arity"), 0);
370
+
371
+ return (int)NUM2INT(arity);
372
+ }
373
+
374
+ static void rb_sqlite3_step(sqlite3_context * ctx, int argc, sqlite3_value **argv)
375
+ {
376
+ VALUE callable = (VALUE)sqlite3_user_data(ctx);
377
+ VALUE * params = NULL;
378
+ int i;
379
+
380
+ if (argc > 0) {
381
+ params = xcalloc((size_t)argc, sizeof(VALUE *));
382
+ for(i = 0; i < argc; i++) {
383
+ params[i] = sqlite3val2rb(argv[i]);
384
+ }
385
+ }
386
+ rb_funcall2(callable, rb_intern("step"), argc, params);
387
+ xfree(params);
388
+ }
389
+
390
+ static void rb_sqlite3_final(sqlite3_context * ctx)
391
+ {
392
+ VALUE callable = (VALUE)sqlite3_user_data(ctx);
393
+ VALUE result = rb_funcall(callable, rb_intern("finalize"), 0);
394
+ set_sqlite3_func_result(ctx, result);
395
+ }
396
+
397
+ /* call-seq: define_aggregator(name, aggregator)
398
+ *
399
+ * Define an aggregate function named +name+ using the object +aggregator+.
400
+ * +aggregator+ must respond to +step+ and +finalize+. +step+ will be called
401
+ * with row information and +finalize+ must return the return value for the
402
+ * aggregator function.
403
+ */
404
+ static VALUE define_aggregator(VALUE self, VALUE name, VALUE aggregator)
405
+ {
406
+ sqlite3RubyPtr ctx;
407
+ int arity, status;
408
+
409
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
410
+ REQUIRE_OPEN_DB(ctx);
411
+
412
+ arity = sqlite3_obj_method_arity(aggregator, rb_intern("step"));
413
+
414
+ status = sqlite3_create_function(
415
+ ctx->db,
416
+ StringValuePtr(name),
417
+ arity,
418
+ SQLITE_UTF8,
419
+ (void *)aggregator,
420
+ NULL,
421
+ rb_sqlite3_step,
422
+ rb_sqlite3_final
423
+ );
424
+
425
+ rb_iv_set(self, "@agregator", aggregator);
426
+
427
+ CHECK(ctx->db, status);
428
+
429
+ return self;
430
+ }
431
+
432
+ /* call-seq: interrupt
433
+ *
434
+ * Interrupts the currently executing operation, causing it to abort.
435
+ */
436
+ static VALUE interrupt(VALUE self)
437
+ {
438
+ sqlite3RubyPtr ctx;
439
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
440
+ REQUIRE_OPEN_DB(ctx);
441
+
442
+ sqlite3_interrupt(ctx->db);
443
+
444
+ return self;
445
+ }
446
+
447
+ /* call-seq: errmsg
448
+ *
449
+ * Return a string describing the last error to have occurred with this
450
+ * database.
451
+ */
452
+ static VALUE errmsg(VALUE self)
453
+ {
454
+ sqlite3RubyPtr ctx;
455
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
456
+ REQUIRE_OPEN_DB(ctx);
457
+
458
+ return rb_str_new2(sqlite3_errmsg(ctx->db));
459
+ }
460
+
461
+ /* call-seq: errcode
462
+ *
463
+ * Return an integer representing the last error to have occurred with this
464
+ * database.
465
+ */
466
+ static VALUE errcode_(VALUE self)
467
+ {
468
+ sqlite3RubyPtr ctx;
469
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
470
+ REQUIRE_OPEN_DB(ctx);
471
+
472
+ return INT2NUM((long)sqlite3_errcode(ctx->db));
473
+ }
474
+
475
+ /* call-seq: complete?(sql)
476
+ *
477
+ * Return +true+ if the string is a valid (ie, parsable) SQL statement, and
478
+ * +false+ otherwise.
479
+ */
480
+ static VALUE complete_p(VALUE UNUSED(self), VALUE sql)
481
+ {
482
+ if(sqlite3_complete(StringValuePtr(sql)))
483
+ return Qtrue;
484
+
485
+ return Qfalse;
486
+ }
487
+
488
+ /* call-seq: changes
489
+ *
490
+ * Returns the number of changes made to this database instance by the last
491
+ * operation performed. Note that a "delete from table" without a where
492
+ * clause will not affect this value.
493
+ */
494
+ static VALUE changes(VALUE self)
495
+ {
496
+ sqlite3RubyPtr ctx;
497
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
498
+ REQUIRE_OPEN_DB(ctx);
499
+
500
+ return INT2NUM(sqlite3_changes(ctx->db));
501
+ }
502
+
503
+ static int rb_sqlite3_auth(
504
+ void *ctx,
505
+ int _action,
506
+ const char * _a,
507
+ const char * _b,
508
+ const char * _c,
509
+ const char * _d)
510
+ {
511
+ VALUE self = (VALUE)ctx;
512
+ VALUE action = INT2NUM(_action);
513
+ VALUE a = _a ? rb_str_new2(_a) : Qnil;
514
+ VALUE b = _b ? rb_str_new2(_b) : Qnil;
515
+ VALUE c = _c ? rb_str_new2(_c) : Qnil;
516
+ VALUE d = _d ? rb_str_new2(_d) : Qnil;
517
+ VALUE callback = rb_iv_get(self, "@authorizer");
518
+ VALUE result = rb_funcall(callback, rb_intern("call"), 5, action, a, b, c, d);
519
+
520
+ if(T_FIXNUM == TYPE(result)) return (int)NUM2INT(result);
521
+ if(Qtrue == result) return SQLITE_OK;
522
+ if(Qfalse == result) return SQLITE_DENY;
523
+
524
+ return SQLITE_IGNORE;
525
+ }
526
+
527
+ /* call-seq: set_authorizer = auth
528
+ *
529
+ * Set the authorizer for this database. +auth+ must respond to +call+, and
530
+ * +call+ must take 5 arguments.
531
+ *
532
+ * Installs (or removes) a block that will be invoked for every access
533
+ * to the database. If the block returns 0 (or +true+), the statement
534
+ * is allowed to proceed. Returning 1 or false causes an authorization error to
535
+ * occur, and returning 2 or nil causes the access to be silently denied.
536
+ */
537
+ static VALUE set_authorizer(VALUE self, VALUE authorizer)
538
+ {
539
+ sqlite3RubyPtr ctx;
540
+ int status;
541
+
542
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
543
+ REQUIRE_OPEN_DB(ctx);
544
+
545
+ status = sqlite3_set_authorizer(
546
+ ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)self
547
+ );
548
+
549
+ CHECK(ctx->db, status);
550
+
551
+ rb_iv_set(self, "@authorizer", authorizer);
552
+
553
+ return self;
554
+ }
555
+
556
+ /* call-seq: db.busy_timeout = ms
557
+ *
558
+ * Indicates that if a request for a resource terminates because that
559
+ * resource is busy, SQLite should sleep and retry for up to the indicated
560
+ * number of milliseconds. By default, SQLite does not retry
561
+ * busy resources. To restore the default behavior, send 0 as the
562
+ * +ms+ parameter.
563
+ *
564
+ * See also the mutually exclusive #busy_handler.
565
+ */
566
+ static VALUE set_busy_timeout(VALUE self, VALUE timeout)
567
+ {
568
+ sqlite3RubyPtr ctx;
569
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
570
+ REQUIRE_OPEN_DB(ctx);
571
+
572
+ CHECK(ctx->db, sqlite3_busy_timeout(ctx->db, (int)NUM2INT(timeout)));
573
+
574
+ return self;
575
+ }
576
+
577
+ int rb_comparator_func(void * ctx, int a_len, const void * a, int b_len, const void * b)
578
+ {
579
+ VALUE comparator;
580
+ VALUE a_str;
581
+ VALUE b_str;
582
+ VALUE comparison;
583
+ #ifdef HAVE_RUBY_ENCODING_H
584
+ rb_encoding * internal_encoding;
585
+
586
+ internal_encoding = rb_default_internal_encoding();
587
+ #endif
588
+
589
+ comparator = (VALUE)ctx;
590
+ a_str = rb_str_new((const char *)a, a_len);
591
+ b_str = rb_str_new((const char *)b, b_len);
592
+
593
+ #ifdef HAVE_RUBY_ENCODING_H
594
+ rb_enc_associate_index(a_str, rb_utf8_encindex());
595
+ rb_enc_associate_index(b_str, rb_utf8_encindex());
596
+
597
+ if(internal_encoding) {
598
+ a_str = rb_str_export_to_enc(a_str, internal_encoding);
599
+ b_str = rb_str_export_to_enc(b_str, internal_encoding);
600
+ }
601
+ #endif
602
+
603
+ comparison = rb_funcall(comparator, rb_intern("compare"), 2, a_str, b_str);
604
+
605
+ return NUM2INT(comparison);
606
+ }
607
+
608
+ /* call-seq: db.collation(name, comparator)
609
+ *
610
+ * Add a collation with name +name+, and a +comparator+ object. The
611
+ * +comparator+ object should implement a method called "compare" that takes
612
+ * two parameters and returns an integer less than, equal to, or greater than
613
+ * 0.
614
+ */
615
+ static VALUE collation(VALUE self, VALUE name, VALUE comparator)
616
+ {
617
+ sqlite3RubyPtr ctx;
618
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
619
+ REQUIRE_OPEN_DB(ctx);
620
+
621
+ CHECK(ctx->db, sqlite3_create_collation_v2(
622
+ ctx->db,
623
+ StringValuePtr(name),
624
+ SQLITE_UTF8,
625
+ (void *)comparator,
626
+ NIL_P(comparator) ? NULL : rb_comparator_func,
627
+ NULL));
628
+
629
+ /* Make sure our comparator doesn't get garbage collected. */
630
+ rb_hash_aset(rb_iv_get(self, "@collations"), name, comparator);
631
+
632
+ return self;
633
+ }
634
+
635
+ /* call-seq: db.load_extension(file)
636
+ *
637
+ * Loads an SQLite extension library from the named file. Extension
638
+ * loading must be enabled using db.enable_load_extension(1) prior
639
+ * to calling this API.
640
+ */
641
+ static VALUE load_extension(VALUE self, VALUE file)
642
+ {
643
+ sqlite3RubyPtr ctx;
644
+ int status;
645
+ char *errMsg;
646
+ VALUE errexp;
647
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
648
+ REQUIRE_OPEN_DB(ctx);
649
+
650
+ status = sqlite3_load_extension(ctx->db, RSTRING_PTR(file), 0, &errMsg);
651
+ if (status != SQLITE_OK)
652
+ {
653
+ errexp = rb_exc_new2(rb_eRuntimeError, errMsg);
654
+ sqlite3_free(errMsg);
655
+ rb_exc_raise(errexp);
656
+ }
657
+
658
+ return self;
659
+ }
660
+
661
+ /* call-seq: db.enable_load_extension(onoff)
662
+ *
663
+ * Enable or disable extension loading.
664
+ */
665
+ static VALUE enable_load_extension(VALUE self, VALUE onoff)
666
+ {
667
+ sqlite3RubyPtr ctx;
668
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
669
+ REQUIRE_OPEN_DB(ctx);
670
+
671
+ CHECK(ctx->db, sqlite3_enable_load_extension(ctx->db, (int)NUM2INT(onoff)));
672
+
673
+ return self;
674
+ }
675
+
676
+ #ifdef HAVE_RUBY_ENCODING_H
677
+ static int enc_cb(void * _self, int UNUSED(columns), char **data, char **UNUSED(names))
678
+ {
679
+ VALUE self = (VALUE)_self;
680
+
681
+ int index = rb_enc_find_index(data[0]);
682
+ rb_encoding * e = rb_enc_from_index(index);
683
+ rb_iv_set(self, "@encoding", rb_enc_from_encoding(e));
684
+
685
+ return 0;
686
+ }
687
+ #else
688
+ static int enc_cb(void * _self, int UNUSED(columns), char **data, char **UNUSED(names))
689
+ {
690
+ VALUE self = (VALUE)_self;
691
+
692
+ rb_iv_set(self, "@encoding", rb_str_new2(data[0]));
693
+
694
+ return 0;
695
+ }
696
+ #endif
697
+
698
+ /* call-seq: db.encoding
699
+ *
700
+ * Fetch the encoding set on this database
701
+ */
702
+ static VALUE db_encoding(VALUE self)
703
+ {
704
+ sqlite3RubyPtr ctx;
705
+ VALUE enc;
706
+
707
+ Data_Get_Struct(self, sqlite3Ruby, ctx);
708
+ REQUIRE_OPEN_DB(ctx);
709
+
710
+ enc = rb_iv_get(self, "@encoding");
711
+
712
+ if(NIL_P(enc)) {
713
+ sqlite3_exec(ctx->db, "PRAGMA encoding", enc_cb, (void *)self, NULL);
714
+ }
715
+
716
+ return rb_iv_get(self, "@encoding");
717
+ }
718
+
719
+ void init_sqlite3_database()
720
+ {
721
+ ID id_utf16, id_results_as_hash, id_type_translation;
722
+ #if 0
723
+ VALUE mSqlite3 = rb_define_module("SQLite3");
724
+ #endif
725
+ cSqlite3Database = rb_define_class_under(mSqlite3, "Database", rb_cObject);
726
+
727
+ rb_define_alloc_func(cSqlite3Database, allocate);
728
+ rb_define_method(cSqlite3Database, "initialize", initialize, -1);
729
+ rb_define_method(cSqlite3Database, "collation", collation, 2);
730
+ rb_define_method(cSqlite3Database, "close", sqlite3_rb_close, 0);
731
+ rb_define_method(cSqlite3Database, "closed?", closed_p, 0);
732
+ rb_define_method(cSqlite3Database, "total_changes", total_changes, 0);
733
+ rb_define_method(cSqlite3Database, "trace", trace, -1);
734
+ rb_define_method(cSqlite3Database, "last_insert_row_id", last_insert_row_id, 0);
735
+ rb_define_method(cSqlite3Database, "define_function", define_function, 1);
736
+ rb_define_method(cSqlite3Database, "define_aggregator", define_aggregator, 2);
737
+ rb_define_method(cSqlite3Database, "interrupt", interrupt, 0);
738
+ rb_define_method(cSqlite3Database, "errmsg", errmsg, 0);
739
+ rb_define_method(cSqlite3Database, "errcode", errcode_, 0);
740
+ rb_define_method(cSqlite3Database, "complete?", complete_p, 1);
741
+ rb_define_method(cSqlite3Database, "changes", changes, 0);
742
+ rb_define_method(cSqlite3Database, "authorizer=", set_authorizer, 1);
743
+ rb_define_method(cSqlite3Database, "busy_handler", busy_handler, -1);
744
+ rb_define_method(cSqlite3Database, "busy_timeout=", set_busy_timeout, 1);
745
+
746
+ #ifdef HAVE_SQLITE3_LOAD_EXTENSION
747
+ rb_define_method(cSqlite3Database, "load_extension", load_extension, 1);
748
+ #endif
749
+
750
+ #ifdef HAVE_SQLITE3_ENABLE_LOAD_EXTENSION
751
+ rb_define_method(cSqlite3Database, "enable_load_extension", enable_load_extension, 1);
752
+ #endif
753
+
754
+ rb_define_method(cSqlite3Database, "encoding", db_encoding, 0);
755
+
756
+ id_utf16 = rb_intern("utf16");
757
+ sym_utf16 = ID2SYM(id_utf16);
758
+ id_results_as_hash = rb_intern("results_as_hash");
759
+ sym_results_as_hash = ID2SYM(id_results_as_hash);
760
+ id_type_translation = rb_intern("type_translation");
761
+ sym_type_translation = ID2SYM(id_type_translation);
762
+ }