extralite 1.12 → 1.13
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +64 -28
- data/Rakefile +1 -1
- data/bin/update_sqlite_source +26 -0
- data/ext/extralite/common.c +347 -0
- data/ext/extralite/database.c +385 -0
- data/ext/extralite/extralite.h +59 -0
- data/ext/extralite/extralite_ext.c +4 -2
- data/ext/extralite/prepared_statement.c +238 -0
- data/ext/extralite/sqlite3.c +5568 -3801
- data/ext/extralite/sqlite3.h +341 -31
- data/lib/extralite/version.rb +1 -1
- data/test/perf_hash.rb +1 -1
- data/test/perf_prepared.rb +64 -0
- data/test/test_prepared_statement.rb +165 -0
- metadata +9 -3
- data/ext/extralite/extralite.c +0 -702
data/ext/extralite/extralite.c
DELETED
@@ -1,702 +0,0 @@
|
|
1
|
-
#include <stdio.h>
|
2
|
-
#include "ruby.h"
|
3
|
-
#include "ruby/thread.h"
|
4
|
-
#include <sqlite3.h>
|
5
|
-
|
6
|
-
VALUE cError;
|
7
|
-
VALUE cSQLError;
|
8
|
-
VALUE cBusyError;
|
9
|
-
|
10
|
-
ID ID_KEYS;
|
11
|
-
ID ID_STRIP;
|
12
|
-
ID ID_TO_S;
|
13
|
-
|
14
|
-
typedef struct Database_t {
|
15
|
-
sqlite3 *sqlite3_db;
|
16
|
-
} Database_t;
|
17
|
-
|
18
|
-
static size_t Database_size(const void *ptr) {
|
19
|
-
return sizeof(Database_t);
|
20
|
-
}
|
21
|
-
|
22
|
-
static void Database_free(void *ptr) {
|
23
|
-
Database_t *db = ptr;
|
24
|
-
if (db->sqlite3_db) {
|
25
|
-
sqlite3_close(db->sqlite3_db);
|
26
|
-
}
|
27
|
-
// close db
|
28
|
-
free(ptr);
|
29
|
-
}
|
30
|
-
|
31
|
-
static const rb_data_type_t Database_type = {
|
32
|
-
"Database",
|
33
|
-
{0, Database_free, Database_size,},
|
34
|
-
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
35
|
-
};
|
36
|
-
|
37
|
-
static VALUE Database_allocate(VALUE klass) {
|
38
|
-
Database_t *db = ALLOC(Database_t);
|
39
|
-
db->sqlite3_db = 0;
|
40
|
-
return TypedData_Wrap_Struct(klass, &Database_type, db);
|
41
|
-
}
|
42
|
-
|
43
|
-
#define GetDatabase(obj, database) \
|
44
|
-
TypedData_Get_Struct((obj), Database_t, &Database_type, (database))
|
45
|
-
|
46
|
-
// make sure the database is open
|
47
|
-
#define GetOpenDatabase(obj, database) { \
|
48
|
-
TypedData_Get_Struct((obj), Database_t, &Database_type, (database)); \
|
49
|
-
if (!(database)->sqlite3_db) { \
|
50
|
-
rb_raise(cError, "Database is closed"); \
|
51
|
-
} \
|
52
|
-
}
|
53
|
-
|
54
|
-
/* call-seq: sqlite3_version
|
55
|
-
*
|
56
|
-
* Returns the sqlite3 version used by Extralite.
|
57
|
-
*/
|
58
|
-
|
59
|
-
VALUE Extralite_sqlite3_version(VALUE self) {
|
60
|
-
return rb_str_new_cstr(sqlite3_version);
|
61
|
-
}
|
62
|
-
|
63
|
-
/* call-seq: initialize(path)
|
64
|
-
*
|
65
|
-
* Initializes a new SQLite database with the given path.
|
66
|
-
*/
|
67
|
-
|
68
|
-
VALUE Database_initialize(VALUE self, VALUE path) {
|
69
|
-
int rc;
|
70
|
-
Database_t *db;
|
71
|
-
GetDatabase(self, db);
|
72
|
-
|
73
|
-
rc = sqlite3_open(StringValueCStr(path), &db->sqlite3_db);
|
74
|
-
if (rc) {
|
75
|
-
sqlite3_close(db->sqlite3_db);
|
76
|
-
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
77
|
-
}
|
78
|
-
|
79
|
-
rc = sqlite3_enable_load_extension(db->sqlite3_db, 1);
|
80
|
-
if (rc) {
|
81
|
-
sqlite3_close(db->sqlite3_db);
|
82
|
-
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
83
|
-
}
|
84
|
-
|
85
|
-
return Qnil;
|
86
|
-
}
|
87
|
-
|
88
|
-
/* call-seq: close
|
89
|
-
*
|
90
|
-
* Closes the database.
|
91
|
-
*/
|
92
|
-
VALUE Database_close(VALUE self) {
|
93
|
-
int rc;
|
94
|
-
Database_t *db;
|
95
|
-
GetDatabase(self, db);
|
96
|
-
|
97
|
-
rc = sqlite3_close(db->sqlite3_db);
|
98
|
-
if (rc) {
|
99
|
-
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
100
|
-
}
|
101
|
-
|
102
|
-
db->sqlite3_db = 0;
|
103
|
-
return self;
|
104
|
-
}
|
105
|
-
|
106
|
-
/* call-seq: closed?
|
107
|
-
*
|
108
|
-
* Returns true if the database is closed.
|
109
|
-
*/
|
110
|
-
VALUE Database_closed_p(VALUE self) {
|
111
|
-
Database_t *db;
|
112
|
-
GetDatabase(self, db);
|
113
|
-
|
114
|
-
return db->sqlite3_db ? Qfalse : Qtrue;
|
115
|
-
}
|
116
|
-
|
117
|
-
static inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
|
118
|
-
switch (type) {
|
119
|
-
case SQLITE_NULL:
|
120
|
-
return Qnil;
|
121
|
-
case SQLITE_INTEGER:
|
122
|
-
return LL2NUM(sqlite3_column_int64(stmt, col));
|
123
|
-
case SQLITE_FLOAT:
|
124
|
-
return DBL2NUM(sqlite3_column_double(stmt, col));
|
125
|
-
case SQLITE_TEXT:
|
126
|
-
return rb_str_new_cstr((char *)sqlite3_column_text(stmt, col));
|
127
|
-
case SQLITE_BLOB:
|
128
|
-
return rb_str_new((const char *)sqlite3_column_blob(stmt, col), (long)sqlite3_column_bytes(stmt, col));
|
129
|
-
default:
|
130
|
-
rb_raise(cError, "Unknown column type: %d", type);
|
131
|
-
}
|
132
|
-
|
133
|
-
return Qnil;
|
134
|
-
}
|
135
|
-
|
136
|
-
static void bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value);
|
137
|
-
|
138
|
-
static inline void bind_hash_parameter_values(sqlite3_stmt *stmt, VALUE hash) {
|
139
|
-
VALUE keys = rb_funcall(hash, ID_KEYS, 0);
|
140
|
-
long len = RARRAY_LEN(keys);
|
141
|
-
for (long i = 0; i < len; i++) {
|
142
|
-
VALUE k = RARRAY_AREF(keys, i);
|
143
|
-
VALUE v = rb_hash_aref(hash, k);
|
144
|
-
|
145
|
-
switch (TYPE(k)) {
|
146
|
-
case T_FIXNUM:
|
147
|
-
bind_parameter_value(stmt, NUM2INT(k), v);
|
148
|
-
break;
|
149
|
-
case T_SYMBOL:
|
150
|
-
k = rb_funcall(k, ID_TO_S, 0);
|
151
|
-
case T_STRING:
|
152
|
-
if(RSTRING_PTR(k)[0] != ':') k = rb_str_plus(rb_str_new2(":"), k);
|
153
|
-
int pos = sqlite3_bind_parameter_index(stmt, StringValuePtr(k));
|
154
|
-
bind_parameter_value(stmt, pos, v);
|
155
|
-
break;
|
156
|
-
default:
|
157
|
-
rb_raise(cError, "Cannot bind hash key value idx %ld", i);
|
158
|
-
}
|
159
|
-
}
|
160
|
-
RB_GC_GUARD(keys);
|
161
|
-
}
|
162
|
-
|
163
|
-
static inline void bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value) {
|
164
|
-
switch (TYPE(value)) {
|
165
|
-
case T_NIL:
|
166
|
-
sqlite3_bind_null(stmt, pos);
|
167
|
-
return;
|
168
|
-
case T_FIXNUM:
|
169
|
-
sqlite3_bind_int64(stmt, pos, NUM2LL(value));
|
170
|
-
return;
|
171
|
-
case T_FLOAT:
|
172
|
-
sqlite3_bind_double(stmt, pos, NUM2DBL(value));
|
173
|
-
return;
|
174
|
-
case T_TRUE:
|
175
|
-
sqlite3_bind_int(stmt, pos, 1);
|
176
|
-
return;
|
177
|
-
case T_FALSE:
|
178
|
-
sqlite3_bind_int(stmt, pos, 0);
|
179
|
-
return;
|
180
|
-
case T_STRING:
|
181
|
-
sqlite3_bind_text(stmt, pos, RSTRING_PTR(value), RSTRING_LEN(value), SQLITE_TRANSIENT);
|
182
|
-
return;
|
183
|
-
case T_HASH:
|
184
|
-
bind_hash_parameter_values(stmt, value);
|
185
|
-
return;
|
186
|
-
default:
|
187
|
-
rb_raise(cError, "Cannot bind parameter at position %d", pos);
|
188
|
-
}
|
189
|
-
}
|
190
|
-
|
191
|
-
static inline void bind_all_parameters(sqlite3_stmt *stmt, int argc, VALUE *argv) {
|
192
|
-
if (argc > 1) {
|
193
|
-
for (int i = 1; i < argc; i++) {
|
194
|
-
bind_parameter_value(stmt, i, argv[i]);
|
195
|
-
}
|
196
|
-
}
|
197
|
-
}
|
198
|
-
|
199
|
-
static inline VALUE get_column_names(sqlite3_stmt *stmt, int column_count) {
|
200
|
-
VALUE arr = rb_ary_new2(column_count);
|
201
|
-
for (int i = 0; i < column_count; i++) {
|
202
|
-
VALUE name = ID2SYM(rb_intern(sqlite3_column_name(stmt, i)));
|
203
|
-
rb_ary_push(arr, name);
|
204
|
-
}
|
205
|
-
return arr;
|
206
|
-
}
|
207
|
-
|
208
|
-
static inline VALUE row_to_hash(sqlite3_stmt *stmt, int column_count, VALUE column_names) {
|
209
|
-
VALUE row = rb_hash_new();
|
210
|
-
for (int i = 0; i < column_count; i++) {
|
211
|
-
VALUE value = get_column_value(stmt, i, sqlite3_column_type(stmt, i));
|
212
|
-
rb_hash_aset(row, RARRAY_AREF(column_names, i), value);
|
213
|
-
}
|
214
|
-
return row;
|
215
|
-
}
|
216
|
-
|
217
|
-
static inline VALUE row_to_ary(sqlite3_stmt *stmt, int column_count) {
|
218
|
-
VALUE row = rb_ary_new2(column_count);
|
219
|
-
for (int i = 0; i < column_count; i++) {
|
220
|
-
VALUE value = get_column_value(stmt, i, sqlite3_column_type(stmt, i));
|
221
|
-
rb_ary_push(row, value);
|
222
|
-
}
|
223
|
-
return row;
|
224
|
-
}
|
225
|
-
|
226
|
-
struct multi_stmt_ctx {
|
227
|
-
sqlite3 *db;
|
228
|
-
sqlite3_stmt **stmt;
|
229
|
-
const char *str;
|
230
|
-
long len;
|
231
|
-
int rc;
|
232
|
-
};
|
233
|
-
|
234
|
-
void *prepare_multi_stmt_without_gvl(void *ptr) {
|
235
|
-
struct multi_stmt_ctx *ctx = (struct multi_stmt_ctx *)ptr;
|
236
|
-
const char *rest = NULL;
|
237
|
-
const char *str = ctx->str;
|
238
|
-
const char *end = ctx->str + ctx->len;
|
239
|
-
while (1) {
|
240
|
-
ctx->rc = sqlite3_prepare_v2(ctx->db, str, end - str, ctx->stmt, &rest);
|
241
|
-
if (ctx->rc) {
|
242
|
-
sqlite3_finalize(*ctx->stmt);
|
243
|
-
return NULL;
|
244
|
-
}
|
245
|
-
|
246
|
-
if (rest == end) return NULL;
|
247
|
-
|
248
|
-
// perform current query, but discard its results
|
249
|
-
ctx->rc = sqlite3_step(*ctx->stmt);
|
250
|
-
sqlite3_finalize(*ctx->stmt);
|
251
|
-
switch (ctx->rc) {
|
252
|
-
case SQLITE_BUSY:
|
253
|
-
case SQLITE_ERROR:
|
254
|
-
case SQLITE_MISUSE:
|
255
|
-
return NULL;
|
256
|
-
}
|
257
|
-
str = rest;
|
258
|
-
}
|
259
|
-
return NULL;
|
260
|
-
}
|
261
|
-
|
262
|
-
/*
|
263
|
-
This function prepares a statement from an SQL string containing one or more SQL
|
264
|
-
statements. It will release the GVL while the statements are being prepared and
|
265
|
-
executed. All statements excluding the last one are executed. The last statement
|
266
|
-
is not executed, but instead handed back to the caller for looping over results.
|
267
|
-
*/
|
268
|
-
static inline void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
|
269
|
-
struct multi_stmt_ctx ctx = {db, stmt, RSTRING_PTR(sql), RSTRING_LEN(sql), 0};
|
270
|
-
rb_thread_call_without_gvl(prepare_multi_stmt_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
|
271
|
-
RB_GC_GUARD(sql);
|
272
|
-
|
273
|
-
switch (ctx.rc) {
|
274
|
-
case 0:
|
275
|
-
return;
|
276
|
-
case SQLITE_BUSY:
|
277
|
-
rb_raise(cBusyError, "Database is busy");
|
278
|
-
case SQLITE_ERROR:
|
279
|
-
rb_raise(cSQLError, "%s", sqlite3_errmsg(db));
|
280
|
-
default:
|
281
|
-
rb_raise(cError, "Invalid return code for prepare_multi_stmt_without_gvl: %d (please open an issue on https://github.com/digital-fabric/extralite)", ctx.rc);
|
282
|
-
}
|
283
|
-
}
|
284
|
-
|
285
|
-
struct step_ctx {
|
286
|
-
sqlite3_stmt *stmt;
|
287
|
-
int rc;
|
288
|
-
};
|
289
|
-
|
290
|
-
void *stmt_iterate_without_gvl(void *ptr) {
|
291
|
-
struct step_ctx *ctx = (struct step_ctx *)ptr;
|
292
|
-
ctx->rc = sqlite3_step(ctx->stmt);
|
293
|
-
return NULL;
|
294
|
-
}
|
295
|
-
|
296
|
-
static inline int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db) {
|
297
|
-
struct step_ctx ctx = {stmt, 0};
|
298
|
-
rb_thread_call_without_gvl(stmt_iterate_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
|
299
|
-
switch (ctx.rc) {
|
300
|
-
case SQLITE_ROW:
|
301
|
-
return 1;
|
302
|
-
case SQLITE_DONE:
|
303
|
-
return 0;
|
304
|
-
case SQLITE_BUSY:
|
305
|
-
rb_raise(cBusyError, "Database is busy");
|
306
|
-
case SQLITE_ERROR:
|
307
|
-
rb_raise(cSQLError, "%s", sqlite3_errmsg(db));
|
308
|
-
default:
|
309
|
-
rb_raise(cError, "Invalid return code for sqlite3_step: %d (please open an issue on https://github.com/digital-fabric/extralite)", ctx.rc);
|
310
|
-
}
|
311
|
-
|
312
|
-
return 0;
|
313
|
-
}
|
314
|
-
|
315
|
-
typedef struct query_ctx {
|
316
|
-
VALUE self;
|
317
|
-
int argc;
|
318
|
-
VALUE *argv;
|
319
|
-
sqlite3_stmt *stmt;
|
320
|
-
} query_ctx;
|
321
|
-
|
322
|
-
VALUE cleanup_stmt(VALUE arg) {
|
323
|
-
query_ctx *ctx = (query_ctx *)arg;
|
324
|
-
if (ctx->stmt) sqlite3_finalize(ctx->stmt);
|
325
|
-
return Qnil;
|
326
|
-
}
|
327
|
-
|
328
|
-
#define check_arity_and_prepare_sql(argc, argv, sql) { \
|
329
|
-
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS); \
|
330
|
-
sql = rb_funcall(argv[0], ID_STRIP, 0); \
|
331
|
-
if (RSTRING_LEN(sql) == 0) return Qnil; \
|
332
|
-
}
|
333
|
-
|
334
|
-
VALUE safe_query_hash(VALUE arg) {
|
335
|
-
query_ctx *ctx = (query_ctx *)arg;
|
336
|
-
Database_t *db;
|
337
|
-
VALUE result = ctx->self;
|
338
|
-
int yield_to_block = rb_block_given_p();
|
339
|
-
VALUE row;
|
340
|
-
VALUE sql;
|
341
|
-
int column_count;
|
342
|
-
VALUE column_names;
|
343
|
-
|
344
|
-
check_arity_and_prepare_sql(ctx->argc, ctx->argv, sql);
|
345
|
-
GetOpenDatabase(ctx->self, db);
|
346
|
-
|
347
|
-
prepare_multi_stmt(db->sqlite3_db, &ctx->stmt, sql);
|
348
|
-
bind_all_parameters(ctx->stmt, ctx->argc, ctx->argv);
|
349
|
-
column_count = sqlite3_column_count(ctx->stmt);
|
350
|
-
column_names = get_column_names(ctx->stmt, column_count);
|
351
|
-
|
352
|
-
// block not given, so prepare the array of records to be returned
|
353
|
-
if (!yield_to_block) result = rb_ary_new();
|
354
|
-
|
355
|
-
while (stmt_iterate(ctx->stmt, db->sqlite3_db)) {
|
356
|
-
row = row_to_hash(ctx->stmt, column_count, column_names);
|
357
|
-
if (yield_to_block) rb_yield(row); else rb_ary_push(result, row);
|
358
|
-
}
|
359
|
-
|
360
|
-
RB_GC_GUARD(column_names);
|
361
|
-
RB_GC_GUARD(row);
|
362
|
-
RB_GC_GUARD(result);
|
363
|
-
return result;
|
364
|
-
}
|
365
|
-
|
366
|
-
/* call-seq:
|
367
|
-
* query(sql, *parameters, &block)
|
368
|
-
* query_hash(sql, *parameters, &block)
|
369
|
-
*
|
370
|
-
* Runs a query returning rows as hashes (with symbol keys). If a block is
|
371
|
-
* given, it will be called for each row. Otherwise, an array containing all
|
372
|
-
* rows is returned.
|
373
|
-
*
|
374
|
-
* Query parameters to be bound to placeholders in the query can be specified as
|
375
|
-
* a list of values or as a hash mapping parameter names to values. When
|
376
|
-
* parameters are given as a least, the query should specify parameters using
|
377
|
-
* `?`:
|
378
|
-
*
|
379
|
-
* db.query('select * from foo where x = ?', 42)
|
380
|
-
*
|
381
|
-
* Named placeholders are specified using `:`. The placeholder values are
|
382
|
-
* specified using a hash, where keys are either strings are symbols. String
|
383
|
-
* keys can include or omit the `:` prefix. The following are equivalent:
|
384
|
-
*
|
385
|
-
* db.query('select * from foo where x = :bar', bar: 42)
|
386
|
-
* db.query('select * from foo where x = :bar', 'bar' => 42)
|
387
|
-
* db.query('select * from foo where x = :bar', ':bar' => 42)
|
388
|
-
*/
|
389
|
-
VALUE Database_query_hash(int argc, VALUE *argv, VALUE self) {
|
390
|
-
query_ctx ctx = { self, argc, argv, 0 };
|
391
|
-
return rb_ensure(safe_query_hash, (VALUE)&ctx, cleanup_stmt, (VALUE)&ctx);
|
392
|
-
}
|
393
|
-
|
394
|
-
VALUE safe_query_ary(VALUE arg) {
|
395
|
-
query_ctx *ctx = (query_ctx *)arg;
|
396
|
-
Database_t *db;
|
397
|
-
int column_count;
|
398
|
-
VALUE result = ctx->self;
|
399
|
-
int yield_to_block = rb_block_given_p();
|
400
|
-
VALUE row;
|
401
|
-
VALUE sql;
|
402
|
-
|
403
|
-
check_arity_and_prepare_sql(ctx->argc, ctx->argv, sql);
|
404
|
-
GetOpenDatabase(ctx->self, db);
|
405
|
-
|
406
|
-
prepare_multi_stmt(db->sqlite3_db, &ctx->stmt, sql);
|
407
|
-
bind_all_parameters(ctx->stmt, ctx->argc, ctx->argv);
|
408
|
-
column_count = sqlite3_column_count(ctx->stmt);
|
409
|
-
|
410
|
-
// block not given, so prepare the array of records to be returned
|
411
|
-
if (!yield_to_block) result = rb_ary_new();
|
412
|
-
|
413
|
-
while (stmt_iterate(ctx->stmt, db->sqlite3_db)) {
|
414
|
-
row = row_to_ary(ctx->stmt, column_count);
|
415
|
-
if (yield_to_block) rb_yield(row); else rb_ary_push(result, row);
|
416
|
-
}
|
417
|
-
|
418
|
-
RB_GC_GUARD(row);
|
419
|
-
RB_GC_GUARD(result);
|
420
|
-
return result;
|
421
|
-
}
|
422
|
-
|
423
|
-
/* call-seq: query_ary(sql, *parameters, &block)
|
424
|
-
*
|
425
|
-
* Runs a query returning rows as arrays. If a block is given, it will be called
|
426
|
-
* for each row. Otherwise, an array containing all rows is returned.
|
427
|
-
*
|
428
|
-
* Query parameters to be bound to placeholders in the query can be specified as
|
429
|
-
* a list of values or as a hash mapping parameter names to values. When
|
430
|
-
* parameters are given as a least, the query should specify parameters using
|
431
|
-
* `?`:
|
432
|
-
*
|
433
|
-
* db.query_ary('select * from foo where x = ?', 42)
|
434
|
-
*
|
435
|
-
* Named placeholders are specified using `:`. The placeholder values are
|
436
|
-
* specified using a hash, where keys are either strings are symbols. String
|
437
|
-
* keys can include or omit the `:` prefix. The following are equivalent:
|
438
|
-
*
|
439
|
-
* db.query_ary('select * from foo where x = :bar', bar: 42)
|
440
|
-
* db.query_ary('select * from foo where x = :bar', 'bar' => 42)
|
441
|
-
* db.query_ary('select * from foo where x = :bar', ':bar' => 42)
|
442
|
-
*/
|
443
|
-
VALUE Database_query_ary(int argc, VALUE *argv, VALUE self) {
|
444
|
-
query_ctx ctx = { self, argc, argv, 0 };
|
445
|
-
return rb_ensure(safe_query_ary, (VALUE)&ctx, cleanup_stmt, (VALUE)&ctx);
|
446
|
-
}
|
447
|
-
|
448
|
-
VALUE safe_query_single_row(VALUE arg) {
|
449
|
-
query_ctx *ctx = (query_ctx *)arg;
|
450
|
-
Database_t *db;
|
451
|
-
int column_count;
|
452
|
-
VALUE sql;
|
453
|
-
VALUE row = Qnil;
|
454
|
-
VALUE column_names;
|
455
|
-
|
456
|
-
check_arity_and_prepare_sql(ctx->argc, ctx->argv, sql);
|
457
|
-
GetOpenDatabase(ctx->self, db);
|
458
|
-
|
459
|
-
prepare_multi_stmt(db->sqlite3_db, &ctx->stmt, sql);
|
460
|
-
bind_all_parameters(ctx->stmt, ctx->argc, ctx->argv);
|
461
|
-
column_count = sqlite3_column_count(ctx->stmt);
|
462
|
-
column_names = get_column_names(ctx->stmt, column_count);
|
463
|
-
|
464
|
-
if (stmt_iterate(ctx->stmt, db->sqlite3_db))
|
465
|
-
row = row_to_hash(ctx->stmt, column_count, column_names);
|
466
|
-
|
467
|
-
RB_GC_GUARD(row);
|
468
|
-
RB_GC_GUARD(column_names);
|
469
|
-
return row;
|
470
|
-
}
|
471
|
-
|
472
|
-
/* call-seq: query_single_row(sql, *parameters)
|
473
|
-
*
|
474
|
-
* Runs a query returning a single row as a hash.
|
475
|
-
*
|
476
|
-
* Query parameters to be bound to placeholders in the query can be specified as
|
477
|
-
* a list of values or as a hash mapping parameter names to values. When
|
478
|
-
* parameters are given as a least, the query should specify parameters using
|
479
|
-
* `?`:
|
480
|
-
*
|
481
|
-
* db.query_single_row('select * from foo where x = ?', 42)
|
482
|
-
*
|
483
|
-
* Named placeholders are specified using `:`. The placeholder values are
|
484
|
-
* specified using a hash, where keys are either strings are symbols. String
|
485
|
-
* keys can include or omit the `:` prefix. The following are equivalent:
|
486
|
-
*
|
487
|
-
* db.query_single_row('select * from foo where x = :bar', bar: 42)
|
488
|
-
* db.query_single_row('select * from foo where x = :bar', 'bar' => 42)
|
489
|
-
* db.query_single_row('select * from foo where x = :bar', ':bar' => 42)
|
490
|
-
*/
|
491
|
-
VALUE Database_query_single_row(int argc, VALUE *argv, VALUE self) {
|
492
|
-
query_ctx ctx = { self, argc, argv, 0 };
|
493
|
-
return rb_ensure(safe_query_single_row, (VALUE)&ctx, cleanup_stmt, (VALUE)&ctx);
|
494
|
-
}
|
495
|
-
|
496
|
-
VALUE safe_query_single_column(VALUE arg) {
|
497
|
-
query_ctx *ctx = (query_ctx *)arg;
|
498
|
-
|
499
|
-
int column_count;
|
500
|
-
Database_t *db;
|
501
|
-
VALUE result = ctx->self;
|
502
|
-
int yield_to_block = rb_block_given_p();
|
503
|
-
VALUE sql;
|
504
|
-
VALUE value;
|
505
|
-
|
506
|
-
check_arity_and_prepare_sql(ctx->argc, ctx->argv, sql);
|
507
|
-
GetOpenDatabase(ctx->self, db);
|
508
|
-
|
509
|
-
prepare_multi_stmt(db->sqlite3_db, &ctx->stmt, sql);
|
510
|
-
bind_all_parameters(ctx->stmt, ctx->argc, ctx->argv);
|
511
|
-
column_count = sqlite3_column_count(ctx->stmt);
|
512
|
-
if (column_count != 1)
|
513
|
-
rb_raise(cError, "Expected query result to have 1 column");
|
514
|
-
|
515
|
-
// block not given, so prepare the array of records to be returned
|
516
|
-
if (!yield_to_block) result = rb_ary_new();
|
517
|
-
|
518
|
-
while (stmt_iterate(ctx->stmt, db->sqlite3_db)) {
|
519
|
-
value = get_column_value(ctx->stmt, 0, sqlite3_column_type(ctx->stmt, 0));
|
520
|
-
if (yield_to_block) rb_yield(value); else rb_ary_push(result, value);
|
521
|
-
}
|
522
|
-
|
523
|
-
RB_GC_GUARD(value);
|
524
|
-
RB_GC_GUARD(result);
|
525
|
-
return result;
|
526
|
-
}
|
527
|
-
|
528
|
-
/* call-seq: query_single_column(sql, *parameters, &block)
|
529
|
-
*
|
530
|
-
* Runs a query returning single column values. If a block is given, it will be called
|
531
|
-
* for each value. Otherwise, an array containing all values is returned.
|
532
|
-
*
|
533
|
-
* Query parameters to be bound to placeholders in the query can be specified as
|
534
|
-
* a list of values or as a hash mapping parameter names to values. When
|
535
|
-
* parameters are given as a least, the query should specify parameters using
|
536
|
-
* `?`:
|
537
|
-
*
|
538
|
-
* db.query_single_column('select x from foo where x = ?', 42)
|
539
|
-
*
|
540
|
-
* Named placeholders are specified using `:`. The placeholder values are
|
541
|
-
* specified using a hash, where keys are either strings are symbols. String
|
542
|
-
* keys can include or omit the `:` prefix. The following are equivalent:
|
543
|
-
*
|
544
|
-
* db.query_single_column('select x from foo where x = :bar', bar: 42)
|
545
|
-
* db.query_single_column('select x from foo where x = :bar', 'bar' => 42)
|
546
|
-
* db.query_single_column('select x from foo where x = :bar', ':bar' => 42)
|
547
|
-
*/
|
548
|
-
VALUE Database_query_single_column(int argc, VALUE *argv, VALUE self) {
|
549
|
-
query_ctx ctx = { self, argc, argv, 0 };
|
550
|
-
return rb_ensure(safe_query_single_column, (VALUE)&ctx, cleanup_stmt, (VALUE)&ctx);
|
551
|
-
}
|
552
|
-
|
553
|
-
VALUE safe_query_single_value(VALUE arg) {
|
554
|
-
query_ctx *ctx = (query_ctx *)arg;
|
555
|
-
int column_count;
|
556
|
-
Database_t *db;
|
557
|
-
VALUE sql;
|
558
|
-
VALUE value = Qnil;
|
559
|
-
|
560
|
-
check_arity_and_prepare_sql(ctx->argc, ctx->argv, sql);
|
561
|
-
GetOpenDatabase(ctx->self, db);
|
562
|
-
|
563
|
-
prepare_multi_stmt(db->sqlite3_db, &ctx->stmt, sql);
|
564
|
-
bind_all_parameters(ctx->stmt, ctx->argc, ctx->argv);
|
565
|
-
column_count = sqlite3_column_count(ctx->stmt);
|
566
|
-
if (column_count != 1)
|
567
|
-
rb_raise(cError, "Expected query result to have 1 column");
|
568
|
-
|
569
|
-
if (stmt_iterate(ctx->stmt, db->sqlite3_db))
|
570
|
-
value = get_column_value(ctx->stmt, 0, sqlite3_column_type(ctx->stmt, 0));
|
571
|
-
|
572
|
-
RB_GC_GUARD(value);
|
573
|
-
return value;
|
574
|
-
}
|
575
|
-
|
576
|
-
/* call-seq: query_single_value(sql, *parameters)
|
577
|
-
*
|
578
|
-
* Runs a query returning a single value from the first row.
|
579
|
-
*
|
580
|
-
* Query parameters to be bound to placeholders in the query can be specified as
|
581
|
-
* a list of values or as a hash mapping parameter names to values. When
|
582
|
-
* parameters are given as a least, the query should specify parameters using
|
583
|
-
* `?`:
|
584
|
-
*
|
585
|
-
* db.query_single_value('select x from foo where x = ?', 42)
|
586
|
-
*
|
587
|
-
* Named placeholders are specified using `:`. The placeholder values are
|
588
|
-
* specified using a hash, where keys are either strings are symbols. String
|
589
|
-
* keys can include or omit the `:` prefix. The following are equivalent:
|
590
|
-
*
|
591
|
-
* db.query_single_value('select x from foo where x = :bar', bar: 42)
|
592
|
-
* db.query_single_value('select x from foo where x = :bar', 'bar' => 42)
|
593
|
-
* db.query_single_value('select x from foo where x = :bar', ':bar' => 42)
|
594
|
-
*/
|
595
|
-
VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
|
596
|
-
query_ctx ctx = { self, argc, argv, 0 };
|
597
|
-
return rb_ensure(safe_query_single_value, (VALUE)&ctx, cleanup_stmt, (VALUE)&ctx);
|
598
|
-
}
|
599
|
-
|
600
|
-
/* call-seq: last_insert_rowid
|
601
|
-
*
|
602
|
-
* Returns the rowid of the last inserted row.
|
603
|
-
*/
|
604
|
-
VALUE Database_last_insert_rowid(VALUE self) {
|
605
|
-
Database_t *db;
|
606
|
-
GetOpenDatabase(self, db);
|
607
|
-
|
608
|
-
return INT2NUM(sqlite3_last_insert_rowid(db->sqlite3_db));
|
609
|
-
}
|
610
|
-
|
611
|
-
/* call-seq: changes
|
612
|
-
*
|
613
|
-
* Returns the number of changes made to the database by the last operation.
|
614
|
-
*/
|
615
|
-
VALUE Database_changes(VALUE self) {
|
616
|
-
Database_t *db;
|
617
|
-
GetOpenDatabase(self, db);
|
618
|
-
|
619
|
-
return INT2NUM(sqlite3_changes(db->sqlite3_db));
|
620
|
-
}
|
621
|
-
|
622
|
-
/* call-seq: filename
|
623
|
-
*
|
624
|
-
* Returns the database filename.
|
625
|
-
*/
|
626
|
-
VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
|
627
|
-
const char *db_name;
|
628
|
-
const char *filename;
|
629
|
-
Database_t *db;
|
630
|
-
GetOpenDatabase(self, db);
|
631
|
-
|
632
|
-
rb_check_arity(argc, 0, 1);
|
633
|
-
db_name = (argc == 1) ? StringValueCStr(argv[0]) : "main";
|
634
|
-
filename = sqlite3_db_filename(db->sqlite3_db, db_name);
|
635
|
-
return filename ? rb_str_new_cstr(filename) : Qnil;
|
636
|
-
}
|
637
|
-
|
638
|
-
/* call-seq: transaction_active?
|
639
|
-
*
|
640
|
-
* Returns true if a transaction is currently in progress.
|
641
|
-
*/
|
642
|
-
VALUE Database_transaction_active_p(VALUE self) {
|
643
|
-
Database_t *db;
|
644
|
-
GetOpenDatabase(self, db);
|
645
|
-
|
646
|
-
return sqlite3_get_autocommit(db->sqlite3_db) ? Qfalse : Qtrue;
|
647
|
-
}
|
648
|
-
|
649
|
-
/* call-seq: load_extension(path)
|
650
|
-
*
|
651
|
-
* Loads an extension with the given path.
|
652
|
-
*/
|
653
|
-
VALUE Database_load_extension(VALUE self, VALUE path) {
|
654
|
-
Database_t *db;
|
655
|
-
GetOpenDatabase(self, db);
|
656
|
-
char *err_msg;
|
657
|
-
|
658
|
-
int rc = sqlite3_load_extension(db->sqlite3_db, RSTRING_PTR(path), 0, &err_msg);
|
659
|
-
if (rc != SQLITE_OK) {
|
660
|
-
VALUE error = rb_exc_new2(cError, err_msg);
|
661
|
-
sqlite3_free(err_msg);
|
662
|
-
rb_exc_raise(error);
|
663
|
-
}
|
664
|
-
|
665
|
-
return self;
|
666
|
-
}
|
667
|
-
|
668
|
-
void Init_Extralite() {
|
669
|
-
VALUE mExtralite = rb_define_module("Extralite");
|
670
|
-
rb_define_singleton_method(mExtralite, "sqlite3_version", Extralite_sqlite3_version, 0);
|
671
|
-
|
672
|
-
VALUE cDatabase = rb_define_class_under(mExtralite, "Database", rb_cObject);
|
673
|
-
rb_define_alloc_func(cDatabase, Database_allocate);
|
674
|
-
|
675
|
-
rb_define_method(cDatabase, "initialize", Database_initialize, 1);
|
676
|
-
rb_define_method(cDatabase, "close", Database_close, 0);
|
677
|
-
rb_define_method(cDatabase, "closed?", Database_closed_p, 0);
|
678
|
-
|
679
|
-
rb_define_method(cDatabase, "query", Database_query_hash, -1);
|
680
|
-
rb_define_method(cDatabase, "query_hash", Database_query_hash, -1);
|
681
|
-
rb_define_method(cDatabase, "query_ary", Database_query_ary, -1);
|
682
|
-
rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
|
683
|
-
rb_define_method(cDatabase, "query_single_column", Database_query_single_column, -1);
|
684
|
-
rb_define_method(cDatabase, "query_single_value", Database_query_single_value, -1);
|
685
|
-
|
686
|
-
rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
|
687
|
-
rb_define_method(cDatabase, "changes", Database_changes, 0);
|
688
|
-
rb_define_method(cDatabase, "filename", Database_filename, -1);
|
689
|
-
rb_define_method(cDatabase, "transaction_active?", Database_transaction_active_p, 0);
|
690
|
-
rb_define_method(cDatabase, "load_extension", Database_load_extension, 1);
|
691
|
-
|
692
|
-
cError = rb_define_class_under(mExtralite, "Error", rb_eRuntimeError);
|
693
|
-
cSQLError = rb_define_class_under(mExtralite, "SQLError", cError);
|
694
|
-
cBusyError = rb_define_class_under(mExtralite, "BusyError", cError);
|
695
|
-
rb_gc_register_mark_object(cError);
|
696
|
-
rb_gc_register_mark_object(cSQLError);
|
697
|
-
rb_gc_register_mark_object(cBusyError);
|
698
|
-
|
699
|
-
ID_KEYS = rb_intern("keys");
|
700
|
-
ID_STRIP = rb_intern("strip");
|
701
|
-
ID_TO_S = rb_intern("to_s");
|
702
|
-
}
|