extralite 1.27 → 2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +1 -1
- data/LICENSE +1 -1
- data/README.md +40 -14
- data/TODO.md +21 -0
- data/ext/extralite/common.c +80 -58
- data/ext/extralite/database.c +138 -78
- data/ext/extralite/extconf.rb +16 -16
- data/ext/extralite/extralite.h +63 -17
- data/ext/extralite/extralite_ext.c +4 -2
- data/ext/extralite/iterator.c +208 -0
- data/ext/extralite/query.c +534 -0
- data/lib/extralite/sqlite3_constants.rb +1 -1
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +0 -2
- data/lib/sequel/adapters/extralite.rb +104 -106
- data/test/perf_prepared.rb +2 -2
- data/test/test_database.rb +35 -9
- data/test/test_extralite.rb +1 -1
- data/test/test_iterator.rb +104 -0
- data/test/test_query.rb +519 -0
- data/test/test_sequel.rb +23 -4
- metadata +6 -4
- data/ext/extralite/prepared_statement.c +0 -333
- data/test/test_prepared_statement.rb +0 -225
@@ -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
|
+
}
|
data/lib/extralite/version.rb
CHANGED