amalgalite 0.10.1-x86-mingw32

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.
Files changed (100) hide show
  1. data/HISTORY +201 -0
  2. data/LICENSE +29 -0
  3. data/README +51 -0
  4. data/bin/amalgalite-pack +126 -0
  5. data/examples/a.rb +9 -0
  6. data/examples/blob.rb +88 -0
  7. data/examples/bootstrap.rb +36 -0
  8. data/examples/define_aggregate.rb +75 -0
  9. data/examples/define_function.rb +104 -0
  10. data/examples/gem-db.rb +94 -0
  11. data/examples/gems.db +0 -0
  12. data/examples/require_me.rb +11 -0
  13. data/examples/requires.rb +42 -0
  14. data/examples/schema-info.rb +34 -0
  15. data/ext/amalgalite/amalgalite3.c +290 -0
  16. data/ext/amalgalite/amalgalite3.h +151 -0
  17. data/ext/amalgalite/amalgalite3_blob.c +240 -0
  18. data/ext/amalgalite/amalgalite3_constants.c +221 -0
  19. data/ext/amalgalite/amalgalite3_database.c +1148 -0
  20. data/ext/amalgalite/amalgalite3_requires_bootstrap.c +210 -0
  21. data/ext/amalgalite/amalgalite3_statement.c +639 -0
  22. data/ext/amalgalite/extconf.rb +36 -0
  23. data/ext/amalgalite/gen_constants.rb +130 -0
  24. data/ext/amalgalite/sqlite3.c +106729 -0
  25. data/ext/amalgalite/sqlite3.h +5626 -0
  26. data/ext/amalgalite/sqlite3_options.h +4 -0
  27. data/ext/amalgalite/sqlite3ext.h +380 -0
  28. data/gemspec.rb +60 -0
  29. data/lib/amalgalite.rb +43 -0
  30. data/lib/amalgalite/1.8/amalgalite3.so +0 -0
  31. data/lib/amalgalite/1.9/amalgalite3.so +0 -0
  32. data/lib/amalgalite/aggregate.rb +67 -0
  33. data/lib/amalgalite/blob.rb +186 -0
  34. data/lib/amalgalite/boolean.rb +42 -0
  35. data/lib/amalgalite/busy_timeout.rb +47 -0
  36. data/lib/amalgalite/column.rb +97 -0
  37. data/lib/amalgalite/core_ext/kernel/require.rb +21 -0
  38. data/lib/amalgalite/database.rb +947 -0
  39. data/lib/amalgalite/function.rb +61 -0
  40. data/lib/amalgalite/index.rb +43 -0
  41. data/lib/amalgalite/packer.rb +226 -0
  42. data/lib/amalgalite/paths.rb +70 -0
  43. data/lib/amalgalite/profile_tap.rb +131 -0
  44. data/lib/amalgalite/progress_handler.rb +21 -0
  45. data/lib/amalgalite/requires.rb +120 -0
  46. data/lib/amalgalite/schema.rb +191 -0
  47. data/lib/amalgalite/sqlite3.rb +6 -0
  48. data/lib/amalgalite/sqlite3/constants.rb +80 -0
  49. data/lib/amalgalite/sqlite3/database/function.rb +48 -0
  50. data/lib/amalgalite/sqlite3/database/status.rb +68 -0
  51. data/lib/amalgalite/sqlite3/status.rb +60 -0
  52. data/lib/amalgalite/sqlite3/version.rb +37 -0
  53. data/lib/amalgalite/statement.rb +414 -0
  54. data/lib/amalgalite/table.rb +90 -0
  55. data/lib/amalgalite/taps.rb +2 -0
  56. data/lib/amalgalite/taps/console.rb +27 -0
  57. data/lib/amalgalite/taps/io.rb +71 -0
  58. data/lib/amalgalite/trace_tap.rb +35 -0
  59. data/lib/amalgalite/type_map.rb +63 -0
  60. data/lib/amalgalite/type_maps/default_map.rb +167 -0
  61. data/lib/amalgalite/type_maps/storage_map.rb +40 -0
  62. data/lib/amalgalite/type_maps/text_map.rb +22 -0
  63. data/lib/amalgalite/version.rb +37 -0
  64. data/lib/amalgalite/view.rb +26 -0
  65. data/spec/aggregate_spec.rb +169 -0
  66. data/spec/amalgalite_spec.rb +4 -0
  67. data/spec/blob_spec.rb +81 -0
  68. data/spec/boolean_spec.rb +23 -0
  69. data/spec/busy_handler.rb +165 -0
  70. data/spec/database_spec.rb +494 -0
  71. data/spec/default_map_spec.rb +87 -0
  72. data/spec/function_spec.rb +94 -0
  73. data/spec/integeration_spec.rb +111 -0
  74. data/spec/packer_spec.rb +60 -0
  75. data/spec/paths_spec.rb +28 -0
  76. data/spec/progress_handler_spec.rb +105 -0
  77. data/spec/requires_spec.rb +23 -0
  78. data/spec/rtree_spec.rb +71 -0
  79. data/spec/schema_spec.rb +120 -0
  80. data/spec/spec_helper.rb +27 -0
  81. data/spec/sqlite3/constants_spec.rb +65 -0
  82. data/spec/sqlite3/database_status_spec.rb +36 -0
  83. data/spec/sqlite3/status_spec.rb +18 -0
  84. data/spec/sqlite3/version_spec.rb +14 -0
  85. data/spec/sqlite3_spec.rb +53 -0
  86. data/spec/statement_spec.rb +161 -0
  87. data/spec/storage_map_spec.rb +41 -0
  88. data/spec/tap_spec.rb +59 -0
  89. data/spec/text_map_spec.rb +23 -0
  90. data/spec/type_map_spec.rb +17 -0
  91. data/spec/version_spec.rb +15 -0
  92. data/tasks/announce.rake +43 -0
  93. data/tasks/config.rb +107 -0
  94. data/tasks/distribution.rake +77 -0
  95. data/tasks/documentation.rake +32 -0
  96. data/tasks/extension.rake +141 -0
  97. data/tasks/rspec.rake +33 -0
  98. data/tasks/rubyforge.rake +59 -0
  99. data/tasks/utils.rb +80 -0
  100. metadata +237 -0
