amalgalite 0.10.1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
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
+