aim-sqlite3 2.9.6.1-aarch64-linux-gnu
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 +7 -0
- data/.gemtest +0 -0
- data/CHANGELOG.md +1062 -0
- data/CONTRIBUTING.md +130 -0
- data/FAQ.md +399 -0
- data/INSTALLATION.md +267 -0
- data/LICENSE +23 -0
- data/README.md +222 -0
- data/dependencies.yml +13 -0
- data/ext/sqlite3/aggregator.c +314 -0
- data/ext/sqlite3/aggregator.h +14 -0
- data/ext/sqlite3/backup.c +190 -0
- data/ext/sqlite3/backup.h +15 -0
- data/ext/sqlite3/database.c +1274 -0
- data/ext/sqlite3/database.h +36 -0
- data/ext/sqlite3/exception.c +122 -0
- data/ext/sqlite3/exception.h +12 -0
- data/ext/sqlite3/extconf.rb +299 -0
- data/ext/sqlite3/gvl.c +154 -0
- data/ext/sqlite3/gvl.h +16 -0
- data/ext/sqlite3/sqlite3.c +225 -0
- data/ext/sqlite3/sqlite3_ruby.h +49 -0
- data/ext/sqlite3/statement.c +755 -0
- data/ext/sqlite3/statement.h +17 -0
- data/ext/sqlite3/timespec.h +20 -0
- data/lib/sqlite3/3.2/sqlite3_native.so +0 -0
- data/lib/sqlite3/3.3/sqlite3_native.so +0 -0
- data/lib/sqlite3/3.4/sqlite3_native.so +0 -0
- data/lib/sqlite3/4.0/sqlite3_native.so +0 -0
- data/lib/sqlite3/aim_version.rb +5 -0
- data/lib/sqlite3/cli.rb +8 -0
- data/lib/sqlite3/constants.rb +198 -0
- data/lib/sqlite3/database.rb +798 -0
- data/lib/sqlite3/errors.rb +88 -0
- data/lib/sqlite3/fork_safety.rb +66 -0
- data/lib/sqlite3/pragmas.rb +648 -0
- data/lib/sqlite3/resultset.rb +96 -0
- data/lib/sqlite3/statement.rb +208 -0
- data/lib/sqlite3/value.rb +54 -0
- data/lib/sqlite3/version.rb +4 -0
- data/lib/sqlite3/version_info.rb +17 -0
- data/lib/sqlite3.rb +21 -0
- data/ports/aarch64-linux-gnu/sqlite3/3.53.2/bin/sqlite3 +0 -0
- metadata +106 -0
|
@@ -0,0 +1,755 @@
|
|
|
1
|
+
#include <sqlite3_ruby.h>
|
|
2
|
+
|
|
3
|
+
#define REQUIRE_OPEN_STMT(_ctxt) \
|
|
4
|
+
if (!_ctxt->st) \
|
|
5
|
+
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed statement");
|
|
6
|
+
|
|
7
|
+
#define REQUIRE_LIVE_DB(_ctxt) \
|
|
8
|
+
if (_ctxt->db->flags & SQLITE3_RB_DATABASE_DISCARDED) \
|
|
9
|
+
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a statement associated with a discarded database");
|
|
10
|
+
|
|
11
|
+
VALUE cSqlite3Statement;
|
|
12
|
+
|
|
13
|
+
static void
|
|
14
|
+
statement_deallocate(void *data)
|
|
15
|
+
{
|
|
16
|
+
sqlite3StmtRubyPtr s = (sqlite3StmtRubyPtr)data;
|
|
17
|
+
|
|
18
|
+
if (s->st) {
|
|
19
|
+
sqlite3_finalize(s->st);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
xfree(data);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static size_t
|
|
26
|
+
statement_memsize(const void *data)
|
|
27
|
+
{
|
|
28
|
+
const sqlite3StmtRubyPtr s = (const sqlite3StmtRubyPtr)data;
|
|
29
|
+
// NB: can't account for s->st because the type is incomplete.
|
|
30
|
+
return sizeof(*s);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static const rb_data_type_t statement_type = {
|
|
34
|
+
"SQLite3::Backup",
|
|
35
|
+
{
|
|
36
|
+
NULL,
|
|
37
|
+
statement_deallocate,
|
|
38
|
+
statement_memsize,
|
|
39
|
+
},
|
|
40
|
+
0,
|
|
41
|
+
0,
|
|
42
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
static VALUE
|
|
46
|
+
allocate(VALUE klass)
|
|
47
|
+
{
|
|
48
|
+
sqlite3StmtRubyPtr ctx;
|
|
49
|
+
return TypedData_Make_Struct(klass, sqlite3StmtRuby, &statement_type, ctx);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static VALUE
|
|
53
|
+
prepare(VALUE self, VALUE db, VALUE sql)
|
|
54
|
+
{
|
|
55
|
+
sqlite3RubyPtr db_ctx = sqlite3_database_unwrap(db);
|
|
56
|
+
sqlite3StmtRubyPtr ctx;
|
|
57
|
+
const char *tail = NULL;
|
|
58
|
+
int status;
|
|
59
|
+
|
|
60
|
+
StringValue(sql);
|
|
61
|
+
|
|
62
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
63
|
+
|
|
64
|
+
/* Dereferencing a pointer to the database struct will be faster than accessing it through the
|
|
65
|
+
* instance variable @connection. The struct pointer is guaranteed to be live because instance
|
|
66
|
+
* variable will keep it from being GCed. */
|
|
67
|
+
ctx->db = db_ctx;
|
|
68
|
+
|
|
69
|
+
#ifdef HAVE_SQLITE3_PREPARE_V2
|
|
70
|
+
status = sqlite3_prepare_v2(
|
|
71
|
+
#else
|
|
72
|
+
status = sqlite3_prepare(
|
|
73
|
+
#endif
|
|
74
|
+
db_ctx->db,
|
|
75
|
+
(const char *)StringValuePtr(sql),
|
|
76
|
+
(int)RSTRING_LEN(sql),
|
|
77
|
+
&ctx->st,
|
|
78
|
+
&tail
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
CHECK_PREPARE(db_ctx->db, status, StringValuePtr(sql));
|
|
82
|
+
timespecclear(&db_ctx->stmt_deadline);
|
|
83
|
+
|
|
84
|
+
return rb_utf8_str_new_cstr(tail);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* call-seq: stmt.close
|
|
88
|
+
*
|
|
89
|
+
* Closes the statement by finalizing the underlying statement
|
|
90
|
+
* handle. The statement must not be used after being closed.
|
|
91
|
+
*/
|
|
92
|
+
static VALUE
|
|
93
|
+
sqlite3_rb_close(VALUE self)
|
|
94
|
+
{
|
|
95
|
+
sqlite3StmtRubyPtr ctx;
|
|
96
|
+
|
|
97
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
98
|
+
|
|
99
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
100
|
+
|
|
101
|
+
sqlite3_finalize(ctx->st);
|
|
102
|
+
ctx->st = NULL;
|
|
103
|
+
|
|
104
|
+
return self;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* call-seq: stmt.closed?
|
|
108
|
+
*
|
|
109
|
+
* Returns true if the statement has been closed.
|
|
110
|
+
*/
|
|
111
|
+
static VALUE
|
|
112
|
+
closed_p(VALUE self)
|
|
113
|
+
{
|
|
114
|
+
sqlite3StmtRubyPtr ctx;
|
|
115
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
116
|
+
|
|
117
|
+
if (!ctx->st) { return Qtrue; }
|
|
118
|
+
|
|
119
|
+
return Qfalse;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static VALUE
|
|
123
|
+
step(VALUE self)
|
|
124
|
+
{
|
|
125
|
+
sqlite3StmtRubyPtr ctx;
|
|
126
|
+
sqlite3_stmt *stmt;
|
|
127
|
+
int value, length;
|
|
128
|
+
VALUE list;
|
|
129
|
+
rb_encoding *internal_encoding;
|
|
130
|
+
|
|
131
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
132
|
+
|
|
133
|
+
REQUIRE_LIVE_DB(ctx);
|
|
134
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
135
|
+
|
|
136
|
+
if (ctx->done_p) { return Qnil; }
|
|
137
|
+
|
|
138
|
+
internal_encoding = rb_default_internal_encoding();
|
|
139
|
+
|
|
140
|
+
stmt = ctx->st;
|
|
141
|
+
|
|
142
|
+
value = rb_sqlite3_step_without_gvl(stmt);
|
|
143
|
+
if (rb_errinfo() != Qnil) {
|
|
144
|
+
/* some user defined function was invoked as a callback during step and
|
|
145
|
+
* it raised an exception that has been suppressed until step returns.
|
|
146
|
+
* Now re-raise it. */
|
|
147
|
+
VALUE exception = rb_errinfo();
|
|
148
|
+
rb_set_errinfo(Qnil);
|
|
149
|
+
rb_exc_raise(exception);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
length = sqlite3_column_count(stmt);
|
|
153
|
+
list = rb_ary_new2((long)length);
|
|
154
|
+
|
|
155
|
+
switch (value) {
|
|
156
|
+
case SQLITE_ROW: {
|
|
157
|
+
int i;
|
|
158
|
+
for (i = 0; i < length; i++) {
|
|
159
|
+
VALUE val;
|
|
160
|
+
|
|
161
|
+
switch (sqlite3_column_type(stmt, i)) {
|
|
162
|
+
case SQLITE_INTEGER:
|
|
163
|
+
val = LL2NUM(sqlite3_column_int64(stmt, i));
|
|
164
|
+
break;
|
|
165
|
+
case SQLITE_FLOAT:
|
|
166
|
+
val = rb_float_new(sqlite3_column_double(stmt, i));
|
|
167
|
+
break;
|
|
168
|
+
case SQLITE_TEXT: {
|
|
169
|
+
val = rb_utf8_str_new(
|
|
170
|
+
(const char *)sqlite3_column_text(stmt, i),
|
|
171
|
+
(long)sqlite3_column_bytes(stmt, i)
|
|
172
|
+
);
|
|
173
|
+
if (internal_encoding) {
|
|
174
|
+
val = rb_str_export_to_enc(val, internal_encoding);
|
|
175
|
+
}
|
|
176
|
+
rb_obj_freeze(val);
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
case SQLITE_BLOB: {
|
|
180
|
+
val = rb_str_new(
|
|
181
|
+
(const char *)sqlite3_column_blob(stmt, i),
|
|
182
|
+
(long)sqlite3_column_bytes(stmt, i)
|
|
183
|
+
);
|
|
184
|
+
rb_obj_freeze(val);
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
case SQLITE_NULL:
|
|
188
|
+
val = Qnil;
|
|
189
|
+
break;
|
|
190
|
+
default:
|
|
191
|
+
rb_raise(rb_eRuntimeError, "bad type");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
rb_ary_store(list, (long)i, val);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
case SQLITE_DONE:
|
|
199
|
+
ctx->done_p = 1;
|
|
200
|
+
return Qnil;
|
|
201
|
+
break;
|
|
202
|
+
default:
|
|
203
|
+
sqlite3_reset(stmt);
|
|
204
|
+
ctx->done_p = 0;
|
|
205
|
+
CHECK(sqlite3_db_handle(ctx->st), value);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
rb_obj_freeze(list);
|
|
209
|
+
|
|
210
|
+
return list;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* call-seq: stmt.bind_param(key, value)
|
|
214
|
+
*
|
|
215
|
+
* Binds value to the named (or positional) placeholder. If +param+ is a
|
|
216
|
+
* Fixnum, it is treated as an index for a positional placeholder.
|
|
217
|
+
* Otherwise it is used as the name of the placeholder to bind to.
|
|
218
|
+
*
|
|
219
|
+
* Type mapping from Ruby to SQLite3:
|
|
220
|
+
* - +nil+ → NULL
|
|
221
|
+
* - Integer → INTEGER (or REAL when outside the signed int64 range)
|
|
222
|
+
* - Float → REAL
|
|
223
|
+
* - SQLite3::Blob → BLOB
|
|
224
|
+
* - String with Encoding::ASCII_8BIT (a.k.a. BINARY) → BLOB
|
|
225
|
+
* - String with Encoding::UTF_16LE or Encoding::UTF_16BE → TEXT (bound as UTF-16)
|
|
226
|
+
* - String (all other encodings) → TEXT (re-encoded to UTF-8 if necessary)
|
|
227
|
+
* - Any other type → raises RuntimeError
|
|
228
|
+
*
|
|
229
|
+
* Note: if you have a string with only ASCII characters but ASCII-8BIT
|
|
230
|
+
* encoding and want it bound as TEXT rather than BLOB, re-encode it first:
|
|
231
|
+
*
|
|
232
|
+
* stmt.bind_param(1, my_binary_str.encode(Encoding::UTF_8))
|
|
233
|
+
*
|
|
234
|
+
* See also #bind_params.
|
|
235
|
+
*/
|
|
236
|
+
static VALUE
|
|
237
|
+
bind_param(VALUE self, VALUE key, VALUE value)
|
|
238
|
+
{
|
|
239
|
+
sqlite3StmtRubyPtr ctx;
|
|
240
|
+
int status;
|
|
241
|
+
int index;
|
|
242
|
+
|
|
243
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
244
|
+
|
|
245
|
+
REQUIRE_LIVE_DB(ctx);
|
|
246
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
247
|
+
|
|
248
|
+
switch (TYPE(key)) {
|
|
249
|
+
case T_SYMBOL:
|
|
250
|
+
key = rb_funcall(key, rb_intern("to_s"), 0);
|
|
251
|
+
case T_STRING:
|
|
252
|
+
if (RSTRING_PTR(key)[0] != ':') { key = rb_str_plus(rb_str_new2(":"), key); }
|
|
253
|
+
index = sqlite3_bind_parameter_index(ctx->st, StringValuePtr(key));
|
|
254
|
+
break;
|
|
255
|
+
default:
|
|
256
|
+
index = (int)NUM2INT(key);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (index == 0) {
|
|
260
|
+
rb_raise(rb_path2class("SQLite3::Exception"), "no such bind parameter");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
switch (TYPE(value)) {
|
|
264
|
+
case T_STRING:
|
|
265
|
+
if (CLASS_OF(value) == cSqlite3Blob
|
|
266
|
+
|| rb_enc_get_index(value) == rb_ascii8bit_encindex()
|
|
267
|
+
) {
|
|
268
|
+
status = sqlite3_bind_blob(
|
|
269
|
+
ctx->st,
|
|
270
|
+
index,
|
|
271
|
+
(const char *)StringValuePtr(value),
|
|
272
|
+
(int)RSTRING_LEN(value),
|
|
273
|
+
SQLITE_TRANSIENT
|
|
274
|
+
);
|
|
275
|
+
} else {
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
if (UTF16_LE_P(value) || UTF16_BE_P(value)) {
|
|
279
|
+
status = sqlite3_bind_text16(
|
|
280
|
+
ctx->st,
|
|
281
|
+
index,
|
|
282
|
+
(const char *)StringValuePtr(value),
|
|
283
|
+
(int)RSTRING_LEN(value),
|
|
284
|
+
SQLITE_TRANSIENT
|
|
285
|
+
);
|
|
286
|
+
} else {
|
|
287
|
+
if (!UTF8_P(value) || !USASCII_P(value)) {
|
|
288
|
+
value = rb_str_encode(value, rb_enc_from_encoding(rb_utf8_encoding()), 0, Qnil);
|
|
289
|
+
}
|
|
290
|
+
status = sqlite3_bind_text(
|
|
291
|
+
ctx->st,
|
|
292
|
+
index,
|
|
293
|
+
(const char *)StringValuePtr(value),
|
|
294
|
+
(int)RSTRING_LEN(value),
|
|
295
|
+
SQLITE_TRANSIENT
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
case T_BIGNUM: {
|
|
301
|
+
sqlite3_int64 num64;
|
|
302
|
+
if (bignum_to_int64(value, &num64)) {
|
|
303
|
+
status = sqlite3_bind_int64(ctx->st, index, num64);
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
case T_FLOAT:
|
|
308
|
+
status = sqlite3_bind_double(ctx->st, index, NUM2DBL(value));
|
|
309
|
+
break;
|
|
310
|
+
case T_FIXNUM:
|
|
311
|
+
status = sqlite3_bind_int64(ctx->st, index, (sqlite3_int64)FIX2LONG(value));
|
|
312
|
+
break;
|
|
313
|
+
case T_NIL:
|
|
314
|
+
status = sqlite3_bind_null(ctx->st, index);
|
|
315
|
+
break;
|
|
316
|
+
default:
|
|
317
|
+
rb_raise(rb_eRuntimeError, "can't prepare %s",
|
|
318
|
+
rb_class2name(CLASS_OF(value)));
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
CHECK(sqlite3_db_handle(ctx->st), status);
|
|
323
|
+
|
|
324
|
+
return self;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/* call-seq: stmt.reset!
|
|
328
|
+
*
|
|
329
|
+
* Resets the statement. This is typically done internally, though it might
|
|
330
|
+
* occasionally be necessary to manually reset the statement.
|
|
331
|
+
*/
|
|
332
|
+
static VALUE
|
|
333
|
+
reset_bang(VALUE self)
|
|
334
|
+
{
|
|
335
|
+
sqlite3StmtRubyPtr ctx;
|
|
336
|
+
|
|
337
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
338
|
+
|
|
339
|
+
REQUIRE_LIVE_DB(ctx);
|
|
340
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
341
|
+
|
|
342
|
+
sqlite3_reset(ctx->st);
|
|
343
|
+
timespecclear(&ctx->db->stmt_deadline);
|
|
344
|
+
|
|
345
|
+
ctx->done_p = 0;
|
|
346
|
+
|
|
347
|
+
return self;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* call-seq: stmt.clear_bindings!
|
|
351
|
+
*
|
|
352
|
+
* Resets the statement. This is typically done internally, though it might
|
|
353
|
+
* occasionally be necessary to manually reset the statement.
|
|
354
|
+
*/
|
|
355
|
+
static VALUE
|
|
356
|
+
clear_bindings_bang(VALUE self)
|
|
357
|
+
{
|
|
358
|
+
sqlite3StmtRubyPtr ctx;
|
|
359
|
+
|
|
360
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
361
|
+
|
|
362
|
+
REQUIRE_LIVE_DB(ctx);
|
|
363
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
364
|
+
|
|
365
|
+
sqlite3_clear_bindings(ctx->st);
|
|
366
|
+
|
|
367
|
+
ctx->done_p = 0;
|
|
368
|
+
|
|
369
|
+
return self;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* call-seq: stmt.done?
|
|
373
|
+
*
|
|
374
|
+
* returns true if all rows have been returned.
|
|
375
|
+
*/
|
|
376
|
+
static VALUE
|
|
377
|
+
done_p(VALUE self)
|
|
378
|
+
{
|
|
379
|
+
sqlite3StmtRubyPtr ctx;
|
|
380
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
381
|
+
|
|
382
|
+
if (ctx->done_p) { return Qtrue; }
|
|
383
|
+
return Qfalse;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/* call-seq: stmt.column_count
|
|
387
|
+
*
|
|
388
|
+
* Returns the number of columns to be returned for this statement
|
|
389
|
+
*/
|
|
390
|
+
static VALUE
|
|
391
|
+
column_count(VALUE self)
|
|
392
|
+
{
|
|
393
|
+
sqlite3StmtRubyPtr ctx;
|
|
394
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
395
|
+
|
|
396
|
+
REQUIRE_LIVE_DB(ctx);
|
|
397
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
398
|
+
|
|
399
|
+
return INT2NUM(sqlite3_column_count(ctx->st));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
#if HAVE_RB_ENC_INTERNED_STR_CSTR
|
|
403
|
+
static VALUE
|
|
404
|
+
interned_utf8_cstr(const char *str)
|
|
405
|
+
{
|
|
406
|
+
return rb_enc_interned_str_cstr(str, rb_utf8_encoding());
|
|
407
|
+
}
|
|
408
|
+
#else
|
|
409
|
+
static VALUE
|
|
410
|
+
interned_utf8_cstr(const char *str)
|
|
411
|
+
{
|
|
412
|
+
VALUE rb_str = rb_utf8_str_new_cstr(str);
|
|
413
|
+
return rb_funcall(rb_str, rb_intern("-@"), 0);
|
|
414
|
+
}
|
|
415
|
+
#endif
|
|
416
|
+
|
|
417
|
+
/* call-seq: stmt.column_name(index)
|
|
418
|
+
*
|
|
419
|
+
* Get the column name at +index+. 0 based.
|
|
420
|
+
*/
|
|
421
|
+
static VALUE
|
|
422
|
+
column_name(VALUE self, VALUE index)
|
|
423
|
+
{
|
|
424
|
+
sqlite3StmtRubyPtr ctx;
|
|
425
|
+
const char *name;
|
|
426
|
+
|
|
427
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
428
|
+
|
|
429
|
+
REQUIRE_LIVE_DB(ctx);
|
|
430
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
431
|
+
|
|
432
|
+
name = sqlite3_column_name(ctx->st, (int)NUM2INT(index));
|
|
433
|
+
|
|
434
|
+
VALUE ret = Qnil;
|
|
435
|
+
|
|
436
|
+
if (name) {
|
|
437
|
+
ret = interned_utf8_cstr(name);
|
|
438
|
+
}
|
|
439
|
+
return ret;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/* call-seq: stmt.column_decltype(index)
|
|
443
|
+
*
|
|
444
|
+
* Get the column type at +index+. 0 based.
|
|
445
|
+
*/
|
|
446
|
+
static VALUE
|
|
447
|
+
column_decltype(VALUE self, VALUE index)
|
|
448
|
+
{
|
|
449
|
+
sqlite3StmtRubyPtr ctx;
|
|
450
|
+
const char *name;
|
|
451
|
+
|
|
452
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
453
|
+
|
|
454
|
+
REQUIRE_LIVE_DB(ctx);
|
|
455
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
456
|
+
|
|
457
|
+
name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
|
|
458
|
+
|
|
459
|
+
if (name) { return rb_str_new2(name); }
|
|
460
|
+
return Qnil;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/* call-seq: stmt.bind_parameter_count
|
|
464
|
+
*
|
|
465
|
+
* Return the number of bind parameters
|
|
466
|
+
*/
|
|
467
|
+
static VALUE
|
|
468
|
+
bind_parameter_count(VALUE self)
|
|
469
|
+
{
|
|
470
|
+
sqlite3StmtRubyPtr ctx;
|
|
471
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
472
|
+
|
|
473
|
+
REQUIRE_LIVE_DB(ctx);
|
|
474
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
475
|
+
|
|
476
|
+
return INT2NUM(sqlite3_bind_parameter_count(ctx->st));
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/** call-seq: stmt.named_params
|
|
480
|
+
*
|
|
481
|
+
* Return the list of named parameters in the statement.
|
|
482
|
+
* This returns a frozen array of strings (without the leading prefix character).
|
|
483
|
+
* The values of this list can be used to bind parameters
|
|
484
|
+
* to the statement using bind_param. Positional (?NNN) and anonymous (?)
|
|
485
|
+
* parameters are excluded.
|
|
486
|
+
*
|
|
487
|
+
*/
|
|
488
|
+
static VALUE
|
|
489
|
+
named_params(VALUE self)
|
|
490
|
+
{
|
|
491
|
+
sqlite3StmtRubyPtr ctx;
|
|
492
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
493
|
+
|
|
494
|
+
REQUIRE_LIVE_DB(ctx);
|
|
495
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
496
|
+
|
|
497
|
+
int param_count = sqlite3_bind_parameter_count(ctx->st);
|
|
498
|
+
VALUE params = rb_ary_new2(param_count);
|
|
499
|
+
|
|
500
|
+
// The first host parameter has an index of 1, not 0.
|
|
501
|
+
for (int i = 1; i <= param_count; i++) {
|
|
502
|
+
const char *name = sqlite3_bind_parameter_name(ctx->st, i);
|
|
503
|
+
// We ignore positional and anonymous parameters, and also null values, since there can be
|
|
504
|
+
// gaps in the list.
|
|
505
|
+
if (name && *name != '?') {
|
|
506
|
+
VALUE param = interned_utf8_cstr(name + 1);
|
|
507
|
+
rb_ary_push(params, param);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return rb_obj_freeze(params);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
enum stmt_stat_sym {
|
|
514
|
+
stmt_stat_sym_fullscan_steps,
|
|
515
|
+
stmt_stat_sym_sorts,
|
|
516
|
+
stmt_stat_sym_autoindexes,
|
|
517
|
+
stmt_stat_sym_vm_steps,
|
|
518
|
+
#ifdef SQLITE_STMTSTATUS_REPREPARE
|
|
519
|
+
stmt_stat_sym_reprepares,
|
|
520
|
+
#endif
|
|
521
|
+
#ifdef SQLITE_STMTSTATUS_RUN
|
|
522
|
+
stmt_stat_sym_runs,
|
|
523
|
+
#endif
|
|
524
|
+
#ifdef SQLITE_STMTSTATUS_FILTER_MISS
|
|
525
|
+
stmt_stat_sym_filter_misses,
|
|
526
|
+
#endif
|
|
527
|
+
#ifdef SQLITE_STMTSTATUS_FILTER_HIT
|
|
528
|
+
stmt_stat_sym_filter_hits,
|
|
529
|
+
#endif
|
|
530
|
+
stmt_stat_sym_last
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
static VALUE stmt_stat_symbols[stmt_stat_sym_last];
|
|
534
|
+
|
|
535
|
+
static void
|
|
536
|
+
setup_stmt_stat_symbols(void)
|
|
537
|
+
{
|
|
538
|
+
if (stmt_stat_symbols[0] == 0) {
|
|
539
|
+
#define S(s) stmt_stat_symbols[stmt_stat_sym_##s] = ID2SYM(rb_intern_const(#s))
|
|
540
|
+
S(fullscan_steps);
|
|
541
|
+
S(sorts);
|
|
542
|
+
S(autoindexes);
|
|
543
|
+
S(vm_steps);
|
|
544
|
+
#ifdef SQLITE_STMTSTATUS_REPREPARE
|
|
545
|
+
S(reprepares);
|
|
546
|
+
#endif
|
|
547
|
+
#ifdef SQLITE_STMTSTATUS_RUN
|
|
548
|
+
S(runs);
|
|
549
|
+
#endif
|
|
550
|
+
#ifdef SQLITE_STMTSTATUS_FILTER_MISS
|
|
551
|
+
S(filter_misses);
|
|
552
|
+
#endif
|
|
553
|
+
#ifdef SQLITE_STMTSTATUS_FILTER_HIT
|
|
554
|
+
S(filter_hits);
|
|
555
|
+
#endif
|
|
556
|
+
#undef S
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
static size_t
|
|
561
|
+
stmt_stat_internal(VALUE hash_or_sym, sqlite3_stmt *stmt)
|
|
562
|
+
{
|
|
563
|
+
VALUE hash = Qnil, key = Qnil;
|
|
564
|
+
|
|
565
|
+
setup_stmt_stat_symbols();
|
|
566
|
+
|
|
567
|
+
if (RB_TYPE_P(hash_or_sym, T_HASH)) {
|
|
568
|
+
hash = hash_or_sym;
|
|
569
|
+
} else if (SYMBOL_P(hash_or_sym)) {
|
|
570
|
+
key = hash_or_sym;
|
|
571
|
+
} else {
|
|
572
|
+
rb_raise(rb_eTypeError, "non-hash or symbol argument");
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
#define SET(name, stat_type) \
|
|
576
|
+
if (key == stmt_stat_symbols[stmt_stat_sym_##name]) \
|
|
577
|
+
return sqlite3_stmt_status(stmt, stat_type, 0); \
|
|
578
|
+
else if (hash != Qnil) \
|
|
579
|
+
rb_hash_aset(hash, stmt_stat_symbols[stmt_stat_sym_##name], SIZET2NUM(sqlite3_stmt_status(stmt, stat_type, 0)));
|
|
580
|
+
|
|
581
|
+
SET(fullscan_steps, SQLITE_STMTSTATUS_FULLSCAN_STEP);
|
|
582
|
+
SET(sorts, SQLITE_STMTSTATUS_SORT);
|
|
583
|
+
SET(autoindexes, SQLITE_STMTSTATUS_AUTOINDEX);
|
|
584
|
+
SET(vm_steps, SQLITE_STMTSTATUS_VM_STEP);
|
|
585
|
+
#ifdef SQLITE_STMTSTATUS_REPREPARE
|
|
586
|
+
SET(reprepares, SQLITE_STMTSTATUS_REPREPARE);
|
|
587
|
+
#endif
|
|
588
|
+
#ifdef SQLITE_STMTSTATUS_RUN
|
|
589
|
+
SET(runs, SQLITE_STMTSTATUS_RUN);
|
|
590
|
+
#endif
|
|
591
|
+
#ifdef SQLITE_STMTSTATUS_FILTER_MISS
|
|
592
|
+
SET(filter_misses, SQLITE_STMTSTATUS_FILTER_MISS);
|
|
593
|
+
#endif
|
|
594
|
+
#ifdef SQLITE_STMTSTATUS_FILTER_HIT
|
|
595
|
+
SET(filter_hits, SQLITE_STMTSTATUS_FILTER_HIT);
|
|
596
|
+
#endif
|
|
597
|
+
#undef SET
|
|
598
|
+
|
|
599
|
+
if (!NIL_P(key)) { /* matched key should return above */
|
|
600
|
+
rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key));
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return 0;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/* call-seq: stmt.stats_as_hash(hash)
|
|
607
|
+
*
|
|
608
|
+
* Returns a Hash containing information about the statement.
|
|
609
|
+
*/
|
|
610
|
+
static VALUE
|
|
611
|
+
stats_as_hash(VALUE self)
|
|
612
|
+
{
|
|
613
|
+
sqlite3StmtRubyPtr ctx;
|
|
614
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
615
|
+
|
|
616
|
+
REQUIRE_LIVE_DB(ctx);
|
|
617
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
618
|
+
|
|
619
|
+
VALUE arg = rb_hash_new();
|
|
620
|
+
|
|
621
|
+
stmt_stat_internal(arg, ctx->st);
|
|
622
|
+
return arg;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/* call-seq: stmt.stmt_stat(hash_or_key)
|
|
626
|
+
*
|
|
627
|
+
* Returns a Hash containing information about the statement.
|
|
628
|
+
*/
|
|
629
|
+
static VALUE
|
|
630
|
+
stat_for(VALUE self, VALUE key)
|
|
631
|
+
{
|
|
632
|
+
sqlite3StmtRubyPtr ctx;
|
|
633
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
634
|
+
|
|
635
|
+
REQUIRE_LIVE_DB(ctx);
|
|
636
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
637
|
+
|
|
638
|
+
if (SYMBOL_P(key)) {
|
|
639
|
+
size_t value = stmt_stat_internal(key, ctx->st);
|
|
640
|
+
return SIZET2NUM(value);
|
|
641
|
+
} else {
|
|
642
|
+
rb_raise(rb_eTypeError, "non-symbol given");
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
#ifdef SQLITE_STMTSTATUS_MEMUSED
|
|
647
|
+
/* call-seq: stmt.memused
|
|
648
|
+
*
|
|
649
|
+
* Return the approximate number of bytes of heap memory used to store the prepared statement
|
|
650
|
+
*/
|
|
651
|
+
static VALUE
|
|
652
|
+
memused(VALUE self)
|
|
653
|
+
{
|
|
654
|
+
sqlite3StmtRubyPtr ctx;
|
|
655
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
656
|
+
|
|
657
|
+
REQUIRE_LIVE_DB(ctx);
|
|
658
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
659
|
+
|
|
660
|
+
return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_MEMUSED, 0));
|
|
661
|
+
}
|
|
662
|
+
#endif
|
|
663
|
+
|
|
664
|
+
#ifdef HAVE_SQLITE3_COLUMN_DATABASE_NAME
|
|
665
|
+
|
|
666
|
+
/* call-seq: stmt.database_name(column_index)
|
|
667
|
+
*
|
|
668
|
+
* Return the database name for the column at +column_index+
|
|
669
|
+
*/
|
|
670
|
+
static VALUE
|
|
671
|
+
database_name(VALUE self, VALUE index)
|
|
672
|
+
{
|
|
673
|
+
sqlite3StmtRubyPtr ctx;
|
|
674
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
675
|
+
|
|
676
|
+
REQUIRE_LIVE_DB(ctx);
|
|
677
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
678
|
+
|
|
679
|
+
return SQLITE3_UTF8_STR_NEW2(
|
|
680
|
+
sqlite3_column_database_name(ctx->st, NUM2INT(index)));
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
#endif
|
|
684
|
+
|
|
685
|
+
/* call-seq: stmt.sql
|
|
686
|
+
*
|
|
687
|
+
* Returns the SQL statement used to create this prepared statement
|
|
688
|
+
*/
|
|
689
|
+
static VALUE
|
|
690
|
+
get_sql(VALUE self)
|
|
691
|
+
{
|
|
692
|
+
sqlite3StmtRubyPtr ctx;
|
|
693
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
694
|
+
|
|
695
|
+
REQUIRE_LIVE_DB(ctx);
|
|
696
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
697
|
+
|
|
698
|
+
return rb_obj_freeze(SQLITE3_UTF8_STR_NEW2(sqlite3_sql(ctx->st)));
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/* call-seq: stmt.expanded_sql
|
|
702
|
+
*
|
|
703
|
+
* Returns the SQL statement used to create this prepared statement, but
|
|
704
|
+
* with bind parameters substituted in to the statement.
|
|
705
|
+
*/
|
|
706
|
+
static VALUE
|
|
707
|
+
get_expanded_sql(VALUE self)
|
|
708
|
+
{
|
|
709
|
+
sqlite3StmtRubyPtr ctx;
|
|
710
|
+
char *expanded_sql;
|
|
711
|
+
VALUE rb_expanded_sql;
|
|
712
|
+
|
|
713
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
714
|
+
|
|
715
|
+
REQUIRE_LIVE_DB(ctx);
|
|
716
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
717
|
+
|
|
718
|
+
expanded_sql = sqlite3_expanded_sql(ctx->st);
|
|
719
|
+
rb_expanded_sql = rb_obj_freeze(SQLITE3_UTF8_STR_NEW2(expanded_sql));
|
|
720
|
+
sqlite3_free(expanded_sql);
|
|
721
|
+
|
|
722
|
+
return rb_expanded_sql;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
void
|
|
726
|
+
init_sqlite3_statement(void)
|
|
727
|
+
{
|
|
728
|
+
cSqlite3Statement = rb_define_class_under(mSqlite3, "Statement", rb_cObject);
|
|
729
|
+
|
|
730
|
+
rb_define_alloc_func(cSqlite3Statement, allocate);
|
|
731
|
+
rb_define_method(cSqlite3Statement, "close", sqlite3_rb_close, 0);
|
|
732
|
+
rb_define_method(cSqlite3Statement, "closed?", closed_p, 0);
|
|
733
|
+
rb_define_method(cSqlite3Statement, "bind_param", bind_param, 2);
|
|
734
|
+
rb_define_method(cSqlite3Statement, "reset!", reset_bang, 0);
|
|
735
|
+
rb_define_method(cSqlite3Statement, "clear_bindings!", clear_bindings_bang, 0);
|
|
736
|
+
rb_define_method(cSqlite3Statement, "step", step, 0);
|
|
737
|
+
rb_define_method(cSqlite3Statement, "done?", done_p, 0);
|
|
738
|
+
rb_define_method(cSqlite3Statement, "column_count", column_count, 0);
|
|
739
|
+
rb_define_method(cSqlite3Statement, "column_name", column_name, 1);
|
|
740
|
+
rb_define_method(cSqlite3Statement, "column_decltype", column_decltype, 1);
|
|
741
|
+
rb_define_method(cSqlite3Statement, "bind_parameter_count", bind_parameter_count, 0);
|
|
742
|
+
rb_define_method(cSqlite3Statement, "named_params", named_params, 0);
|
|
743
|
+
rb_define_method(cSqlite3Statement, "sql", get_sql, 0);
|
|
744
|
+
rb_define_method(cSqlite3Statement, "expanded_sql", get_expanded_sql, 0);
|
|
745
|
+
#ifdef HAVE_SQLITE3_COLUMN_DATABASE_NAME
|
|
746
|
+
rb_define_method(cSqlite3Statement, "database_name", database_name, 1);
|
|
747
|
+
#endif
|
|
748
|
+
#ifdef SQLITE_STMTSTATUS_MEMUSED
|
|
749
|
+
rb_define_method(cSqlite3Statement, "memused", memused, 0);
|
|
750
|
+
#endif
|
|
751
|
+
|
|
752
|
+
rb_define_private_method(cSqlite3Statement, "prepare", prepare, 2);
|
|
753
|
+
rb_define_private_method(cSqlite3Statement, "stats_as_hash", stats_as_hash, 0);
|
|
754
|
+
rb_define_private_method(cSqlite3Statement, "stat_for", stat_for, 1);
|
|
755
|
+
}
|