@@ -0,0 +1,1148 @@
1
+ #include "amalgalite3.h"
2
+ /**
3
+ * Copyright (c) 2008 Jeremy Hinegardner
4
+ * All rights reserved. See LICENSE and/or COPYING for details.
5
+ *
6
+ * vim: shiftwidth=4
7
+ */
8
+
9
+ VALUE cAS_Database; /* class Amalgalite::SQLite3::Database */
10
+ VALUE cAS_Database_Stat; /* class Amalgalite::SQLite3::Database::Stat */
11
+
12
+ /**
13
+ * Document-method: open
14
+ *
15
+ * call-seq:
16
+ * Amalgalite::SQLite3::Database.open( filename, flags = READWRITE | CREATE ) -> Database
17
+ *
18
+ * Create a new SQLite2 database with a UTF-8 encoding.
19
+ *
20
+ */
21
+ VALUE am_sqlite3_database_open(int argc, VALUE *argv, VALUE class)
22
+ {
23
+ VALUE self = am_sqlite3_database_alloc(class);
24
+ VALUE rFlags;
25
+ VALUE rFilename;
26
+ int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
27
+ char* filename;
28
+ int rc;
29
+ am_sqlite3* am_db;
30
+
31
+ /* at least a filename argument is required */
32
+ rb_scan_args( argc, argv, "11", &rFilename, &rFlags );
33
+
34
+ /* convert flags to the sqlite version */
35
+ flags = ( Qnil == rFlags ) ? flags : FIX2INT(rFlags);
36
+ filename = StringValuePtr(rFilename);
37
+
38
+ /* extract the sqlite3 wrapper struct */
39
+ Data_Get_Struct(self, am_sqlite3, am_db);
40
+
41
+ /* open the sqlite3 database */
42
+ rc = sqlite3_open_v2( filename, &(am_db->db), flags, 0);
43
+ if ( SQLITE_OK != rc ) {
44
+ rb_raise(eAS_Error, "Failure to open database %s : [SQLITE_ERROR %d] : %s\n",
45
+ filename, rc, sqlite3_errmsg(am_db->db));
46
+ }
47
+
48
+ /* by default turn on the extended result codes */
49
+ rc = sqlite3_extended_result_codes( am_db->db, 1);
50
+ if ( SQLITE_OK != rc ) {
51
+ rb_raise(eAS_Error, "Failure to set extended result codes %s : [SQLITE_ERROR %d] : %s\n",
52
+ filename, rc, sqlite3_errmsg(am_db->db));
53
+ }
54
+
55
+ return self;
56
+ }
57
+
58
+ /**
59
+ * call-seq:
60
+ * Amalgalite::SQLite3::Database.open16( filename ) -> SQLite3::Database
61
+ *
62
+ * Create a new SQLite3 database with a UTF-16 encoding
63
+ *
64
+ */
65
+ VALUE am_sqlite3_database_open16(VALUE class, VALUE rFilename)
66
+ {
67
+ VALUE self = am_sqlite3_database_alloc(class);
68
+ char* filename = StringValuePtr(rFilename);
69
+ am_sqlite3* am_db;
70
+ int rc;
71
+
72
+ Data_Get_Struct(self, am_sqlite3, am_db);
73
+ rc = sqlite3_open16( filename, &(am_db->db) );
74
+ if ( SQLITE_OK != rc ) {
75
+ rb_raise(eAS_Error, "Failure to open UTF-16 database %s : [SQLITE_ERROR %d] : %s\n",
76
+ filename, rc, sqlite3_errmsg( am_db->db ));
77
+ }
78
+
79
+ /* by default turn on the extended result codes */
80
+ rc = sqlite3_extended_result_codes( am_db->db, 1);
81
+ if ( SQLITE_OK != rc ) {
82
+ rb_raise(eAS_Error, "Failure to set extended result codes on UTF-16 database %s : [SQLITE_ERROR %d] : %s\n",
83
+ filename, rc, (char*)sqlite3_errmsg16(am_db->db));
84
+ }
85
+
86
+ return self;
87
+ }
88
+
89
+ /**
90
+ * call-seq:
91
+ * database.close
92
+ *
93
+ * Close the database
94
+ */
95
+ VALUE am_sqlite3_database_close(VALUE self)
96
+ {
97
+ am_sqlite3 *am_db;
98
+ int rc = 0;
99
+
100
+ Data_Get_Struct(self, am_sqlite3, am_db);
101
+ rc = sqlite3_close( am_db->db );
102
+ if ( SQLITE_OK != rc ) {
103
+ rb_raise(eAS_Error, "Failure to close database : [SQLITE_ERROR %d] : %s\n",
104
+ rc, sqlite3_errmsg( am_db->db ));
105
+ }
106
+
107
+ return self;
108
+
109
+ }
110
+
111
+ /**
112
+ * call-seq:
113
+ * database.last_insert_rowid -> Integer
114
+ *
115
+ * Return the rowid of the last row inserted into the database from this
116
+ * database connection.
117
+ */
118
+ VALUE am_sqlite3_database_last_insert_rowid(VALUE self)
119
+ {
120
+ am_sqlite3 *am_db;
121
+ sqlite3_int64 last_id;
122
+
123
+ Data_Get_Struct(self, am_sqlite3, am_db);
124
+ last_id = sqlite3_last_insert_rowid( am_db->db );
125
+
126
+ return SQLINT64_2NUM( last_id );
127
+ }
128
+
129
+ /**
130
+ * call-seq:
131
+ * database.autocommit? -> true or false
132
+ *
133
+ * return true if the database is in autocommit mode, otherwise return false
134
+ *
135
+ */
136
+ VALUE am_sqlite3_database_is_autocommit(VALUE self)
137
+ {
138
+ am_sqlite3 *am_db;
139
+ int rc;
140
+
141
+ Data_Get_Struct(self, am_sqlite3, am_db);
142
+ rc = sqlite3_get_autocommit( am_db->db );
143
+
144
+ return ( 0 == rc ) ? Qfalse : Qtrue ;
145
+ }
146
+
147
+ /**
148
+ * call-seq:
149
+ * database.row_changes -> Integer
150
+ *
151
+ * return the number of rows changed with the most recent INSERT, UPDATE or
152
+ * DELETE statement.
153
+ *
154
+ */
155
+ VALUE am_sqlite3_database_row_changes(VALUE self)
156
+ {
157
+ am_sqlite3 *am_db;
158
+ int rc;
159
+
160
+ Data_Get_Struct(self, am_sqlite3, am_db);
161
+ rc = sqlite3_changes( am_db->db );
162
+
163
+ return INT2FIX(rc);
164
+ }
165
+
166
+ /**
167
+ * call-seq:
168
+ * database.last_error_code -> Integer
169
+ *
170
+ * return the last error code that happened in the database
171
+ *
172
+ */
173
+ VALUE am_sqlite3_database_last_error_code(VALUE self)
174
+ {
175
+ am_sqlite3 *am_db;
176
+ int code;
177
+
178
+ Data_Get_Struct(self, am_sqlite3, am_db);
179
+ code = sqlite3_errcode( am_db->db );
180
+
181
+ return INT2FIX( code );
182
+ }
183
+
184
+ /**
185
+ * call-seq:
186
+ * database.last_error_message -> String
187
+ *
188
+ * return the last error message that happened in the database
189
+ *
190
+ */
191
+ VALUE am_sqlite3_database_last_error_message(VALUE self)
192
+ {
193
+ am_sqlite3 *am_db;
194
+ const char *message;
195
+
196
+ Data_Get_Struct(self, am_sqlite3, am_db);
197
+ message = sqlite3_errmsg( am_db->db );
198
+
199
+ return rb_str_new2( message );
200
+ }
201
+
202
+ /**
203
+ * call-seq:
204
+ * database.total_changes -> Integer
205
+ *
206
+ * return the number of rows changed by INSERT, UPDATE or DELETE statements
207
+ * in the database connection since the connection was opened.
208
+ *
209
+ */
210
+ VALUE am_sqlite3_database_total_changes(VALUE self)
211
+ {
212
+ am_sqlite3 *am_db;
213
+ int rc;
214
+
215
+ Data_Get_Struct(self, am_sqlite3, am_db);
216
+ rc = sqlite3_total_changes( am_db->db );
217
+
218
+ return INT2FIX(rc);
219
+ }
220
+
221
+ /*
222
+ * call-seq:
223
+ * stat.update!( reset = false ) -> nil
224
+ *
225
+ * Populates the _@current_ and _@higwater_ instance variables of the given
226
+ * Database Stat object with the values from the sqlite3_db_status call.
227
+ * If reset it true then the highwater mark for the stat is reset
228
+ *
229
+ */
230
+ VALUE am_sqlite3_database_stat_update_bang( int argc, VALUE *argv, VALUE self )
231
+ {
232
+ int current = -1;
233
+ int highwater = -1;
234
+ int reset_flag = 0;
235
+ int status_op = FIX2INT( rb_iv_get( self, "@code" ) );
236
+ int rc;
237
+
238
+ am_sqlite3 *am_db;
239
+
240
+ VALUE reset = Qfalse;
241
+ VALUE db = rb_iv_get( self, "@api_db" );
242
+
243
+ Data_Get_Struct(db, am_sqlite3, am_db);
244
+
245
+ if ( argc > 0 ) {
246
+ reset = argv[0];
247
+ reset_flag = ( Qtrue == reset ) ? 1 : 0 ;
248
+ }
249
+
250
+ rc = sqlite3_db_status( am_db->db, status_op, &current, &highwater, reset_flag );
251
+
252
+ if ( SQLITE_OK != rc ) {
253
+ VALUE n = rb_iv_get( self, "@name");
254
+ char* name = StringValuePtr( n );
255
+ rb_raise(eAS_Error, "Failure to retrieve database status for %s : [SQLITE_ERROR %d] \n", name, rc);
256
+ }
257
+
258
+ rb_iv_set( self, "@current", INT2NUM( current ) );
259
+ rb_iv_set( self, "@highwater", INT2NUM( highwater) );
260
+
261
+ return Qnil;
262
+ }
263
+
264
+
265
+
266
+ /**
267
+ * call-seq:
268
+ * database.prepare( sql ) -> SQLite3::Statement
269
+ *
270
+ * Create a new SQLite3 statement.
271
+ */
272
+ VALUE am_sqlite3_database_prepare(VALUE self, VALUE rSQL)
273
+ {
274
+ VALUE sql = StringValue( rSQL );
275
+ VALUE stmt = am_sqlite3_statement_alloc(cAS_Statement);
276
+ am_sqlite3 *am_db;
277
+ am_sqlite3_stmt *am_stmt;
278
+ const char *tail;
279
+ int rc;
280
+
281
+ Data_Get_Struct(self, am_sqlite3, am_db);
282
+
283
+ Data_Get_Struct(stmt, am_sqlite3_stmt, am_stmt);
284
+ rc = sqlite3_prepare_v2( am_db->db, RSTRING_PTR(sql), RSTRING_LEN(sql),
285
+ &(am_stmt->stmt), &tail);
286
+ if ( SQLITE_OK != rc) {
287
+ rb_raise(eAS_Error, "Failure to prepare statement %s : [SQLITE_ERROR %d] : %s\n",
288
+ RSTRING_PTR(sql), rc, sqlite3_errmsg(am_db->db));
289
+ am_sqlite3_statement_free( am_stmt );
290
+ }
291
+
292
+ if ( tail != NULL ) {
293
+ am_stmt->remaining_sql = rb_str_new2( tail );
294
+ rb_gc_register_address( &(am_stmt->remaining_sql) );
295
+ } else {
296
+ am_stmt->remaining_sql = Qnil;
297
+ }
298
+
299
+ return stmt;
300
+ }
301
+
302
+ /**
303
+ * This function is registered with a sqlite3 database using the sqlite3_trace
304
+ * function. During the registration process a handle on a VALUE is also
305
+ * registered.
306
+ *
307
+ * When this function is called, it calls the 'trace' method on the tap object,
308
+ * which is the VALUE that was registered during the sqlite3_trace call.
309
+ *
310
+ * This function corresponds to the SQLite xTrace function specification.
311
+ *
312
+ */
313
+ void amalgalite_xTrace(void* tap, const char* msg)
314
+ {
315
+ VALUE trace_obj = (VALUE) tap;
316
+
317
+ rb_funcall( trace_obj, rb_intern("trace"), 1, rb_str_new2( msg ) );
318
+ return;
319
+ }
320
+
321
+
322
+ /**
323
+ * call-seq:
324
+ * database.register_trace_tap( tap_obj )
325
+ *
326
+ * This registers an object to be called with every trace event in SQLite.
327
+ *
328
+ * This is an experimental api and is subject to change, or removal.
329
+ *
330
+ */
331
+ VALUE am_sqlite3_database_register_trace_tap(VALUE self, VALUE tap)
332
+ {
333
+ am_sqlite3 *am_db;
334
+
335
+ Data_Get_Struct(self, am_sqlite3, am_db);
336
+
337
+ /* Qnil, unregister the item and tell the garbage collector we are done with
338
+ * it.
339
+ */
340
+ if ( Qnil == tap ) {
341
+
342
+ sqlite3_trace( am_db->db, NULL, NULL );
343
+ rb_gc_unregister_address( &(am_db->trace_obj) );
344
+ am_db->trace_obj = Qnil;
345
+
346
+ /* register the item and store the reference to the object in the am_db
347
+ * structure. We also have to tell the Ruby garbage collector that we
348
+ * point to the Ruby object from C.
349
+ */
350
+ } else {
351
+
352
+ am_db->trace_obj = tap;
353
+ rb_gc_register_address( &(am_db->trace_obj) );
354
+ sqlite3_trace( am_db->db, amalgalite_xTrace, (void *)am_db->trace_obj );
355
+ }
356
+
357
+ return Qnil;
358
+ }
359
+
360
+
361
+ /**
362
+ * the amagliate trace function to be registered with register_trace_tap
363
+ * When it is called, it calls the 'trace' method on the tap object.
364
+ *
365
+ * This function conforms to the sqlite3 xProfile function specification.
366
+ */
367
+ void amalgalite_xProfile(void* tap, const char* msg, sqlite3_uint64 time)
368
+ {
369
+ VALUE trace_obj = (VALUE) tap;
370
+
371
+ rb_funcall( trace_obj, rb_intern("profile"),
372
+ 2, rb_str_new2( msg ), SQLUINT64_2NUM(time) );
373
+
374
+ return;
375
+ }
376
+
377
+ /**
378
+ * call-seq:
379
+ * database.register_profile_tap( tap_obj )
380
+ *
381
+ * This registers an object to be called with every profile event in SQLite.
382
+ *
383
+ * This is an experimental api and is subject to change or removal.
384
+ *
385
+ */
386
+ VALUE am_sqlite3_database_register_profile_tap(VALUE self, VALUE tap)
387
+ {
388
+ am_sqlite3 *am_db;
389
+
390
+ Data_Get_Struct(self, am_sqlite3, am_db);
391
+
392
+ /* Qnil, unregister the item and tell the garbage collector we are done with
393
+ * it.
394
+ */
395
+
396
+ if ( tap == Qnil ) {
397
+ sqlite3_profile( am_db->db, NULL, NULL );
398
+ rb_gc_unregister_address( &(am_db->profile_obj) );
399
+ am_db->profile_obj = Qnil;
400
+
401
+ /* register the item and store the reference to the object in the am_db
402
+ * structure. We also have to tell the Ruby garbage collector that we
403
+ * point to the Ruby object from C.
404
+ */
405
+ } else {
406
+ am_db->profile_obj = tap;
407
+ rb_gc_register_address( &(am_db->profile_obj) );
408
+ sqlite3_profile( am_db->db, amalgalite_xProfile, (void *)am_db->profile_obj );
409
+ }
410
+ return Qnil;
411
+ }
412
+
413
+ /**
414
+ * invoke a ruby function. This is here to be used by rb_protect.
415
+ */
416
+ VALUE amalgalite_wrap_funcall2( VALUE arg )
417
+ {
418
+ am_protected_t *protected = (am_protected_t*) arg;
419
+ return rb_funcall2( protected->instance, protected->method, protected->argc, protected->argv );
420
+ }
421
+
422
+ /**
423
+ * Set the context result on the sqlite3_context based upon the ruby VALUE.
424
+ * This converts the ruby value to the appropriate C-type and makes the
425
+ * appropriate call sqlite3_result_* call
426
+ */
427
+ void amalgalite_set_context_result( sqlite3_context* context, VALUE result )
428
+ {
429
+ switch( TYPE(result) ) {
430
+ case T_FIXNUM:
431
+ case T_BIGNUM:
432
+ sqlite3_result_int64( context, NUM2SQLINT64(result) );
433
+ break;
434
+ case T_FLOAT:
435
+ sqlite3_result_double( context, NUM2DBL(result) );
436
+ break;
437
+ case T_NIL:
438
+ sqlite3_result_null( context );
439
+ break;
440
+ case T_TRUE:
441
+ sqlite3_result_int64( context, 1);
442
+ break;
443
+ case T_FALSE:
444
+ sqlite3_result_int64( context, 0);
445
+ break;
446
+ case T_STRING:
447
+ sqlite3_result_text( context, RSTRING_PTR(result), RSTRING_LEN(result), NULL);
448
+ break;
449
+ default:
450
+ sqlite3_result_error( context, "Unable to convert ruby object to an SQL function result", -1 );
451
+ sqlite3_result_error_code( context, 42 );
452
+ break;
453
+ }
454
+ return;
455
+ }
456
+
457
+ /**
458
+ * Convert from a protected sqlite3_value to a ruby object
459
+ */
460
+ VALUE sqlite3_value_to_ruby_value( sqlite3_value* s_value )
461
+ {
462
+ VALUE rb_value = Qnil;
463
+ sqlite3_int64 i64;
464
+
465
+ switch( sqlite3_value_type( s_value) ) {
466
+ case SQLITE_NULL:
467
+ rb_value = Qnil;
468
+ break;
469
+ case SQLITE_INTEGER:
470
+ i64 = sqlite3_value_int64( s_value);
471
+ rb_value = SQLINT64_2NUM(i64);
472
+ break;
473
+ case SQLITE_FLOAT:
474
+ rb_value = rb_float_new( sqlite3_value_double( s_value ) );
475
+ break;
476
+ case SQLITE_TEXT:
477
+ case SQLITE_BLOB:
478
+ rb_value = rb_str_new2((const char*) sqlite3_value_text( s_value ) );
479
+ break;
480
+ }
481
+ return rb_value;
482
+ }
483
+
484
+
485
+ /**
486
+ * the amalgalite xBusy handler that is used to invoke the ruby function for
487
+ * doing busy callbacks.
488
+ *
489
+ * This function conforms to the xBusy function specification for
490
+ * sqlite3_busy_handler.
491
+ */
492
+ int amalgalite_xBusy( void *pArg , int nArg)
493
+ {
494
+ VALUE *args = ALLOCA_N( VALUE, 1 );
495
+ VALUE result = Qnil;
496
+ int state;
497
+ int busy = 1;
498
+ am_protected_t protected;
499
+
500
+ args[0] = INT2FIX(nArg);
501
+
502
+ protected.instance = (VALUE)pArg;
503
+ protected.method = rb_intern("call");
504
+ protected.argc = 1;
505
+ protected.argv = args;
506
+
507
+ result = rb_protect( amalgalite_wrap_funcall2, (VALUE)&protected, &state );
508
+ if ( state || ( Qnil == result || Qfalse == result ) ){
509
+ busy = 0;
510
+ }
511
+ return busy;
512
+ }
513
+
514
+
515
+ /**
516
+ * call-seq:
517
+ * database.busy_handler( proc_like or nil )
518
+ *
519
+ * register a busy handler. If the argument is nil, then an existing busy
520
+ * handler is removed. Otherwise the argument is registered as the busy
521
+ * handler.
522
+ */
523
+ VALUE am_sqlite3_database_busy_handler( VALUE self, VALUE handler )
524
+ {
525
+ am_sqlite3 *am_db;
526
+ int rc;
527
+
528
+ Data_Get_Struct(self, am_sqlite3, am_db);
529
+
530
+ /* Removing a busy handler case, remove it from sqlite and then remove it
531
+ * from the garbage collector if it existed */
532
+ if ( Qnil == handler ) {
533
+ rc = sqlite3_busy_handler( am_db->db, NULL, NULL );
534
+ if ( SQLITE_OK != rc ) {
535
+ rb_raise(eAS_Error, "Failure removing busy handler : [SQLITE_ERROR %d] : %s\n",
536
+ rc, sqlite3_errmsg( am_db->db ));
537
+ }
538
+ if ( Qnil != am_db->busy_handler_obj ) {
539
+ rb_gc_unregister_address( &(am_db->busy_handler_obj) );
540
+ }
541
+ } else {
542
+ /* installing a busy handler
543
+ * - register it with sqlite
544
+ * - keep a reference for ourselves with our database handle
545
+ * - registere the handler reference with the garbage collector
546
+ */
547
+ rc = sqlite3_busy_handler( am_db->db, amalgalite_xBusy, (void*)handler );
548
+ if ( SQLITE_OK != rc ) {
549
+ rb_raise(eAS_Error, "Failure setting busy handler : [SQLITE_ERROR %d] : %s\n",
550
+ rc, sqlite3_errmsg( am_db->db ));
551
+ }
552
+ am_db->busy_handler_obj = handler;
553
+ rb_gc_register_address( &(am_db->busy_handler_obj) );
554
+ }
555
+ return Qnil;
556
+ }
557
+
558
+
559
+ /**
560
+ * the amalgalite xProgress handler that is used to invoke the ruby function for
561
+ * doing progress handler callbacks.
562
+ *
563
+ * This function conforms to the xProgress function specification for
564
+ * sqlite3_progress_handler.
565
+ */
566
+ int amalgalite_xProgress( void *pArg )
567
+ {
568
+ VALUE result = Qnil;
569
+ int state;
570
+ int cancel = 0;
571
+ am_protected_t protected;
572
+
573
+ protected.instance = (VALUE)pArg;
574
+ protected.method = rb_intern("call");
575
+ protected.argc = 0;
576
+ protected.argv = NULL;
577
+
578
+ result = rb_protect( amalgalite_wrap_funcall2, (VALUE)&protected, &state );
579
+ if ( state || ( Qnil == result || Qfalse == result ) ){
580
+ cancel = 1;
581
+ }
582
+ return cancel;
583
+ }
584
+
585
+
586
+ /**
587
+ * call-seq:
588
+ * database.progress_handler( op_count, proc_like or nil )
589
+ *
590
+ * register a progress handler. If the argument is nil, then an existing
591
+ * progress handler is removed. Otherwise the argument is registered as the
592
+ * progress handler.
593
+ */
594
+ VALUE am_sqlite3_database_progress_handler( VALUE self, VALUE op_count, VALUE handler )
595
+ {
596
+ am_sqlite3 *am_db;
597
+
598
+ Data_Get_Struct(self, am_sqlite3, am_db);
599
+
600
+ /* Removing a progress handler, remove it from sqlite and then remove it
601
+ * from the garbage collector if it existed */
602
+ if ( Qnil == handler ) {
603
+ sqlite3_progress_handler( am_db->db, -1, NULL, (void*)NULL );
604
+ if ( Qnil != am_db->progress_handler_obj ) {
605
+ rb_gc_unregister_address( &(am_db->progress_handler_obj) );
606
+ }
607
+ } else {
608
+ int op_codes = FIX2INT( op_count );
609
+ /* installing a progress handler
610
+ * - register it with sqlite
611
+ * - keep a reference for ourselves with our database handle
612
+ * - register the handler reference with the garbage collector
613
+ */
614
+ sqlite3_progress_handler( am_db->db, op_codes, amalgalite_xProgress, (void*)handler );
615
+ am_db->progress_handler_obj = handler;
616
+ rb_gc_register_address( &(am_db->progress_handler_obj) );
617
+ }
618
+ return Qnil;
619
+ }
620
+
621
+
622
+ /**
623
+ * the amalgalite xFunc callback that is used to invoke the ruby function for
624
+ * doing scalar SQL functions.
625
+ *
626
+ * This function conforms to the xFunc function specification for
627
+ * sqlite3_create_function
628
+ */
629
+ void amalgalite_xFunc( sqlite3_context* context, int argc, sqlite3_value** argv )
630
+ {
631
+ VALUE *args = ALLOCA_N( VALUE, argc );
632
+ VALUE result;
633
+ int state;
634
+ int i;
635
+ am_protected_t protected;
636
+
637
+ /* convert each item in argv to a VALUE object based upon its type via
638
+ * sqlite3_value_type( argv[n] )
639
+ */
640
+ for( i = 0 ; i < argc ; i++) {
641
+ args[i] = sqlite3_value_to_ruby_value( argv[i] );
642
+ }
643
+
644
+ /* gather all the data to make the protected call */
645
+ protected.instance = (VALUE) sqlite3_user_data( context );
646
+ protected.method = rb_intern("call");
647
+ protected.argc = argc;
648
+ protected.argv = args;
649
+
650
+ result = rb_protect( amalgalite_wrap_funcall2, (VALUE)&protected, &state );
651
+ /* check the results */
652
+ if ( state ) {
653
+ VALUE msg = ERROR_INFO_MESSAGE();
654
+ sqlite3_result_error( context, RSTRING_PTR(msg), RSTRING_LEN(msg) );
655
+ } else {
656
+ amalgalite_set_context_result( context, result );
657
+ }
658
+
659
+ return;
660
+ }
661
+
662
+ /**
663
+ * call-seq:
664
+ * database.define_function( name, proc_like )
665
+ *
666
+ * register the given function to be invoked as an sql function.
667
+ */
668
+ VALUE am_sqlite3_database_define_function( VALUE self, VALUE name, VALUE proc_like )
669
+ {
670
+ am_sqlite3 *am_db;
671
+ int rc;
672
+ VALUE arity = rb_funcall( proc_like, rb_intern( "arity" ), 0 );
673
+ char* zFunctionName = RSTRING_PTR(name);
674
+ int nArg = FIX2INT( arity );
675
+
676
+ Data_Get_Struct(self, am_sqlite3, am_db);
677
+ rc = sqlite3_create_function( am_db->db,
678
+ zFunctionName, nArg,
679
+ SQLITE_ANY,
680
+ (void *)proc_like, amalgalite_xFunc,
681
+ NULL, NULL);
682
+ if ( SQLITE_OK != rc ) {
683
+ /* in the case of SQLITE_MISUSE the error message in the database may
684
+ * not be set. In this case, hardcode the error.
685
+ * http://sqlite.org/c3ref/errcode.html
686
+ *
687
+ * This is a result of 3.6.15 which has sqlite3_create_function return
688
+ * SQLITE_MISUSE intead of SQLITE_ERROR if called with incorrect
689
+ * parameters.
690
+ */
691
+ if ( SQLITE_MISUSE == rc ) {
692
+ rb_raise(eAS_Error, "Failure defining SQL function '%s' with arity '%d' : [SQLITE_ERROR %d] : Library used incorrectly\n",
693
+ zFunctionName, nArg, rc);
694
+ } else {
695
+ rb_raise(eAS_Error, "Failure defining SQL function '%s' with arity '%d' : [SQLITE_ERROR %d] : %s\n",
696
+ zFunctionName, nArg, rc, sqlite3_errmsg( am_db->db ));
697
+ }
698
+ }
699
+ rb_gc_register_address( &proc_like );
700
+ return Qnil;
701
+ }
702
+
703
+ /**
704
+ * call-seq:
705
+ * database.remove_function( name, proc_like )
706
+ *
707
+ * remove the given function from availability in SQL.
708
+ */
709
+ VALUE am_sqlite3_database_remove_function( VALUE self, VALUE name, VALUE proc_like )
710
+ {
711
+ am_sqlite3 *am_db;
712
+ int rc;
713
+ VALUE arity = rb_funcall( proc_like, rb_intern( "arity" ), 0 );
714
+ char* zFunctionName = RSTRING_PTR(name);
715
+ int nArg = FIX2INT( arity );
716
+
717
+ Data_Get_Struct(self, am_sqlite3, am_db);
718
+ rc = sqlite3_create_function( am_db->db,
719
+ zFunctionName, nArg,
720
+ SQLITE_ANY,
721
+ NULL, NULL,
722
+ NULL, NULL);
723
+ if ( SQLITE_OK != rc ) {
724
+ rb_raise(eAS_Error, "Failure removing SQL function '%s' with arity '%d' : [SQLITE_ERROR %d] : %s\n",
725
+ zFunctionName, nArg, rc, sqlite3_errmsg( am_db->db ));
726
+ }
727
+ rb_gc_unregister_address( &proc_like );
728
+ return Qnil;
729
+ }
730
+
731
+ /* wrap rb_class_new_instance so it can be called from within an rb_protect */
732
+ VALUE amalgalite_wrap_new_aggregate( VALUE arg )
733
+ {
734
+ return rb_class_new_instance( 0, 0, arg );
735
+ }
736
+
737
+
738
+ /**
739
+ * the amalgalite xStep callback that is used to invoke the ruby method for
740
+ * doing aggregate step oprations as part of an aggregate SQL function.
741
+ *
742
+ * This function conforms to the xStep function specification for
743
+ * sqlite3_create_function.
744
+ */
745
+ void amalgalite_xStep( sqlite3_context* context, int argc, sqlite3_value** argv )
746
+ {
747
+ VALUE *args = ALLOCA_N( VALUE, argc );
748
+ VALUE result;
749
+ int state;
750
+ int i;
751
+ am_protected_t protected;
752
+ VALUE *aggregate_context = (VALUE*)sqlite3_aggregate_context( context, sizeof( VALUE ) );
753
+
754
+ if ( 0 == aggregate_context ) {
755
+ sqlite3_result_error_nomem( context );
756
+ return;
757
+ }
758
+
759
+ /* instantiate an instance of the aggregate function class if the
760
+ * aggregate context is zero'd out .
761
+ *
762
+ * If there is an error in initialization of the aggregate, set the error
763
+ * context
764
+ */
765
+ if ( *aggregate_context == T_NONE ) {
766
+ VALUE klass = (VALUE) sqlite3_user_data( context );
767
+ result = rb_protect( amalgalite_wrap_new_aggregate, klass, &state );
768
+ *aggregate_context = result;
769
+ /* mark the instance as protected from collection */
770
+ rb_gc_register_address( aggregate_context );
771
+ if ( state ) {
772
+ VALUE msg = ERROR_INFO_MESSAGE();
773
+ sqlite3_result_error( context, RSTRING_PTR(msg), RSTRING_LEN(msg));
774
+ rb_iv_set( *aggregate_context, "@_exception", rb_gv_get("$!" ));
775
+ return;
776
+ } else {
777
+ rb_iv_set( *aggregate_context, "@_exception", Qnil );
778
+ }
779
+ }
780
+
781
+ /* convert each item in argv to a VALUE object based upon its type via
782
+ * sqlite3_value_type( argv[n] )
783
+ */
784
+ for( i = 0 ; i < argc ; i++) {
785
+ args[i] = sqlite3_value_to_ruby_value( argv[i] );
786
+ }
787
+
788
+ /* gather all the data to make the protected call */
789
+ protected.instance = *aggregate_context;
790
+ protected.method = rb_intern("step");
791
+ protected.argc = argc;
792
+ protected.argv = args;
793
+
794
+ result = rb_protect( amalgalite_wrap_funcall2, (VALUE)&protected, &state );
795
+
796
+ /* check the results, if there is an error, set the @exception ivar */
797
+ if ( state ) {
798
+ VALUE msg = ERROR_INFO_MESSAGE();
799
+ sqlite3_result_error( context, RSTRING_PTR(msg), RSTRING_LEN(msg));
800
+ rb_iv_set( *aggregate_context, "@_exception", rb_gv_get("$!" ));
801
+ }
802
+
803
+ return ;
804
+ }
805
+
806
+
807
+ /**
808
+ * the amalgalite xFinal callback that is used to invoke the ruby method for
809
+ * doing aggregate final operations as part of an aggregate SQL function.
810
+ *
811
+ * This function conforms to the xFinal function specification for
812
+ * sqlite3_create_function.
813
+ */
814
+ void amalgalite_xFinal( sqlite3_context* context )
815
+ {
816
+ VALUE result;
817
+ int state;
818
+ am_protected_t protected;
819
+ VALUE *aggregate_context = (VALUE*)sqlite3_aggregate_context( context, sizeof( VALUE ) );
820
+ VALUE exception = rb_iv_get( *aggregate_context, "@_exception" );
821
+
822
+ if ( Qnil == exception ) {
823
+ /* gather all the data to make the protected call */
824
+ protected.instance = *aggregate_context;
825
+ protected.method = rb_intern("finalize");
826
+ protected.argc = 0;
827
+ protected.argv = NULL;
828
+
829
+ result = rb_protect( amalgalite_wrap_funcall2, (VALUE)&protected, &state );
830
+
831
+ /* check the results */
832
+ if ( state ) {
833
+ VALUE msg = ERROR_INFO_MESSAGE();
834
+ sqlite3_result_error( context, RSTRING_PTR(msg), RSTRING_LEN(msg) );
835
+ } else {
836
+ amalgalite_set_context_result( context, result );
837
+ }
838
+ } else {
839
+ VALUE msg = rb_obj_as_string( exception );
840
+ sqlite3_result_error( context, RSTRING_PTR(msg), RSTRING_LEN(msg) );
841
+ }
842
+
843
+
844
+
845
+ /* release the aggregate instance from garbage collector protection */
846
+ rb_gc_unregister_address( aggregate_context );
847
+
848
+ return ;
849
+ }
850
+
851
+
852
+
853
+ /**
854
+ * call-seq:
855
+ * database.define_aggregate( name, arity, klass )
856
+ *
857
+ * register the given klass to be invoked as an sql aggregate.
858
+ */
859
+ VALUE am_sqlite3_database_define_aggregate( VALUE self, VALUE name, VALUE arity, VALUE klass )
860
+ {
861
+ am_sqlite3 *am_db;
862
+ int rc;
863
+ char* zFunctionName = RSTRING_PTR(name);
864
+ int nArg = FIX2INT( arity );
865
+
866
+ Data_Get_Struct(self, am_sqlite3, am_db);
867
+ rc = sqlite3_create_function( am_db->db,
868
+ zFunctionName, nArg,
869
+ SQLITE_ANY,
870
+ (void *)klass, NULL,
871
+ amalgalite_xStep,
872
+ amalgalite_xFinal);
873
+ if ( SQLITE_OK != rc ) {
874
+ /* in the case of SQLITE_MISUSE the error message in the database may
875
+ * not be set. In this case, hardcode the error.
876
+ * http://sqlite.org/c3ref/errcode.html
877
+ *
878
+ * This is a result of 3.6.15 which has sqlite3_create_function return
879
+ * SQLITE_MISUSE intead of SQLITE_ERROR if called with incorrect
880
+ * parameters.
881
+ */
882
+ if ( SQLITE_MISUSE == rc ) {
883
+ rb_raise(eAS_Error, "Failure defining SQL function '%s' with arity '%d' : [SQLITE_ERROR %d] : Library used incorrectly\n",
884
+ zFunctionName, nArg, rc);
885
+ } else {
886
+ rb_raise(eAS_Error, "Failure defining SQL function '%s' with arity '%d' : [SQLITE_ERROR %d] : %s\n",
887
+ zFunctionName, nArg, rc, sqlite3_errmsg( am_db->db ));
888
+ }
889
+ }
890
+ rb_gc_register_address( &klass );
891
+ return Qnil;
892
+ }
893
+
894
+
895
+ /**
896
+ * call-seq:
897
+ * database.remove_aggregate( name, arity, klass )
898
+ *
899
+ * remove the given klass from availability in SQL as an aggregate.
900
+ */
901
+ VALUE am_sqlite3_database_remove_aggregate( VALUE self, VALUE name, VALUE arity, VALUE klass )
902
+ {
903
+ am_sqlite3 *am_db;
904
+ int rc;
905
+ char* zFunctionName = RSTRING_PTR(name);
906
+ int nArg = FIX2INT( arity );
907
+
908
+ Data_Get_Struct(self, am_sqlite3, am_db);
909
+ rc = sqlite3_create_function( am_db->db,
910
+ zFunctionName, nArg,
911
+ SQLITE_ANY,
912
+ NULL, NULL,
913
+ NULL,
914
+ NULL);
915
+ if ( SQLITE_OK != rc ) {
916
+ rb_raise(eAS_Error, "Failure removing SQL aggregate '%s' with arity '%d' : [SQLITE_ERROR %d] : %s\n",
917
+ zFunctionName, nArg, rc, sqlite3_errmsg( am_db->db ));
918
+ }
919
+ rb_gc_unregister_address( &klass );
920
+ return Qnil;
921
+ }
922
+
923
+
924
+ /**
925
+ * call-seq:
926
+ * database.interrupt!
927
+ *
928
+ * Cause another thread with a handle on this database to be interrupted and
929
+ * return at the earliest opportunity as interrupted.
930
+ */
931
+ VALUE am_sqlite3_database_interrupt_bang( VALUE self )
932
+ {
933
+ am_sqlite3 *am_db;
934
+
935
+ Data_Get_Struct(self, am_sqlite3, am_db);
936
+ sqlite3_interrupt( am_db->db );
937
+ return Qnil;
938
+ }
939
+
940
+ /**
941
+ * call-seq:
942
+ * database.replicate_to( other_db ) -> other_db
943
+ *
944
+ * Replicates the current database to the database passed in using the
945
+ * sqlite3_backup api
946
+ *
947
+ */
948
+ VALUE am_sqlite3_database_replicate_to( VALUE self, VALUE other )
949
+ {
950
+ am_sqlite3 *am_src_db;
951
+ am_sqlite3 *am_dest_db;
952
+
953
+ sqlite3_backup *backup;
954
+ sqlite3 *src;
955
+ sqlite3 *dest;
956
+
957
+ int rc_s;
958
+ int rc_f;
959
+
960
+ /* source database */
961
+ Data_Get_Struct(self, am_sqlite3, am_src_db);
962
+ src = am_src_db->db;
963
+
964
+ /* destination database */
965
+ Data_Get_Struct(other, am_sqlite3, am_dest_db);
966
+ dest = am_dest_db->db;
967
+
968
+ backup = sqlite3_backup_init( dest, "main", src, "main" );
969
+ if ( NULL == backup ) {
970
+ rb_raise(eAS_Error, "Failure to initialize replication: [SQLITE_ERROR %d] : %s\n",
971
+ sqlite3_errcode( dest ), sqlite3_errmsg( dest ));
972
+ }
973
+
974
+ rc_s = sqlite3_backup_step( backup, -1 ); /* copy the whole thing at once */
975
+ rc_f = sqlite3_backup_finish( backup );
976
+
977
+ /* report the rc_s error if that one is bad,
978
+ * else raise the rc_f error, or nothing */
979
+ if ( SQLITE_DONE != rc_s ) {
980
+ rb_raise(eAS_Error, "Failure in replication : [SQLITE_ERROR %d] : %s\n",
981
+ sqlite3_errcode( dest ), sqlite3_errmsg( dest ) );
982
+ } else if ( SQLITE_OK != rc_f ) {
983
+ rb_raise(eAS_Error, "Failure in finishing replication: [SQLITE_ERROR %d] : %s\n",
984
+ sqlite3_errcode( dest ), sqlite3_errmsg( dest ) );
985
+ }
986
+
987
+ return other;
988
+ }
989
+
990
+ /**
991
+ * call-seq:
992
+ * database.table_column_metadata( db_name, table_name, column_name) -> Hash
993
+ *
994
+ * Returns a hash containing the meta information about the column. The
995
+ * available keys are:
996
+ *
997
+ * declared_data_type:: the declared data type of the column
998
+ * collation_sequence_name:: the name of the collation sequence for the column
999
+ * not_null_constraint:: True if the column has a NOT NULL constraint
1000
+ * primary_key:: True if the column is part of a primary key
1001
+ * auto_increment:: True if the column is AUTO INCREMENT
1002
+ *
1003
+ */
1004
+ VALUE am_sqlite3_database_table_column_metadata(VALUE self, VALUE db_name, VALUE tbl_name, VALUE col_name)
1005
+ {
1006
+ am_sqlite3 *am_db;
1007
+ int rc;
1008
+
1009
+ /* input */
1010
+ const char *zDbName = StringValuePtr( db_name );
1011
+ const char *zTableName = StringValuePtr( tbl_name );
1012
+ const char *zColumnName = StringValuePtr( col_name );
1013
+
1014
+ /* output */
1015
+ const char *pzDataType = NULL;
1016
+ const char *pzCollSeq = NULL;
1017
+ int pNotNull, pPrimaryKey, pAutoinc;
1018
+ VALUE rHash = rb_hash_new();
1019
+ VALUE rStr = Qnil;
1020
+
1021
+ Data_Get_Struct(self, am_sqlite3, am_db);
1022
+
1023
+ rc = sqlite3_table_column_metadata( am_db->db,
1024
+ zDbName, zTableName, zColumnName,
1025
+ &pzDataType, &pzCollSeq,
1026
+ &pNotNull, &pPrimaryKey, &pAutoinc);
1027
+ if ( SQLITE_OK != rc ) {
1028
+ rb_raise(eAS_Error, "Failure retrieveing column meta data for table '%s' column '%s' : [SQLITE_ERROR %d] : %s\n",
1029
+ zTableName, zColumnName, rc, sqlite3_errmsg( am_db-> db ));
1030
+
1031
+ }
1032
+
1033
+ rStr = ( NULL == pzDataType) ? Qnil : rb_str_new2( pzDataType );
1034
+ rb_hash_aset( rHash, rb_str_new2("declared_data_type"), rStr );
1035
+
1036
+ rStr = ( NULL == pzCollSeq) ? Qnil : rb_str_new2( pzCollSeq );
1037
+ rb_hash_aset( rHash, rb_str_new2("collation_sequence_name"), rStr );
1038
+
1039
+ rb_hash_aset( rHash, rb_str_new2("not_null_constraint"), ( pNotNull ? Qtrue : Qfalse ));
1040
+ rb_hash_aset( rHash, rb_str_new2("primary_key"), ( pPrimaryKey ? Qtrue : Qfalse ));
1041
+ rb_hash_aset( rHash, rb_str_new2("auto_increment"), ( pAutoinc ? Qtrue : Qfalse ));
1042
+
1043
+ return rHash;
1044
+ }
1045
+
1046
+ /***********************************************************************
1047
+ * Ruby life cycle methods
1048
+ ***********************************************************************/
1049
+
1050
+
1051
+ /*
1052
+ * garbage collector free method for the am_data structure. Make sure to un
1053
+ * registere the trace and profile objects if they are not Qnil
1054
+ */
1055
+ void am_sqlite3_database_free(am_sqlite3* am_db)
1056
+ {
1057
+ if ( Qnil != am_db->trace_obj ) {
1058
+ rb_gc_unregister_address( &(am_db->trace_obj) );
1059
+ am_db->trace_obj = Qnil;
1060
+ }
1061
+
1062
+ if ( Qnil != am_db->profile_obj) {
1063
+ rb_gc_unregister_address( &(am_db->profile_obj) );
1064
+ am_db->profile_obj = Qnil;
1065
+ }
1066
+
1067
+ if ( Qnil != am_db->busy_handler_obj ) {
1068
+ rb_gc_unregister_address( &(am_db->busy_handler_obj) );
1069
+ am_db->busy_handler_obj = Qnil;
1070
+ }
1071
+
1072
+ if ( Qnil != am_db->progress_handler_obj ) {
1073
+ rb_gc_unregister_address( &(am_db->progress_handler_obj) );
1074
+ am_db->progress_handler_obj = Qnil;
1075
+ }
1076
+
1077
+
1078
+
1079
+ free(am_db);
1080
+ return;
1081
+ }
1082
+
1083
+ /*
1084
+ * allocate the am_data structure
1085
+ */
1086
+ VALUE am_sqlite3_database_alloc(VALUE klass)
1087
+ {
1088
+ am_sqlite3* am_db = ALLOC(am_sqlite3);
1089
+ VALUE obj ;
1090
+
1091
+ am_db->trace_obj = Qnil;
1092
+ am_db->profile_obj = Qnil;
1093
+ am_db->busy_handler_obj = Qnil;
1094
+ am_db->progress_handler_obj = Qnil;
1095
+
1096
+ obj = Data_Wrap_Struct(klass, NULL, am_sqlite3_database_free, am_db);
1097
+ return obj;
1098
+ }
1099
+
1100
+ /**
1101
+ * Document-class: Amalgalite::SQLite3::Database
1102
+ *
1103
+ * The ruby extension wrapper around the core sqlite3 database object.
1104
+ *
1105
+ */
1106
+ void Init_amalgalite3_database( )
1107
+ {
1108
+
1109
+ VALUE ma = rb_define_module("Amalgalite");
1110
+ VALUE mas = rb_define_module_under(ma, "SQLite3");
1111
+
1112
+ /*
1113
+ * Encapsulate an SQLite3 database
1114
+ */
1115
+ cAS_Database = rb_define_class_under( mas, "Database", rb_cObject);
1116
+
1117
+ rb_define_alloc_func(cAS_Database, am_sqlite3_database_alloc);
1118
+ rb_define_singleton_method(cAS_Database, "open", am_sqlite3_database_open, -1);
1119
+ rb_define_singleton_method(cAS_Database, "open16", am_sqlite3_database_open16, 1);
1120
+ rb_define_method(cAS_Database, "prepare", am_sqlite3_database_prepare, 1);
1121
+ rb_define_method(cAS_Database, "close", am_sqlite3_database_close, 0); /* in amalgalite3_database.c */
1122
+ rb_define_method(cAS_Database, "last_insert_rowid", am_sqlite3_database_last_insert_rowid, 0); /* in amalgalite3_database.c */
1123
+ rb_define_method(cAS_Database, "autocommit?", am_sqlite3_database_is_autocommit, 0); /* in amalgalite3_database.c */
1124
+ rb_define_method(cAS_Database, "register_trace_tap", am_sqlite3_database_register_trace_tap, 1); /* in amalgalite3_database.c */
1125
+ rb_define_method(cAS_Database, "register_profile_tap", am_sqlite3_database_register_profile_tap, 1); /* in amalgalite3_database.c */
1126
+ rb_define_method(cAS_Database, "table_column_metadata", am_sqlite3_database_table_column_metadata, 3); /* in amalgalite3_database.c */
1127
+ rb_define_method(cAS_Database, "row_changes", am_sqlite3_database_row_changes, 0); /* in amalgalite3_database.c */
1128
+ rb_define_method(cAS_Database, "total_changes", am_sqlite3_database_total_changes, 0); /* in amalgalite3_database.c */
1129
+ rb_define_method(cAS_Database, "last_error_code", am_sqlite3_database_last_error_code, 0); /* in amalgalite3_database.c */
1130
+ rb_define_method(cAS_Database, "last_error_message", am_sqlite3_database_last_error_message, 0); /* in amalgalite3_database.c */
1131
+ rb_define_method(cAS_Database, "define_function", am_sqlite3_database_define_function, 2); /* in amalgalite3_database.c */
1132
+ rb_define_method(cAS_Database, "remove_function", am_sqlite3_database_remove_function, 2); /* in amalgalite3_database.c */
1133
+ rb_define_method(cAS_Database, "define_aggregate", am_sqlite3_database_define_aggregate, 3); /* in amalgalite3_database.c */
1134
+ rb_define_method(cAS_Database, "remove_aggregate", am_sqlite3_database_remove_aggregate, 3); /* in amalgalite3_database.c */
1135
+ rb_define_method(cAS_Database, "busy_handler", am_sqlite3_database_busy_handler, 1); /* in amalgalite3_database.c */
1136
+ rb_define_method(cAS_Database, "progress_handler", am_sqlite3_database_progress_handler, 2); /* in amalgalite3_database.c */
1137
+ rb_define_method(cAS_Database, "interrupt!", am_sqlite3_database_interrupt_bang, 0); /* in amalgalite3_database.c */
1138
+ rb_define_method(cAS_Database, "replicate_to", am_sqlite3_database_replicate_to, 1); /* in amalgalite3_database.c */
1139
+
1140
+
1141
+ /*
1142
+ * Ecapuslate a SQLite3 Database stat
1143
+ */
1144
+ cAS_Database_Stat = rb_define_class_under( cAS_Database, "Stat", rb_cObject );
1145
+ rb_define_method(cAS_Database_Stat, "update!", am_sqlite3_database_stat_update_bang, -1); /* in amalgalite3_database.c */
1146
+
1147
+ }
1148
+