extralite 1.27 → 2.0

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