extralite 1.27 → 2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,534 @@
1
+ #include <stdio.h>
2
+ #include "extralite.h"
3
+
4
+ /*
5
+ * Document-class: Extralite::Query
6
+ *
7
+ * This class represents a prepared query that can be reused with different
8
+ * parameters. It encapsulates [SQLite prepared
9
+ * statements](https://sqlite.org/c3ref/stmt.html).
10
+ */
11
+
12
+ VALUE cQuery;
13
+
14
+ ID ID_inspect;
15
+ ID ID_slice;
16
+
17
+ static size_t Query_size(const void *ptr) {
18
+ return sizeof(Query_t);
19
+ }
20
+
21
+ static void Query_mark(void *ptr) {
22
+ Query_t *query = ptr;
23
+ rb_gc_mark(query->db);
24
+ rb_gc_mark(query->sql);
25
+ }
26
+
27
+ static void Query_free(void *ptr) {
28
+ Query_t *query = ptr;
29
+ if (query->stmt) sqlite3_finalize(query->stmt);
30
+ free(ptr);
31
+ }
32
+
33
+ static const rb_data_type_t Query_type = {
34
+ "Query",
35
+ {Query_mark, Query_free, Query_size,},
36
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
37
+ };
38
+
39
+ static VALUE Query_allocate(VALUE klass) {
40
+ Query_t *query = ALLOC(Query_t);
41
+ query->db = Qnil;
42
+ query->sql = Qnil;
43
+ query->sqlite3_db = NULL;
44
+ query->stmt = NULL;
45
+ return TypedData_Wrap_Struct(klass, &Query_type, query);
46
+ }
47
+
48
+ static inline Query_t *self_to_query(VALUE obj) {
49
+ Query_t *query;
50
+ TypedData_Get_Struct((obj), Query_t, &Query_type, (query));
51
+ return query;
52
+ }
53
+
54
+ /* Initializes a new prepared query with the given database and SQL string. A
55
+ * `Query` is normally instantiated by calling `Database#prepare`:
56
+ *
57
+ * query = @db.prepare('select * from foo')
58
+ *
59
+ * @param db [Extralite::Database] associated database
60
+ * @param sql [String] SQL string
61
+ * @return [void]
62
+ */
63
+ VALUE Query_initialize(VALUE self, VALUE db, VALUE sql) {
64
+ Query_t *query = self_to_query(self);
65
+
66
+ sql = rb_funcall(sql, ID_strip, 0);
67
+ if (!RSTRING_LEN(sql))
68
+ rb_raise(cError, "Cannot prepare an empty SQL query");
69
+
70
+ query->db = db;
71
+ query->db_struct = self_to_database(db);
72
+ query->sqlite3_db = Database_sqlite3_db(db);
73
+ query->sql = sql;
74
+ query->stmt = NULL;
75
+ query->closed = 0;
76
+ query->eof = 0;
77
+
78
+ return Qnil;
79
+ }
80
+
81
+ static inline void query_reset(Query_t *query) {
82
+ if (!query->stmt)
83
+ prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
84
+ if (query->db_struct->trace_block != Qnil)
85
+ rb_funcall(query->db_struct->trace_block, ID_call, 1, query->sql);
86
+ sqlite3_reset(query->stmt);
87
+ query->eof = 0;
88
+ }
89
+
90
+ static inline void query_reset_and_bind(Query_t *query, int argc, VALUE * argv) {
91
+ if (!query->stmt)
92
+ prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
93
+
94
+ if (query->db_struct->trace_block != Qnil)
95
+ rb_funcall(query->db_struct->trace_block, ID_call, 1, query->sql);
96
+
97
+ sqlite3_reset(query->stmt);
98
+ query->eof = 0;
99
+ if (argc > 0) {
100
+ sqlite3_clear_bindings(query->stmt);
101
+ bind_all_parameters(query->stmt, argc, argv);
102
+ }
103
+ }
104
+
105
+ /* Resets the underlying prepared statement. After calling this method the
106
+ * underlying prepared statement is reset to its initial state, and any call to
107
+ * one of the `#next_xxx` methods will return the first row in the query's
108
+ * result set.
109
+ *
110
+ * query = @db.prepare('select * from foo where bar = ?')
111
+ * first = query.next
112
+ * second = query.next
113
+ * query.reset
114
+ * query.next #=> returns the first row again
115
+ *
116
+ * @return [Extralite::Query] self
117
+ */
118
+ VALUE Query_reset(VALUE self) {
119
+ Query_t *query = self_to_query(self);
120
+ if (query->closed) rb_raise(cError, "Query is closed");
121
+
122
+ query_reset(query);
123
+ if (query->db_struct->trace_block != Qnil)
124
+ rb_funcall(query->db_struct->trace_block, ID_call, 1, query->sql);
125
+
126
+ return self;
127
+ }
128
+
129
+ /* Resets the underlying prepared statement and rebinds parameters if any are
130
+ * given. After calling this method the underlying prepared statement is reset
131
+ * to its initial state, and any call to one of the `#next_xxx` methods will
132
+ * return the first row in the query's result set.
133
+ *
134
+ * Bound parameters can be specified as a list of values or as a hash mapping
135
+ * parameter names to values. When parameters are given as a splatted array, the
136
+ * query should specify parameters using `?`:
137
+ *
138
+ * query = db.prepare('select * from foo where x = ?')
139
+ * query.bind(42)
140
+ *
141
+ * Named placeholders are specified using `:`. The placeholder values are
142
+ * specified using a hash, where keys are either strings are symbols. String
143
+ * keys can include or omit the `:` prefix. The following are equivalent:
144
+ *
145
+ * query = db.prepare('select * from foo where x = :bar')
146
+ * query.bind(bar: 42)
147
+ *
148
+ * @return [Extralite::Query] self
149
+ */
150
+ VALUE Query_bind(int argc, VALUE *argv, VALUE self) {
151
+ Query_t *query = self_to_query(self);
152
+ if (query->closed) rb_raise(cError, "Query is closed");
153
+
154
+ query_reset_and_bind(query, argc, argv);
155
+ return self;
156
+ }
157
+
158
+ /* Returns true if iteration has reached the end of the result set.
159
+ *
160
+ * @return [boolean] true if iteration has reached the end of the result set
161
+ */
162
+ VALUE Query_eof_p(VALUE self) {
163
+ Query_t *query = self_to_query(self);
164
+ if (query->closed) rb_raise(cError, "Query is closed");
165
+
166
+ return query->eof ? Qtrue : Qfalse;
167
+ }
168
+
169
+ #define MAX_ROWS(max_rows) (max_rows == SINGLE_ROW ? 1 : max_rows)
170
+
171
+ static inline VALUE Query_perform_next(VALUE self, int max_rows, VALUE (*call)(query_ctx *)) {
172
+ Query_t *query = self_to_query(self);
173
+ if (query->closed) rb_raise(cError, "Query is closed");
174
+
175
+ if (!query->stmt) query_reset(query);
176
+ if (query->eof) return rb_block_given_p() ? self : Qnil;
177
+
178
+ query_ctx ctx = {
179
+ self,
180
+ query->sqlite3_db,
181
+ query->stmt,
182
+ Qnil,
183
+ QUERY_MODE(max_rows == SINGLE_ROW ? QUERY_SINGLE_ROW : QUERY_MULTI_ROW),
184
+ MAX_ROWS(max_rows),
185
+ 0
186
+ };
187
+ VALUE result = call(&ctx);
188
+ query->eof = ctx.eof;
189
+ return (ctx.mode == QUERY_YIELD) ? self : result;
190
+ }
191
+
192
+ #define MAX_ROWS_FROM_ARGV(argc, argv) (argc == 1 ? FIX2INT(argv[0]) : SINGLE_ROW)
193
+
194
+ /* Returns the next 1 or more rows from the associated query's result set as a
195
+ * hash.
196
+ *
197
+ * If no row count is given, a single row is returned. If a row count is given,
198
+ * an array containing up to the `row_count` rows is returned. If `row_count` is
199
+ * -1, all rows are returned. If the end of the result set has been reached,
200
+ * `nil` is returned.
201
+ *
202
+ * If a block is given, rows are passed to the block and self is returned.
203
+ *
204
+ * @overload next()
205
+ * @return [Hash, Extralite::Query] next row or self if block is given
206
+ * @overload next_hash()
207
+ * @return [Hash, Extralite::Query] next row or self if block is given
208
+ * @overload next(row_count)
209
+ * @param row_count [Integer] maximum row count or -1 for all rows
210
+ * @return [Array<Hash>, Extralite::Query] next rows or self if block is given
211
+ * @overload next_hash(row_count)
212
+ * @param row_count [Integer] maximum row count or -1 for all rows
213
+ * @return [Array<Hash>, Extralite::Query] next rows or self if block is given
214
+ */
215
+ VALUE Query_next_hash(int argc, VALUE *argv, VALUE self) {
216
+ rb_check_arity(argc, 0, 1);
217
+ return Query_perform_next(self, MAX_ROWS_FROM_ARGV(argc, argv), safe_query_hash);
218
+ }
219
+
220
+ /* Returns the next 1 or more rows from the associated query's result set as an
221
+ * array.
222
+ *
223
+ * If no row count is given, a single row is returned. If a row count is given,
224
+ * an array containing up to the `row_count` rows is returned. If `row_count` is
225
+ * -1, all rows are returned. If the end of the result set has been reached,
226
+ * `nil` is returned.
227
+ *
228
+ * If a block is given, rows are passed to the block and self is returned.
229
+ *
230
+ * @overload next_ary()
231
+ * @return [Array, Extralite::Query] next row or self if block is given
232
+ * @overload next_ary(row_count)
233
+ * @param row_count [Integer] maximum row count or -1 for all rows
234
+ * @return [Array<Array>, Extralite::Query] next rows or self if block is given
235
+ */
236
+ VALUE Query_next_ary(int argc, VALUE *argv, VALUE self) {
237
+ rb_check_arity(argc, 0, 1);
238
+ return Query_perform_next(self, MAX_ROWS_FROM_ARGV(argc, argv), safe_query_ary);
239
+ }
240
+
241
+ /* Returns the next 1 or more rows from the associated query's result set as an
242
+ * single values. If the result set contains more than one column an error is
243
+ * raised.
244
+ *
245
+ * If no row count is given, a single row is returned. If a row count is given,
246
+ * an array containing up to the `row_count` rows is returned. If `row_count` is
247
+ * -1, all rows are returned. If the end of the result set has been reached,
248
+ * `nil` is returned.
249
+ *
250
+ * If a block is given, rows are passed to the block and self is returned.
251
+ *
252
+ * @overload next_ary()
253
+ * @return [Object, Extralite::Query] next row or self if block is given
254
+ * @overload next_ary(row_count)
255
+ * @param row_count [Integer] maximum row count or -1 for all rows
256
+ * @return [Array<Object>, Extralite::Query] next rows or self if block is given
257
+ */
258
+ VALUE Query_next_single_column(int argc, VALUE *argv, VALUE self) {
259
+ rb_check_arity(argc, 0, 1);
260
+ return Query_perform_next(self, MAX_ROWS_FROM_ARGV(argc, argv), safe_query_single_column);
261
+ }
262
+
263
+ /* Returns all rows in the associated query's result set as hashes.
264
+ *
265
+ * @overload to_a()
266
+ * @return [Array<Hash>] all rows
267
+ * @overload to_a_hash
268
+ * @return [Array<Hash>] all rows
269
+ */
270
+ VALUE Query_to_a_hash(VALUE self) {
271
+ Query_t *query = self_to_query(self);
272
+ query_reset(query);
273
+ return Query_perform_next(self, ALL_ROWS, safe_query_hash);
274
+ }
275
+
276
+ /* Returns all rows in the associated query's result set as arrays.
277
+ *
278
+ * @return [Array<Array>] all rows
279
+ */
280
+ VALUE Query_to_a_ary(VALUE self) {
281
+ Query_t *query = self_to_query(self);
282
+ query_reset(query);
283
+ return Query_perform_next(self, ALL_ROWS, safe_query_ary);
284
+ }
285
+
286
+ /* Returns all rows in the associated query's result set as single values. If
287
+ * the result set contains more than one column an error is raised.
288
+ *
289
+ * @return [Array<Object>] all rows
290
+ */
291
+ VALUE Query_to_a_single_column(VALUE self) {
292
+ Query_t *query = self_to_query(self);
293
+ query_reset(query);
294
+ return Query_perform_next(self, ALL_ROWS, safe_query_single_column);
295
+ }
296
+
297
+ /* Iterates through the result set, passing each row to the given block as a
298
+ * hash. If no block is given, returns a `Extralite::Iterator` instance in hash
299
+ * mode.
300
+ *
301
+ * @return [Extralite::Query, Extralite::Iterator] self or an iterator if no block is given
302
+ */
303
+ VALUE Query_each_hash(VALUE self) {
304
+ if (!rb_block_given_p()) return rb_funcall(cIterator, ID_new, 2, self, SYM_hash);
305
+
306
+ Query_t *query = self_to_query(self);
307
+ query_reset(query);
308
+ return Query_perform_next(self, ALL_ROWS, safe_query_hash);
309
+ }
310
+
311
+ /* Iterates through the result set, passing each row to the given block as an
312
+ * array. If no block is given, returns a `Extralite::Iterator` instance in
313
+ * array mode.
314
+ *
315
+ * @return [Extralite::Query, Extralite::Iterator] self or an iterator if no block is given
316
+ */
317
+ VALUE Query_each_ary(VALUE self) {
318
+ if (!rb_block_given_p()) return rb_funcall(cIterator, ID_new, 2, self, SYM_ary);
319
+
320
+ Query_t *query = self_to_query(self);
321
+ query_reset(query);
322
+ return Query_perform_next(self, ALL_ROWS, safe_query_ary);
323
+ }
324
+
325
+ /* Iterates through the result set, passing each row to the given block as a
326
+ * single value. If the result set contains more than one column an error is
327
+ * raised. If no block is given, returns a `Extralite::Iterator` instance in
328
+ * single column mode.
329
+ *
330
+ * @return [Extralite::Query, Extralite::Iterator] self or an iterator if no block is given
331
+ */
332
+ VALUE Query_each_single_column(VALUE self) {
333
+ if (!rb_block_given_p()) return rb_funcall(cIterator, ID_new, 2, self, SYM_single_column);
334
+
335
+ Query_t *query = self_to_query(self);
336
+ query_reset(query);
337
+ return Query_perform_next(self, ALL_ROWS, safe_query_single_column);
338
+ }
339
+
340
+ /* call-seq:
341
+ * query.execute(*parameters) -> changes
342
+ *
343
+ * Runs a query returning the total changes effected. This method should be used
344
+ * for data- or schema-manipulation queries.
345
+ *
346
+ * Query parameters to be bound to placeholders in the query can be specified as
347
+ * a list of values or as a hash mapping parameter names to values. When
348
+ * parameters are given as an array, the query should specify parameters using
349
+ * `?`:
350
+ *
351
+ * query = db.prepare('update foo set x = ? where y = ?')
352
+ * query.execute(42, 43)
353
+ *
354
+ * Named placeholders are specified using `:`. The placeholder values are
355
+ * specified using a hash, where keys are either strings are symbols. String
356
+ * keys can include or omit the `:` prefix. The following are equivalent:
357
+ *
358
+ * query = db.prepare('update foo set x = :bar')
359
+ * query.execute(bar: 42)
360
+ * query.execute('bar' => 42)
361
+ * query.execute(':bar' => 42)
362
+ */
363
+ VALUE Query_execute(int argc, VALUE *argv, VALUE self) {
364
+ Query_t *query = self_to_query(self);
365
+ query_reset_and_bind(query, argc, argv);
366
+ return Query_perform_next(self, ALL_ROWS, safe_query_changes);
367
+ }
368
+
369
+ /* Executes the query for each set of parameters in the given array. Parameters
370
+ * can be specified as either an array (for unnamed parameters) or a hash (for
371
+ * named parameters). Returns the number of changes effected. This method is
372
+ * designed for inserting multiple records.
373
+ *
374
+ * query = db.prepare('insert into foo values (?, ?, ?)')
375
+ * records = [
376
+ * [1, 2, 3],
377
+ * [4, 5, 6]
378
+ * ]
379
+ * query.execute_multi(records)
380
+ *
381
+ * @param parameters [Array<Array, Hash>] array of parameters to run query with
382
+ * @return [Integer] number of changes effected
383
+ */
384
+ VALUE Query_execute_multi(VALUE self, VALUE parameters) {
385
+ Query_t *query = self_to_query(self);
386
+ if (query->closed) rb_raise(cError, "Query is closed");
387
+
388
+ if (!query->stmt)
389
+ prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
390
+
391
+ query_ctx ctx = { self, query->sqlite3_db, query->stmt, parameters, QUERY_MODE(QUERY_MULTI_ROW), ALL_ROWS };
392
+ return safe_execute_multi(&ctx);
393
+ }
394
+
395
+ /* Returns the database associated with the query.
396
+ *
397
+ * @overload database()
398
+ * @return [Extralite::Database] associated database
399
+ * @overload db()
400
+ * @return [Extralite::Database] associated database
401
+ */
402
+ VALUE Query_database(VALUE self) {
403
+ Query_t *query = self_to_query(self);
404
+ return query->db;
405
+ }
406
+
407
+ /* Returns the SQL string for the query.
408
+ *
409
+ * @return [String] SQL string
410
+ */
411
+ VALUE Query_sql(VALUE self) {
412
+ Query_t *query = self_to_query(self);
413
+ return query->sql;
414
+ }
415
+
416
+ /* Returns the column names for the query without running it.
417
+ *
418
+ * @return [Array<Symbol>] column names
419
+ */
420
+ VALUE Query_columns(VALUE self) {
421
+ Query_t *query = self_to_query(self);
422
+ query_reset(query);
423
+ return Query_perform_next(self, ALL_ROWS, safe_query_columns);
424
+ }
425
+
426
+ /* Closes the query. Attempting to run a closed query will raise an error.
427
+ *
428
+ * @return [Extralite::Query] self
429
+ */
430
+ VALUE Query_close(VALUE self) {
431
+ Query_t *query = self_to_query(self);
432
+ if (query->stmt) {
433
+ sqlite3_finalize(query->stmt);
434
+ query->stmt = NULL;
435
+ }
436
+ query->closed = 1;
437
+ return self;
438
+ }
439
+
440
+ /* Returns true if the query is closed.
441
+ *
442
+ * @return [boolean] true if query is closed
443
+ */
444
+ VALUE Query_closed_p(VALUE self) {
445
+ Query_t *query = self_to_query(self);
446
+ return query->closed ? Qtrue : Qfalse;
447
+ }
448
+
449
+ /* Returns the current [status
450
+ * value](https://sqlite.org/c3ref/c_stmtstatus_counter.html) for the given op.
451
+ * To reset the value, pass true as reset.
452
+ *
453
+ * @overload status(op)
454
+ * @param op [Integer] status op
455
+ * @return [Integer] current status value for the given op
456
+ * @overload status(op, reset)
457
+ * @param op [Integer] status op
458
+ * @param reset [true] reset flag
459
+ * @return [Integer] current status value for the given op (before reset)
460
+ */
461
+ VALUE Query_status(int argc, VALUE* argv, VALUE self) {
462
+ VALUE op, reset;
463
+
464
+ rb_scan_args(argc, argv, "11", &op, &reset);
465
+
466
+ Query_t *query = self_to_query(self);
467
+ if (query->closed) rb_raise(cError, "Query is closed");
468
+
469
+ if (!query->stmt)
470
+ prepare_single_stmt(query->sqlite3_db, &query->stmt, query->sql);
471
+
472
+ int value = sqlite3_stmt_status(query->stmt, NUM2INT(op), RTEST(reset) ? 1 : 0);
473
+ return INT2NUM(value);
474
+ }
475
+
476
+ /* Returns a short string representation of the query instance, including the
477
+ * SQL string.
478
+ *
479
+ * @return [String] string representation
480
+ */
481
+ VALUE Query_inspect(VALUE self) {
482
+ VALUE cname = rb_class_name(CLASS_OF(self));
483
+ VALUE sql = self_to_query(self)->sql;
484
+ if (RSTRING_LEN(sql) > 48) {
485
+ sql = rb_funcall(sql, ID_slice, 2, INT2FIX(0), INT2FIX(45));
486
+ rb_str_cat2(sql, "...");
487
+ }
488
+ sql = rb_funcall(sql, ID_inspect, 0);
489
+
490
+ RB_GC_GUARD(sql);
491
+ return rb_sprintf("#<%"PRIsVALUE":%p %"PRIsVALUE">", cname, (void*)self, sql);
492
+ }
493
+
494
+ void Init_ExtraliteQuery(void) {
495
+ VALUE mExtralite = rb_define_module("Extralite");
496
+
497
+ cQuery = rb_define_class_under(mExtralite, "Query", rb_cObject);
498
+ rb_define_alloc_func(cQuery, Query_allocate);
499
+
500
+ rb_define_method(cQuery, "bind", Query_bind, -1);
501
+ rb_define_method(cQuery, "close", Query_close, 0);
502
+ rb_define_method(cQuery, "closed?", Query_closed_p, 0);
503
+ rb_define_method(cQuery, "columns", Query_columns, 0);
504
+ rb_define_method(cQuery, "database", Query_database, 0);
505
+ rb_define_method(cQuery, "db", Query_database, 0);
506
+
507
+ rb_define_method(cQuery, "each", Query_each_hash, 0);
508
+ rb_define_method(cQuery, "each_ary", Query_each_ary, 0);
509
+ rb_define_method(cQuery, "each_hash", Query_each_hash, 0);
510
+ rb_define_method(cQuery, "each_single_column", Query_each_single_column, 0);
511
+
512
+ rb_define_method(cQuery, "eof?", Query_eof_p, 0);
513
+ rb_define_method(cQuery, "execute", Query_execute, -1);
514
+ rb_define_method(cQuery, "execute_multi", Query_execute_multi, 1);
515
+ rb_define_method(cQuery, "initialize", Query_initialize, 2);
516
+ rb_define_method(cQuery, "inspect", Query_inspect, 0);
517
+
518
+ rb_define_method(cQuery, "next", Query_next_hash, -1);
519
+ rb_define_method(cQuery, "next_ary", Query_next_ary, -1);
520
+ rb_define_method(cQuery, "next_hash", Query_next_hash, -1);
521
+ rb_define_method(cQuery, "next_single_column", Query_next_single_column, -1);
522
+
523
+ rb_define_method(cQuery, "reset", Query_reset, 0);
524
+ rb_define_method(cQuery, "sql", Query_sql, 0);
525
+ rb_define_method(cQuery, "status", Query_status, -1);
526
+
527
+ rb_define_method(cQuery, "to_a", Query_to_a_hash, 0);
528
+ rb_define_method(cQuery, "to_a_ary", Query_to_a_ary, 0);
529
+ rb_define_method(cQuery, "to_a_hash", Query_to_a_hash, 0);
530
+ rb_define_method(cQuery, "to_a_single_column", Query_to_a_single_column, 0);
531
+
532
+ ID_inspect = rb_intern("inspect");
533
+ ID_slice = rb_intern("slice");
534
+ }
@@ -46,7 +46,7 @@ module Extralite
46
46
  SQLITE_LIMIT_VARIABLE_NUMBER = 9
47
47
  SQLITE_LIMIT_TRIGGER_DEPTH = 10
48
48
  SQLITE_LIMIT_WORKER_THREADS = 11
49
-
49
+
50
50
  SQLITE_OK = 0
51
51
  SQLITE_ERROR = 1
52
52
  SQLITE_INTERNAL = 2
@@ -1,4 +1,4 @@
1
1
  module Extralite
2
2
  # Extralite version
3
- VERSION = '1.27'
3
+ VERSION = '2.1'
4
4
  end
data/lib/extralite.rb CHANGED
@@ -27,8 +27,6 @@ module Extralite
27
27
 
28
28
  # An SQLite database
29
29
  class Database
30
- alias_method :execute, :query
31
-
32
30
  # @!visibility private
33
31
  TABLES_SQL = <<~SQL
34
32
  SELECT name FROM sqlite_master