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.
- data/HISTORY +201 -0
- data/LICENSE +29 -0
- data/README +51 -0
- data/bin/amalgalite-pack +126 -0
- data/examples/a.rb +9 -0
- data/examples/blob.rb +88 -0
- data/examples/bootstrap.rb +36 -0
- data/examples/define_aggregate.rb +75 -0
- data/examples/define_function.rb +104 -0
- data/examples/gem-db.rb +94 -0
- data/examples/gems.db +0 -0
- data/examples/require_me.rb +11 -0
- data/examples/requires.rb +42 -0
- data/examples/schema-info.rb +34 -0
- data/ext/amalgalite/amalgalite3.c +290 -0
- data/ext/amalgalite/amalgalite3.h +151 -0
- data/ext/amalgalite/amalgalite3_blob.c +240 -0
- data/ext/amalgalite/amalgalite3_constants.c +221 -0
- data/ext/amalgalite/amalgalite3_database.c +1148 -0
- data/ext/amalgalite/amalgalite3_requires_bootstrap.c +210 -0
- data/ext/amalgalite/amalgalite3_statement.c +639 -0
- data/ext/amalgalite/extconf.rb +36 -0
- data/ext/amalgalite/gen_constants.rb +130 -0
- data/ext/amalgalite/sqlite3.c +106729 -0
- data/ext/amalgalite/sqlite3.h +5626 -0
- data/ext/amalgalite/sqlite3_options.h +4 -0
- data/ext/amalgalite/sqlite3ext.h +380 -0
- data/gemspec.rb +60 -0
- data/lib/amalgalite.rb +43 -0
- data/lib/amalgalite/1.8/amalgalite3.so +0 -0
- data/lib/amalgalite/1.9/amalgalite3.so +0 -0
- data/lib/amalgalite/aggregate.rb +67 -0
- data/lib/amalgalite/blob.rb +186 -0
- data/lib/amalgalite/boolean.rb +42 -0
- data/lib/amalgalite/busy_timeout.rb +47 -0
- data/lib/amalgalite/column.rb +97 -0
- data/lib/amalgalite/core_ext/kernel/require.rb +21 -0
- data/lib/amalgalite/database.rb +947 -0
- data/lib/amalgalite/function.rb +61 -0
- data/lib/amalgalite/index.rb +43 -0
- data/lib/amalgalite/packer.rb +226 -0
- data/lib/amalgalite/paths.rb +70 -0
- data/lib/amalgalite/profile_tap.rb +131 -0
- data/lib/amalgalite/progress_handler.rb +21 -0
- data/lib/amalgalite/requires.rb +120 -0
- data/lib/amalgalite/schema.rb +191 -0
- data/lib/amalgalite/sqlite3.rb +6 -0
- data/lib/amalgalite/sqlite3/constants.rb +80 -0
- data/lib/amalgalite/sqlite3/database/function.rb +48 -0
- data/lib/amalgalite/sqlite3/database/status.rb +68 -0
- data/lib/amalgalite/sqlite3/status.rb +60 -0
- data/lib/amalgalite/sqlite3/version.rb +37 -0
- data/lib/amalgalite/statement.rb +414 -0
- data/lib/amalgalite/table.rb +90 -0
- data/lib/amalgalite/taps.rb +2 -0
- data/lib/amalgalite/taps/console.rb +27 -0
- data/lib/amalgalite/taps/io.rb +71 -0
- data/lib/amalgalite/trace_tap.rb +35 -0
- data/lib/amalgalite/type_map.rb +63 -0
- data/lib/amalgalite/type_maps/default_map.rb +167 -0
- data/lib/amalgalite/type_maps/storage_map.rb +40 -0
- data/lib/amalgalite/type_maps/text_map.rb +22 -0
- data/lib/amalgalite/version.rb +37 -0
- data/lib/amalgalite/view.rb +26 -0
- data/spec/aggregate_spec.rb +169 -0
- data/spec/amalgalite_spec.rb +4 -0
- data/spec/blob_spec.rb +81 -0
- data/spec/boolean_spec.rb +23 -0
- data/spec/busy_handler.rb +165 -0
- data/spec/database_spec.rb +494 -0
- data/spec/default_map_spec.rb +87 -0
- data/spec/function_spec.rb +94 -0
- data/spec/integeration_spec.rb +111 -0
- data/spec/packer_spec.rb +60 -0
- data/spec/paths_spec.rb +28 -0
- data/spec/progress_handler_spec.rb +105 -0
- data/spec/requires_spec.rb +23 -0
- data/spec/rtree_spec.rb +71 -0
- data/spec/schema_spec.rb +120 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/sqlite3/constants_spec.rb +65 -0
- data/spec/sqlite3/database_status_spec.rb +36 -0
- data/spec/sqlite3/status_spec.rb +18 -0
- data/spec/sqlite3/version_spec.rb +14 -0
- data/spec/sqlite3_spec.rb +53 -0
- data/spec/statement_spec.rb +161 -0
- data/spec/storage_map_spec.rb +41 -0
- data/spec/tap_spec.rb +59 -0
- data/spec/text_map_spec.rb +23 -0
- data/spec/type_map_spec.rb +17 -0
- data/spec/version_spec.rb +15 -0
- data/tasks/announce.rake +43 -0
- data/tasks/config.rb +107 -0
- data/tasks/distribution.rake +77 -0
- data/tasks/documentation.rake +32 -0
- data/tasks/extension.rake +141 -0
- data/tasks/rspec.rake +33 -0
- data/tasks/rubyforge.rake +59 -0
- data/tasks/utils.rb +80 -0
- 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, ¤t, &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
|
+
|