extralite-bundle 2.5 → 2.7
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/.gitignore +1 -0
- data/CHANGELOG.md +34 -13
- data/Gemfile +4 -0
- data/Gemfile-bundle +1 -1
- data/LICENSE +1 -1
- data/README.md +1059 -247
- data/Rakefile +18 -0
- data/TODO.md +0 -7
- data/examples/kv_store.rb +49 -0
- data/examples/multi_fiber.rb +16 -0
- data/examples/on_progress.rb +9 -0
- data/examples/pubsub_store_polyphony.rb +194 -0
- data/examples/pubsub_store_threads.rb +204 -0
- data/ext/extralite/changeset.c +463 -0
- data/ext/extralite/common.c +177 -91
- data/ext/extralite/database.c +745 -276
- data/ext/extralite/extconf-bundle.rb +10 -4
- data/ext/extralite/extconf.rb +34 -34
- data/ext/extralite/extralite.h +104 -47
- data/ext/extralite/extralite_ext.c +6 -0
- data/ext/extralite/iterator.c +14 -86
- data/ext/extralite/query.c +171 -264
- data/extralite-bundle.gemspec +1 -1
- data/extralite.gemspec +1 -1
- data/gemspec.rb +10 -11
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +69 -10
- data/lib/sequel/adapters/extralite.rb +1 -1
- data/test/helper.rb +9 -1
- data/test/perf_argv_transform.rb +74 -0
- data/test/perf_ary.rb +14 -12
- data/test/perf_hash.rb +17 -15
- data/test/perf_hash_prepared.rb +58 -0
- data/test/perf_hash_transform.rb +66 -0
- data/test/perf_polyphony.rb +74 -0
- data/test/test_changeset.rb +161 -0
- data/test/test_database.rb +720 -104
- data/test/test_extralite.rb +2 -2
- data/test/test_iterator.rb +28 -13
- data/test/test_query.rb +352 -110
- data/test/test_sequel.rb +4 -4
- metadata +24 -16
- data/Gemfile.lock +0 -37
- data/test/perf_prepared.rb +0 -64
data/ext/extralite/query.c
CHANGED
@@ -14,6 +14,12 @@ VALUE cQuery;
|
|
14
14
|
ID ID_inspect;
|
15
15
|
ID ID_slice;
|
16
16
|
|
17
|
+
VALUE SYM_hash;
|
18
|
+
VALUE SYM_argv;
|
19
|
+
VALUE SYM_ary;
|
20
|
+
|
21
|
+
#define DB_GVL_MODE(query) Database_prepare_gvl_mode(query->db_struct)
|
22
|
+
|
17
23
|
static size_t Query_size(const void *ptr) {
|
18
24
|
return sizeof(Query_t);
|
19
25
|
}
|
@@ -22,12 +28,14 @@ static void Query_mark(void *ptr) {
|
|
22
28
|
Query_t *query = ptr;
|
23
29
|
rb_gc_mark_movable(query->db);
|
24
30
|
rb_gc_mark_movable(query->sql);
|
31
|
+
rb_gc_mark_movable(query->transform_proc);
|
25
32
|
}
|
26
33
|
|
27
34
|
static void Query_compact(void *ptr) {
|
28
35
|
Query_t *query = ptr;
|
29
36
|
query->db = rb_gc_location(query->db);
|
30
37
|
query->sql = rb_gc_location(query->sql);
|
38
|
+
query->transform_proc = rb_gc_location(query->transform_proc);
|
31
39
|
}
|
32
40
|
|
33
41
|
static void Query_free(void *ptr) {
|
@@ -46,6 +54,7 @@ static VALUE Query_allocate(VALUE klass) {
|
|
46
54
|
Query_t *query = ALLOC(Query_t);
|
47
55
|
query->db = Qnil;
|
48
56
|
query->sql = Qnil;
|
57
|
+
query->transform_proc = Qnil;
|
49
58
|
query->sqlite3_db = NULL;
|
50
59
|
query->stmt = NULL;
|
51
60
|
return TypedData_Wrap_Struct(klass, &Query_type, query);
|
@@ -57,6 +66,27 @@ static inline Query_t *self_to_query(VALUE obj) {
|
|
57
66
|
return query;
|
58
67
|
}
|
59
68
|
|
69
|
+
static inline enum query_mode symbol_to_query_mode(VALUE sym) {
|
70
|
+
if (sym == SYM_hash) return QUERY_HASH;
|
71
|
+
if (sym == SYM_argv) return QUERY_ARGV;
|
72
|
+
if (sym == SYM_ary) return QUERY_ARY;
|
73
|
+
|
74
|
+
rb_raise(cError, "Invalid query mode");
|
75
|
+
}
|
76
|
+
|
77
|
+
static inline VALUE query_mode_to_symbol(enum query_mode query_mode) {
|
78
|
+
switch (query_mode) {
|
79
|
+
case QUERY_HASH:
|
80
|
+
return SYM_hash;
|
81
|
+
case QUERY_ARGV:
|
82
|
+
return SYM_argv;
|
83
|
+
case QUERY_ARY:
|
84
|
+
return SYM_ary;
|
85
|
+
default:
|
86
|
+
rb_raise(cError, "Invalid mode");
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
60
90
|
/* Initializes a new prepared query with the given database and SQL string. A
|
61
91
|
* `Query` is normally instantiated by calling `Database#prepare`:
|
62
92
|
*
|
@@ -64,9 +94,10 @@ static inline Query_t *self_to_query(VALUE obj) {
|
|
64
94
|
*
|
65
95
|
* @param db [Extralite::Database] associated database
|
66
96
|
* @param sql [String] SQL string
|
97
|
+
* @param mode [Symbol] query mode
|
67
98
|
* @return [void]
|
68
99
|
*/
|
69
|
-
VALUE Query_initialize(VALUE self, VALUE db, VALUE sql) {
|
100
|
+
VALUE Query_initialize(VALUE self, VALUE db, VALUE sql, VALUE mode) {
|
70
101
|
Query_t *query = self_to_query(self);
|
71
102
|
|
72
103
|
sql = rb_funcall(sql, ID_strip, 0);
|
@@ -75,6 +106,8 @@ VALUE Query_initialize(VALUE self, VALUE db, VALUE sql) {
|
|
75
106
|
|
76
107
|
RB_OBJ_WRITE(self, &query->db, db);
|
77
108
|
RB_OBJ_WRITE(self, &query->sql, sql);
|
109
|
+
if (rb_block_given_p())
|
110
|
+
RB_OBJ_WRITE(self, &query->transform_proc, rb_block_proc());
|
78
111
|
|
79
112
|
query->db = db;
|
80
113
|
query->db_struct = self_to_database(db);
|
@@ -82,26 +115,23 @@ VALUE Query_initialize(VALUE self, VALUE db, VALUE sql) {
|
|
82
115
|
query->stmt = NULL;
|
83
116
|
query->closed = 0;
|
84
117
|
query->eof = 0;
|
118
|
+
query->query_mode = symbol_to_query_mode(mode);
|
85
119
|
|
86
120
|
return Qnil;
|
87
121
|
}
|
88
122
|
|
89
123
|
static inline void query_reset(Query_t *query) {
|
90
124
|
if (!query->stmt)
|
91
|
-
prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
|
92
|
-
|
93
|
-
rb_funcall(query->db_struct->trace_block, ID_call, 1, query->sql);
|
125
|
+
prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
|
126
|
+
Database_issue_query(query->db_struct, query->sql);
|
94
127
|
sqlite3_reset(query->stmt);
|
95
128
|
query->eof = 0;
|
96
129
|
}
|
97
130
|
|
98
131
|
static inline void query_reset_and_bind(Query_t *query, int argc, VALUE * argv) {
|
99
132
|
if (!query->stmt)
|
100
|
-
prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
|
101
|
-
|
102
|
-
if (query->db_struct->trace_block != Qnil)
|
103
|
-
rb_funcall(query->db_struct->trace_block, ID_call, 1, query->sql);
|
104
|
-
|
133
|
+
prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
|
134
|
+
Database_issue_query(query->db_struct, query->sql);
|
105
135
|
sqlite3_reset(query->stmt);
|
106
136
|
query->eof = 0;
|
107
137
|
if (argc > 0) {
|
@@ -128,9 +158,6 @@ VALUE Query_reset(VALUE self) {
|
|
128
158
|
if (query->closed) rb_raise(cError, "Query is closed");
|
129
159
|
|
130
160
|
query_reset(query);
|
131
|
-
if (query->db_struct->trace_block != Qnil)
|
132
|
-
rb_funcall(query->db_struct->trace_block, ID_call, 1, query->sql);
|
133
|
-
|
134
161
|
return self;
|
135
162
|
}
|
136
163
|
|
@@ -176,7 +203,7 @@ VALUE Query_eof_p(VALUE self) {
|
|
176
203
|
|
177
204
|
#define MAX_ROWS(max_rows) (max_rows == SINGLE_ROW ? 1 : max_rows)
|
178
205
|
|
179
|
-
static inline VALUE Query_perform_next(VALUE self, int max_rows,
|
206
|
+
static inline VALUE Query_perform_next(VALUE self, int max_rows, safe_query_impl call) {
|
180
207
|
Query_t *query = self_to_query(self);
|
181
208
|
if (query->closed) rb_raise(cError, "Query is closed");
|
182
209
|
|
@@ -185,69 +212,37 @@ static inline VALUE Query_perform_next(VALUE self, int max_rows, VALUE (*call)(q
|
|
185
212
|
|
186
213
|
query_ctx ctx = QUERY_CTX(
|
187
214
|
self,
|
215
|
+
query->sql,
|
188
216
|
query->db_struct,
|
189
217
|
query->stmt,
|
190
218
|
Qnil,
|
191
|
-
|
219
|
+
query->transform_proc,
|
220
|
+
query->query_mode,
|
221
|
+
ROW_YIELD_OR_MODE(max_rows == SINGLE_ROW ? ROW_SINGLE : ROW_MULTI),
|
192
222
|
MAX_ROWS(max_rows)
|
193
223
|
);
|
194
224
|
VALUE result = call(&ctx);
|
195
225
|
query->eof = ctx.eof;
|
196
|
-
return (ctx.
|
226
|
+
return (ctx.row_mode == ROW_YIELD) ? self : result;
|
197
227
|
}
|
198
228
|
|
199
229
|
#define MAX_ROWS_FROM_ARGV(argc, argv) (argc == 1 ? FIX2INT(argv[0]) : SINGLE_ROW)
|
200
230
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
* @return [Hash, Extralite::Query] next row or self if block is given
|
213
|
-
* @overload next_hash()
|
214
|
-
* @return [Hash, Extralite::Query] next row or self if block is given
|
215
|
-
* @overload next(row_count)
|
216
|
-
* @param row_count [Integer] maximum row count or -1 for all rows
|
217
|
-
* @return [Array<Hash>, Extralite::Query] next rows or self if block is given
|
218
|
-
* @overload next_hash(row_count)
|
219
|
-
* @param row_count [Integer] maximum row count or -1 for all rows
|
220
|
-
* @return [Array<Hash>, Extralite::Query] next rows or self if block is given
|
221
|
-
*/
|
222
|
-
VALUE Query_next_hash(int argc, VALUE *argv, VALUE self) {
|
223
|
-
rb_check_arity(argc, 0, 1);
|
224
|
-
return Query_perform_next(self, MAX_ROWS_FROM_ARGV(argc, argv), safe_query_hash);
|
225
|
-
}
|
226
|
-
|
227
|
-
/* Returns the next 1 or more rows from the associated query's result set as an
|
228
|
-
* array.
|
229
|
-
*
|
230
|
-
* If no row count is given, a single row is returned. If a row count is given,
|
231
|
-
* an array containing up to the `row_count` rows is returned. If `row_count` is
|
232
|
-
* -1, all rows are returned. If the end of the result set has been reached,
|
233
|
-
* `nil` is returned.
|
234
|
-
*
|
235
|
-
* If a block is given, rows are passed to the block and self is returned.
|
236
|
-
*
|
237
|
-
* @overload next_ary()
|
238
|
-
* @return [Array, Extralite::Query] next row or self if block is given
|
239
|
-
* @overload next_ary(row_count)
|
240
|
-
* @param row_count [Integer] maximum row count or -1 for all rows
|
241
|
-
* @return [Array<Array>, Extralite::Query] next rows or self if block is given
|
242
|
-
*/
|
243
|
-
VALUE Query_next_ary(int argc, VALUE *argv, VALUE self) {
|
244
|
-
rb_check_arity(argc, 0, 1);
|
245
|
-
return Query_perform_next(self, MAX_ROWS_FROM_ARGV(argc, argv), safe_query_ary);
|
231
|
+
inline safe_query_impl query_impl(enum query_mode query_mode) {
|
232
|
+
switch (query_mode) {
|
233
|
+
case QUERY_HASH:
|
234
|
+
return safe_query_hash;
|
235
|
+
case QUERY_ARGV:
|
236
|
+
return safe_query_argv;
|
237
|
+
case QUERY_ARY:
|
238
|
+
return safe_query_ary;
|
239
|
+
default:
|
240
|
+
rb_raise(cError, "Invalid query mode (query_impl)");
|
241
|
+
}
|
246
242
|
}
|
247
243
|
|
248
|
-
/* Returns the next 1 or more rows from the associated query's result set
|
249
|
-
*
|
250
|
-
* raised.
|
244
|
+
/* Returns the next 1 or more rows from the associated query's result set. The
|
245
|
+
* row value is returned according to the query mode and the query transform.
|
251
246
|
*
|
252
247
|
* If no row count is given, a single row is returned. If a row count is given,
|
253
248
|
* an array containing up to the `row_count` rows is returned. If `row_count` is
|
@@ -256,92 +251,41 @@ VALUE Query_next_ary(int argc, VALUE *argv, VALUE self) {
|
|
256
251
|
*
|
257
252
|
* If a block is given, rows are passed to the block and self is returned.
|
258
253
|
*
|
259
|
-
* @overload
|
260
|
-
* @return [
|
261
|
-
* @overload
|
254
|
+
* @overload next()
|
255
|
+
* @return [any, Extralite::Query] next row or self if block is given
|
256
|
+
* @overload next(row_count)
|
262
257
|
* @param row_count [Integer] maximum row count or -1 for all rows
|
263
|
-
* @return [Array<
|
264
|
-
*/
|
265
|
-
VALUE Query_next_single_column(int argc, VALUE *argv, VALUE self) {
|
266
|
-
rb_check_arity(argc, 0, 1);
|
267
|
-
return Query_perform_next(self, MAX_ROWS_FROM_ARGV(argc, argv), safe_query_single_column);
|
268
|
-
}
|
269
|
-
|
270
|
-
/* Returns all rows in the associated query's result set as hashes.
|
271
|
-
*
|
272
|
-
* @overload to_a()
|
273
|
-
* @return [Array<Hash>] all rows
|
274
|
-
* @overload to_a_hash
|
275
|
-
* @return [Array<Hash>] all rows
|
276
|
-
*/
|
277
|
-
VALUE Query_to_a_hash(VALUE self) {
|
278
|
-
Query_t *query = self_to_query(self);
|
279
|
-
query_reset(query);
|
280
|
-
return Query_perform_next(self, ALL_ROWS, safe_query_hash);
|
281
|
-
}
|
282
|
-
|
283
|
-
/* Returns all rows in the associated query's result set as arrays.
|
284
|
-
*
|
285
|
-
* @return [Array<Array>] all rows
|
286
|
-
*/
|
287
|
-
VALUE Query_to_a_ary(VALUE self) {
|
288
|
-
Query_t *query = self_to_query(self);
|
289
|
-
query_reset(query);
|
290
|
-
return Query_perform_next(self, ALL_ROWS, safe_query_ary);
|
291
|
-
}
|
292
|
-
|
293
|
-
/* Returns all rows in the associated query's result set as single values. If
|
294
|
-
* the result set contains more than one column an error is raised.
|
295
|
-
*
|
296
|
-
* @return [Array<Object>] all rows
|
258
|
+
* @return [Array<any>, Extralite::Query] next rows or self if block is given
|
297
259
|
*/
|
298
|
-
VALUE
|
260
|
+
VALUE Query_next(int argc, VALUE *argv, VALUE self) {
|
299
261
|
Query_t *query = self_to_query(self);
|
300
|
-
|
301
|
-
return Query_perform_next(self,
|
262
|
+
rb_check_arity(argc, 0, 1);
|
263
|
+
return Query_perform_next(self, MAX_ROWS_FROM_ARGV(argc, argv), query_impl(query->query_mode));
|
302
264
|
}
|
303
265
|
|
304
|
-
/*
|
305
|
-
*
|
306
|
-
* mode.
|
266
|
+
/* Returns all rows in the associated query's result set. Rows are returned
|
267
|
+
* according to the query mode and the query transform.
|
307
268
|
*
|
308
|
-
* @return [
|
269
|
+
* @return [Array<any>] all rows
|
309
270
|
*/
|
310
|
-
VALUE
|
311
|
-
if (!rb_block_given_p()) return rb_funcall(cIterator, ID_new, 2, self, SYM_hash);
|
312
|
-
|
271
|
+
VALUE Query_to_a(VALUE self) {
|
313
272
|
Query_t *query = self_to_query(self);
|
314
273
|
query_reset(query);
|
315
|
-
return Query_perform_next(self, ALL_ROWS,
|
274
|
+
return Query_perform_next(self, ALL_ROWS, query_impl(query->query_mode));
|
316
275
|
}
|
317
276
|
|
318
|
-
/* Iterates through the result set, passing each row to the given block
|
319
|
-
*
|
320
|
-
*
|
277
|
+
/* Iterates through the result set, passing each row to the given block. If no
|
278
|
+
* block is given, returns a `Extralite::Iterator` instance. Rows are given to
|
279
|
+
* the block according to the query mode and the query transform.
|
321
280
|
*
|
322
281
|
* @return [Extralite::Query, Extralite::Iterator] self or an iterator if no block is given
|
323
282
|
*/
|
324
|
-
VALUE
|
325
|
-
if (!rb_block_given_p()) return rb_funcall(cIterator, ID_new,
|
283
|
+
VALUE Query_each(VALUE self) {
|
284
|
+
if (!rb_block_given_p()) return rb_funcall(cIterator, ID_new, 1, self);
|
326
285
|
|
327
286
|
Query_t *query = self_to_query(self);
|
328
287
|
query_reset(query);
|
329
|
-
return Query_perform_next(self, ALL_ROWS,
|
330
|
-
}
|
331
|
-
|
332
|
-
/* Iterates through the result set, passing each row to the given block as a
|
333
|
-
* single value. If the result set contains more than one column an error is
|
334
|
-
* raised. If no block is given, returns a `Extralite::Iterator` instance in
|
335
|
-
* single column mode.
|
336
|
-
*
|
337
|
-
* @return [Extralite::Query, Extralite::Iterator] self or an iterator if no block is given
|
338
|
-
*/
|
339
|
-
VALUE Query_each_single_column(VALUE self) {
|
340
|
-
if (!rb_block_given_p()) return rb_funcall(cIterator, ID_new, 2, self, SYM_single_column);
|
341
|
-
|
342
|
-
Query_t *query = self_to_query(self);
|
343
|
-
query_reset(query);
|
344
|
-
return Query_perform_next(self, ALL_ROWS, safe_query_single_column);
|
288
|
+
return Query_perform_next(self, ALL_ROWS, query_impl(query->query_mode));
|
345
289
|
}
|
346
290
|
|
347
291
|
/* call-seq:
|
@@ -437,14 +381,17 @@ VALUE Query_batch_execute(VALUE self, VALUE parameters) {
|
|
437
381
|
if (query->closed) rb_raise(cError, "Query is closed");
|
438
382
|
|
439
383
|
if (!query->stmt)
|
440
|
-
prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
|
384
|
+
prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
|
441
385
|
|
442
386
|
query_ctx ctx = QUERY_CTX(
|
443
387
|
self,
|
388
|
+
query->sql,
|
444
389
|
query->db_struct,
|
445
390
|
query->stmt,
|
446
391
|
parameters,
|
447
|
-
|
392
|
+
Qnil,
|
393
|
+
QUERY_HASH,
|
394
|
+
ROW_YIELD_OR_MODE(ROW_MULTI),
|
448
395
|
ALL_ROWS
|
449
396
|
);
|
450
397
|
return safe_batch_execute(&ctx);
|
@@ -463,6 +410,8 @@ VALUE Query_batch_execute(VALUE self, VALUE parameters) {
|
|
463
410
|
* invocation of the query, and the total number of changes is returned.
|
464
411
|
* Otherwise, an array containing the resulting rows for each invocation is
|
465
412
|
* returned.
|
413
|
+
*
|
414
|
+
* Rows are returned according to the query mode and transform.
|
466
415
|
*
|
467
416
|
* q = db.prepare('insert into foo values (?, ?) returning bar, baz')
|
468
417
|
* records = [
|
@@ -474,114 +423,29 @@ VALUE Query_batch_execute(VALUE self, VALUE parameters) {
|
|
474
423
|
* *
|
475
424
|
* @param sql [String] query SQL
|
476
425
|
* @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
|
477
|
-
* @return [Array<
|
426
|
+
* @return [Array<Array>, Integer] Returned rows or total number of changes effected
|
478
427
|
*/
|
479
428
|
VALUE Query_batch_query(VALUE self, VALUE parameters) {
|
480
429
|
Query_t *query = self_to_query(self);
|
481
430
|
if (query->closed) rb_raise(cError, "Query is closed");
|
482
431
|
|
483
432
|
if (!query->stmt)
|
484
|
-
prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
|
433
|
+
prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
|
485
434
|
|
486
435
|
query_ctx ctx = QUERY_CTX(
|
487
436
|
self,
|
437
|
+
query->sql,
|
488
438
|
query->db_struct,
|
489
439
|
query->stmt,
|
490
440
|
parameters,
|
491
|
-
|
441
|
+
query->transform_proc,
|
442
|
+
query->query_mode,
|
443
|
+
ROW_YIELD_OR_MODE(ROW_MULTI),
|
492
444
|
ALL_ROWS
|
493
445
|
);
|
494
446
|
return safe_batch_query(&ctx);
|
495
447
|
}
|
496
448
|
|
497
|
-
/* call-seq:
|
498
|
-
* query.batch_query_ary(sql, params_array) -> rows
|
499
|
-
* query.batch_query_ary(sql, enumerable) -> rows
|
500
|
-
* query.batch_query_ary(sql, callable) -> rows
|
501
|
-
* query.batch_query_ary(sql, params_array) { |rows| ... } -> changes
|
502
|
-
* query.batch_query_ary(sql, enumerable) { |rows| ... } -> changes
|
503
|
-
* query.batch_query_ary(sql, callable) { |rows| ... } -> changes
|
504
|
-
*
|
505
|
-
* Executes the prepared query for each list of parameters in the given paramter
|
506
|
-
* source. If a block is given, it is called with the resulting rows for each
|
507
|
-
* invocation of the query, and the total number of changes is returned.
|
508
|
-
* Otherwise, an array containing the resulting rows for each invocation is
|
509
|
-
* returned. Rows are represented as arrays.
|
510
|
-
*
|
511
|
-
* q = db.prepare('insert into foo values (?, ?) returning bar, baz')
|
512
|
-
* records = [
|
513
|
-
* [1, 2],
|
514
|
-
* [3, 4]
|
515
|
-
* ]
|
516
|
-
* q.batch_query_ary(records)
|
517
|
-
* #=> [{ bar: 1, baz: 2 }, { bar: 3, baz: 4}]
|
518
|
-
* *
|
519
|
-
* @param sql [String] query SQL
|
520
|
-
* @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
|
521
|
-
* @return [Array<Hash>, Integer] Total number of changes effected
|
522
|
-
*/
|
523
|
-
VALUE Query_batch_query_ary(VALUE self, VALUE parameters) {
|
524
|
-
Query_t *query = self_to_query(self);
|
525
|
-
if (query->closed) rb_raise(cError, "Query is closed");
|
526
|
-
|
527
|
-
if (!query->stmt)
|
528
|
-
prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
|
529
|
-
|
530
|
-
query_ctx ctx = QUERY_CTX(
|
531
|
-
self,
|
532
|
-
query->db_struct,
|
533
|
-
query->stmt,
|
534
|
-
parameters,
|
535
|
-
QUERY_MODE(QUERY_MULTI_ROW),
|
536
|
-
ALL_ROWS
|
537
|
-
);
|
538
|
-
return safe_batch_query_ary(&ctx);
|
539
|
-
}
|
540
|
-
|
541
|
-
/* call-seq:
|
542
|
-
* query.batch_query_single_column(sql, params_array) -> rows
|
543
|
-
* query.batch_query_single_column(sql, enumerable) -> rows
|
544
|
-
* query.batch_query_single_column(sql, callable) -> rows
|
545
|
-
* query.batch_query_single_column(sql, params_array) { |rows| ... } -> changes
|
546
|
-
* query.batch_query_single_column(sql, enumerable) { |rows| ... } -> changes
|
547
|
-
* query.batch_query_single_column(sql, callable) { |rows| ... } -> changes
|
548
|
-
*
|
549
|
-
* Executes the prepared query for each list of parameters in the given paramter
|
550
|
-
* source. If a block is given, it is called with the resulting rows for each
|
551
|
-
* invocation of the query, and the total number of changes is returned.
|
552
|
-
* Otherwise, an array containing the resulting rows for each invocation is
|
553
|
-
* returned. Rows are represented as single values.
|
554
|
-
*
|
555
|
-
* q = db.prepare('insert into foo values (?, ?) returning bar, baz')
|
556
|
-
* records = [
|
557
|
-
* [1, 2],
|
558
|
-
* [3, 4]
|
559
|
-
* ]
|
560
|
-
* q.batch_query_single_column(records)
|
561
|
-
* #=> [{ bar: 1, baz: 2 }, { bar: 3, baz: 4}]
|
562
|
-
* *
|
563
|
-
* @param sql [String] query SQL
|
564
|
-
* @param parameters [Array<Array, Hash>, Enumerable, Enumerator, Callable] parameters to run query with
|
565
|
-
* @return [Array<Hash>, Integer] Total number of changes effected
|
566
|
-
*/
|
567
|
-
VALUE Query_batch_query_single_column(VALUE self, VALUE parameters) {
|
568
|
-
Query_t *query = self_to_query(self);
|
569
|
-
if (query->closed) rb_raise(cError, "Query is closed");
|
570
|
-
|
571
|
-
if (!query->stmt)
|
572
|
-
prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
|
573
|
-
|
574
|
-
query_ctx ctx = QUERY_CTX(
|
575
|
-
self,
|
576
|
-
query->db_struct,
|
577
|
-
query->stmt,
|
578
|
-
parameters,
|
579
|
-
QUERY_MODE(QUERY_MULTI_ROW),
|
580
|
-
ALL_ROWS
|
581
|
-
);
|
582
|
-
return safe_batch_query_single_column(&ctx);
|
583
|
-
}
|
584
|
-
|
585
449
|
/* Returns the database associated with the query.
|
586
450
|
*
|
587
451
|
* @overload database()
|
@@ -623,7 +487,12 @@ VALUE Query_columns(VALUE self) {
|
|
623
487
|
*/
|
624
488
|
VALUE Query_clone(VALUE self) {
|
625
489
|
Query_t *query = self_to_query(self);
|
626
|
-
|
490
|
+
VALUE args[] = {
|
491
|
+
query->db,
|
492
|
+
query->sql,
|
493
|
+
query_mode_to_symbol(query->query_mode)
|
494
|
+
};
|
495
|
+
return rb_funcall_with_block(cQuery, ID_new, 3, args, query->transform_proc);
|
627
496
|
}
|
628
497
|
|
629
498
|
/* Closes the query. Attempting to run a closed query will raise an error.
|
@@ -670,12 +539,33 @@ VALUE Query_status(int argc, VALUE* argv, VALUE self) {
|
|
670
539
|
if (query->closed) rb_raise(cError, "Query is closed");
|
671
540
|
|
672
541
|
if (!query->stmt)
|
673
|
-
prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
|
542
|
+
prepare_single_stmt(DB_GVL_MODE(query), query->sqlite3_db, &query->stmt, query->sql);
|
674
543
|
|
675
544
|
int value = sqlite3_stmt_status(query->stmt, NUM2INT(op), RTEST(reset) ? 1 : 0);
|
676
545
|
return INT2NUM(value);
|
677
546
|
}
|
678
547
|
|
548
|
+
/* Sets the transform block to the given block. If a transform block is set,
|
549
|
+
* calls to #to_a, #next, #each and #batch_query will transform values fetched
|
550
|
+
* from the database using the transform block before passing them to the
|
551
|
+
* application code. To remove the transform block, call `#transform`
|
552
|
+
* without a block. The transform for each row is done by passing the row hash
|
553
|
+
* to the block.
|
554
|
+
*
|
555
|
+
* # fetch column c as an ORM object
|
556
|
+
* q = db.prepare('select * from foo order by a').transform do |h|
|
557
|
+
* MyModel.new(h)
|
558
|
+
* end
|
559
|
+
*
|
560
|
+
* @return [Extralite::Query] query
|
561
|
+
*/
|
562
|
+
VALUE Query_transform(VALUE self) {
|
563
|
+
Query_t *query = self_to_query(self);
|
564
|
+
|
565
|
+
RB_OBJ_WRITE(self, &query->transform_proc, rb_block_given_p() ? rb_block_proc() : Qnil);
|
566
|
+
return self;
|
567
|
+
}
|
568
|
+
|
679
569
|
/* Returns a short string representation of the query instance, including the
|
680
570
|
* SQL string.
|
681
571
|
*
|
@@ -694,50 +584,67 @@ VALUE Query_inspect(VALUE self) {
|
|
694
584
|
return rb_sprintf("#<%"PRIsVALUE":%p %"PRIsVALUE">", cname, (void*)self, sql);
|
695
585
|
}
|
696
586
|
|
587
|
+
/* Returns the query mode.
|
588
|
+
*
|
589
|
+
* @return [Symbol] query mode
|
590
|
+
*/
|
591
|
+
VALUE Query_mode_get(VALUE self) {
|
592
|
+
Query_t *query = self_to_query(self);
|
593
|
+
return query_mode_to_symbol(query->query_mode);
|
594
|
+
}
|
595
|
+
|
596
|
+
/* Sets the query mode. This can be one of `:hash`, `:argv`, `:ary`.
|
597
|
+
*
|
598
|
+
* @param [Symbol] query mode
|
599
|
+
* @return [Symbol] query mode
|
600
|
+
*/
|
601
|
+
VALUE Query_mode_set(VALUE self, VALUE mode) {
|
602
|
+
Query_t *query = self_to_query(self);
|
603
|
+
query->query_mode = symbol_to_query_mode(mode);
|
604
|
+
return mode;
|
605
|
+
}
|
606
|
+
|
697
607
|
void Init_ExtraliteQuery(void) {
|
698
608
|
VALUE mExtralite = rb_define_module("Extralite");
|
699
609
|
|
700
610
|
cQuery = rb_define_class_under(mExtralite, "Query", rb_cObject);
|
701
611
|
rb_define_alloc_func(cQuery, Query_allocate);
|
702
612
|
|
703
|
-
rb_define_method(cQuery,
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
rb_define_method(cQuery, "next_hash", Query_next_hash, -1);
|
730
|
-
rb_define_method(cQuery, "next_single_column", Query_next_single_column, -1);
|
731
|
-
|
732
|
-
rb_define_method(cQuery, "reset", Query_reset, 0);
|
733
|
-
rb_define_method(cQuery, "sql", Query_sql, 0);
|
734
|
-
rb_define_method(cQuery, "status", Query_status, -1);
|
735
|
-
|
736
|
-
rb_define_method(cQuery, "to_a", Query_to_a_hash, 0);
|
737
|
-
rb_define_method(cQuery, "to_a_ary", Query_to_a_ary, 0);
|
738
|
-
rb_define_method(cQuery, "to_a_hash", Query_to_a_hash, 0);
|
739
|
-
rb_define_method(cQuery, "to_a_single_column", Query_to_a_single_column, 0);
|
613
|
+
#define DEF(s, f, a) rb_define_method(cQuery, s, f, a)
|
614
|
+
|
615
|
+
DEF("bind", Query_bind, -1);
|
616
|
+
DEF("close", Query_close, 0);
|
617
|
+
DEF("closed?", Query_closed_p, 0);
|
618
|
+
DEF("columns", Query_columns, 0);
|
619
|
+
DEF("clone", Query_clone, 0);
|
620
|
+
DEF("database", Query_database, 0);
|
621
|
+
DEF("db", Query_database, 0);
|
622
|
+
DEF("dup", Query_clone, 0);
|
623
|
+
DEF("each", Query_each, 0);
|
624
|
+
DEF("eof?", Query_eof_p, 0);
|
625
|
+
DEF("execute", Query_execute, -1);
|
626
|
+
DEF("<<", Query_execute_chevrons, 1);
|
627
|
+
DEF("batch_execute", Query_batch_execute, 1);
|
628
|
+
DEF("batch_query", Query_batch_query, 1);
|
629
|
+
DEF("initialize", Query_initialize, 3);
|
630
|
+
DEF("inspect", Query_inspect, 0);
|
631
|
+
DEF("mode", Query_mode_get, 0);
|
632
|
+
DEF("mode=", Query_mode_set, 1);
|
633
|
+
DEF("next", Query_next, -1);
|
634
|
+
DEF("reset", Query_reset, 0);
|
635
|
+
DEF("sql", Query_sql, 0);
|
636
|
+
DEF("status", Query_status, -1);
|
637
|
+
DEF("to_a", Query_to_a, 0);
|
638
|
+
DEF("transform", Query_transform, 0);
|
740
639
|
|
741
640
|
ID_inspect = rb_intern("inspect");
|
742
641
|
ID_slice = rb_intern("slice");
|
642
|
+
|
643
|
+
SYM_hash = ID2SYM(rb_intern("hash"));
|
644
|
+
SYM_argv = ID2SYM(rb_intern("argv"));
|
645
|
+
SYM_ary = ID2SYM(rb_intern("ary"));
|
646
|
+
|
647
|
+
rb_gc_register_mark_object(SYM_hash);
|
648
|
+
rb_gc_register_mark_object(SYM_argv);
|
649
|
+
rb_gc_register_mark_object(SYM_ary);
|
743
650
|
}
|
data/extralite-bundle.gemspec
CHANGED
data/extralite.gemspec
CHANGED