amalgalite 1.6.0-x64-mingw32

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