sqlite3 2.9.6 → 2.10.2
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/CHANGELOG.md +73 -1
- data/FAQ.md +19 -0
- data/dependencies.yml +8 -8
- data/ext/sqlite3/aggregator.c +103 -109
- data/ext/sqlite3/aggregator.h +4 -3
- data/ext/sqlite3/backup.c +7 -2
- data/ext/sqlite3/database.c +336 -109
- data/ext/sqlite3/database.h +6 -3
- data/ext/sqlite3/exception.c +95 -0
- data/ext/sqlite3/exception.h +21 -0
- data/ext/sqlite3/extconf.rb +8 -2
- data/ext/sqlite3/sqlite3.c +14 -0
- data/ext/sqlite3/sqlite3_ruby.h +3 -0
- data/ext/sqlite3/statement.c +105 -27
- data/ext/sqlite3/timespec.h +9 -0
- data/lib/sqlite3/database.rb +20 -30
- data/lib/sqlite3/resultset.rb +16 -6
- data/lib/sqlite3/statement.rb +48 -13
- data/lib/sqlite3/version.rb +1 -1
- data/ports/archives/sqlite-autoconf-3540100.tar.gz +0 -0
- metadata +3 -3
- data/ports/archives/sqlite-autoconf-3530200.tar.gz +0 -0
data/ext/sqlite3/exception.c
CHANGED
|
@@ -120,3 +120,98 @@ rb_sqlite3_raise_with_sql(sqlite3 *db, int status, const char *sql)
|
|
|
120
120
|
|
|
121
121
|
rb_exc_raise(exception);
|
|
122
122
|
}
|
|
123
|
+
|
|
124
|
+
/* Each call into sqlite that can run callbacks pushes a frame on the current
|
|
125
|
+
* fiber's chain. A callback that fails records its rb_protect state in the top
|
|
126
|
+
* frame, and the call resumes Ruby's pending jump once sqlite returns. Ruby
|
|
127
|
+
* code that runs in between, such as a finalizer during an allocation, pushes
|
|
128
|
+
* its own frame for any nested call, so it never takes the outer call's jump.
|
|
129
|
+
* The chain is fiber-local because Ruby code inside a callback can switch
|
|
130
|
+
* threads or fibers, and a Ruby thread can move between native threads inside
|
|
131
|
+
* rb_protect. */
|
|
132
|
+
typedef struct {
|
|
133
|
+
rb_sqlite3_callback_frame_t *top;
|
|
134
|
+
} callback_frames_t;
|
|
135
|
+
|
|
136
|
+
static const rb_data_type_t callback_frames_type = {
|
|
137
|
+
.wrap_struct_name = "SQLite3::CallbackFrames",
|
|
138
|
+
.function = {
|
|
139
|
+
.dfree = RUBY_TYPED_DEFAULT_FREE,
|
|
140
|
+
},
|
|
141
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
static ID id_callback_frames;
|
|
145
|
+
|
|
146
|
+
/* Ruby code can overwrite or delete the fiber-local, so check its type. */
|
|
147
|
+
static VALUE
|
|
148
|
+
current_callback_frames_holder(void)
|
|
149
|
+
{
|
|
150
|
+
VALUE holder = rb_thread_local_aref(rb_thread_current(), id_callback_frames);
|
|
151
|
+
return rb_typeddata_is_kind_of(holder, &callback_frames_type) ? holder : Qnil;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static callback_frames_t *
|
|
155
|
+
current_callback_frames(void)
|
|
156
|
+
{
|
|
157
|
+
VALUE holder = current_callback_frames_holder();
|
|
158
|
+
return NIL_P(holder) ? NULL : (callback_frames_t *)RTYPEDDATA_DATA(holder);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
void
|
|
162
|
+
rb_sqlite3_push_callback_frame(rb_sqlite3_callback_frame_t *frame)
|
|
163
|
+
{
|
|
164
|
+
VALUE holder = current_callback_frames_holder();
|
|
165
|
+
callback_frames_t *frames;
|
|
166
|
+
|
|
167
|
+
if (NIL_P(holder)) {
|
|
168
|
+
holder = TypedData_Make_Struct(rb_cObject, callback_frames_t, &callback_frames_type, frames);
|
|
169
|
+
rb_thread_local_aset(rb_thread_current(), id_callback_frames, holder);
|
|
170
|
+
}
|
|
171
|
+
frames = (callback_frames_t *)RTYPEDDATA_DATA(holder);
|
|
172
|
+
|
|
173
|
+
frame->state = 0;
|
|
174
|
+
frame->prev = frames->top;
|
|
175
|
+
frame->holder = holder;
|
|
176
|
+
frames->top = frame;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
int
|
|
180
|
+
rb_sqlite3_pop_callback_frame(rb_sqlite3_callback_frame_t *frame)
|
|
181
|
+
{
|
|
182
|
+
((callback_frames_t *)RTYPEDDATA_DATA(frame->holder))->top = frame->prev;
|
|
183
|
+
return frame->state;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
VALUE
|
|
187
|
+
rb_sqlite3_protect_callback(VALUE (*body)(VALUE), VALUE data, int *failed)
|
|
188
|
+
{
|
|
189
|
+
callback_frames_t *frames = current_callback_frames();
|
|
190
|
+
rb_sqlite3_callback_frame_t *frame = frames ? frames->top : NULL;
|
|
191
|
+
VALUE result;
|
|
192
|
+
int state;
|
|
193
|
+
|
|
194
|
+
if (frame && frame->state) {
|
|
195
|
+
*failed = 1;
|
|
196
|
+
return Qnil;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
result = rb_protect(body, data, &state);
|
|
200
|
+
*failed = state != 0;
|
|
201
|
+
|
|
202
|
+
if (frame) {
|
|
203
|
+
frame->state = state;
|
|
204
|
+
} else if (state) {
|
|
205
|
+
/* sqlite ran the callback from a call that cannot resume the jump, such
|
|
206
|
+
* as Statement#reset!, so the error is dropped */
|
|
207
|
+
rb_set_errinfo(Qnil);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
void
|
|
214
|
+
init_sqlite3_exception(void)
|
|
215
|
+
{
|
|
216
|
+
id_callback_frames = rb_intern("__sqlite3_callback_frames__");
|
|
217
|
+
}
|
data/ext/sqlite3/exception.h
CHANGED
|
@@ -9,4 +9,25 @@ void rb_sqlite3_raise(sqlite3 *db, int status);
|
|
|
9
9
|
void rb_sqlite3_raise_msg(sqlite3 *db, int status, const char *msg);
|
|
10
10
|
void rb_sqlite3_raise_with_sql(sqlite3 *db, int status, const char *sql);
|
|
11
11
|
|
|
12
|
+
typedef struct rb_sqlite3_callback_frame {
|
|
13
|
+
int state;
|
|
14
|
+
struct rb_sqlite3_callback_frame *prev;
|
|
15
|
+
/* the frame lives on the C stack, so GC marks and pins this */
|
|
16
|
+
VALUE holder;
|
|
17
|
+
} rb_sqlite3_callback_frame_t;
|
|
18
|
+
|
|
19
|
+
/* A call into sqlite that can run callbacks is wrapped in a frame. sqlite
|
|
20
|
+
* cannot carry a raise or a throw, so a failed callback records its rb_protect
|
|
21
|
+
* state in the frame, and later callbacks skip their Ruby code. Pop the frame
|
|
22
|
+
* when sqlite returns, then resume the jump with rb_jump_tag if the returned
|
|
23
|
+
* state is nonzero. Run nothing but the sqlite call in between, because a
|
|
24
|
+
* raise there would skip the pop. */
|
|
25
|
+
void rb_sqlite3_push_callback_frame(rb_sqlite3_callback_frame_t *frame);
|
|
26
|
+
int rb_sqlite3_pop_callback_frame(rb_sqlite3_callback_frame_t *frame);
|
|
27
|
+
|
|
28
|
+
/* Runs a callback's Ruby code for sqlite. Sets *failed if it did not complete. */
|
|
29
|
+
VALUE rb_sqlite3_protect_callback(VALUE (*body)(VALUE), VALUE data, int *failed);
|
|
30
|
+
|
|
31
|
+
void init_sqlite3_exception(void);
|
|
32
|
+
|
|
12
33
|
#endif
|
data/ext/sqlite3/extconf.rb
CHANGED
|
@@ -65,7 +65,10 @@ module Sqlite3
|
|
|
65
65
|
"-DSQLITE_ENABLE_DBPAGE_VTAB=1",
|
|
66
66
|
"-DSQLITE_ENABLE_DBSTAT_VTAB=1"
|
|
67
67
|
]
|
|
68
|
-
|
|
68
|
+
# sqlite >= 3.54 requires Vista APIs, same as ruby's default target. Placed first so a
|
|
69
|
+
# user-defined _WIN32_WINNT takes precedence.
|
|
70
|
+
windows_cflags = windows? ? ["-D_WIN32_WINNT=0x0600"] : []
|
|
71
|
+
env["CFLAGS"] = [windows_cflags, user_cflags, env["CFLAGS"], more_cflags].flatten.join(" ")
|
|
69
72
|
recipe.configure_options += env.slice(*ENV_ALLOWLIST)
|
|
70
73
|
.map { |key, value| "#{key}=#{value.strip}" }
|
|
71
74
|
end
|
|
@@ -115,9 +118,10 @@ module Sqlite3
|
|
|
115
118
|
|
|
116
119
|
abort_could_not_find(libname) unless find_library(libname, "sqlite3_libversion_number", "sqlite3.h")
|
|
117
120
|
|
|
118
|
-
#
|
|
121
|
+
# Ruby 3.0+, but not on every implementation:
|
|
119
122
|
# https://github.com/oracle/truffleruby/issues/3408
|
|
120
123
|
have_func("rb_enc_interned_str_cstr")
|
|
124
|
+
have_func("rb_enc_interned_str")
|
|
121
125
|
|
|
122
126
|
# Functions defined in 1.9 but not 1.8
|
|
123
127
|
have_func("rb_proc_arity")
|
|
@@ -139,6 +143,8 @@ module Sqlite3
|
|
|
139
143
|
have_func("sqlite3_prepare_v2")
|
|
140
144
|
have_func("sqlite3_db_name", "sqlite3.h") # v3.39.0
|
|
141
145
|
have_func("sqlite3_error_offset", "sqlite3.h") # v3.38.0
|
|
146
|
+
have_func("sqlite3_serialize", "sqlite3.h") # v3.23.0
|
|
147
|
+
have_func("sqlite3_deserialize", "sqlite3.h") # v3.23.0
|
|
142
148
|
|
|
143
149
|
have_type("sqlite3_int64", "sqlite3.h")
|
|
144
150
|
have_type("sqlite3_uint64", "sqlite3.h")
|
data/ext/sqlite3/sqlite3.c
CHANGED
|
@@ -60,6 +60,19 @@ bignum_to_int64(VALUE value, sqlite3_int64 *result)
|
|
|
60
60
|
#endif
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
void
|
|
64
|
+
rb_sqlite3_pin_array_and_contents(VALUE ary)
|
|
65
|
+
{
|
|
66
|
+
long i;
|
|
67
|
+
|
|
68
|
+
if (NIL_P(ary) || !ary) { return; }
|
|
69
|
+
|
|
70
|
+
rb_gc_mark(ary);
|
|
71
|
+
for (i = 0; i < RARRAY_LEN(ary); i++) {
|
|
72
|
+
rb_gc_mark(RARRAY_AREF(ary, i));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
63
76
|
static VALUE
|
|
64
77
|
libversion(VALUE UNUSED(klass))
|
|
65
78
|
{
|
|
@@ -191,6 +204,7 @@ Init_sqlite3_native(void)
|
|
|
191
204
|
sqlite3_initialize();
|
|
192
205
|
#endif
|
|
193
206
|
|
|
207
|
+
init_sqlite3_exception();
|
|
194
208
|
init_sqlite3_constants();
|
|
195
209
|
init_sqlite3_database();
|
|
196
210
|
init_sqlite3_statement();
|
data/ext/sqlite3/sqlite3_ruby.h
CHANGED
data/ext/sqlite3/statement.c
CHANGED
|
@@ -31,7 +31,7 @@ statement_memsize(const void *data)
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
static const rb_data_type_t statement_type = {
|
|
34
|
-
"SQLite3::
|
|
34
|
+
"SQLite3::Statement",
|
|
35
35
|
{
|
|
36
36
|
NULL,
|
|
37
37
|
statement_deallocate,
|
|
@@ -54,8 +54,9 @@ prepare(VALUE self, VALUE db, VALUE sql)
|
|
|
54
54
|
{
|
|
55
55
|
sqlite3RubyPtr db_ctx = sqlite3_database_unwrap(db);
|
|
56
56
|
sqlite3StmtRubyPtr ctx;
|
|
57
|
-
const char *tail = NULL;
|
|
58
|
-
|
|
57
|
+
const char *sql_cstr, *tail = NULL;
|
|
58
|
+
rb_sqlite3_callback_frame_t frame;
|
|
59
|
+
int sql_len, status, callback_state;
|
|
59
60
|
|
|
60
61
|
StringValue(sql);
|
|
61
62
|
|
|
@@ -66,20 +67,26 @@ prepare(VALUE self, VALUE db, VALUE sql)
|
|
|
66
67
|
* variable will keep it from being GCed. */
|
|
67
68
|
ctx->db = db_ctx;
|
|
68
69
|
|
|
70
|
+
timespecclear(&db_ctx->stmt_deadline);
|
|
71
|
+
|
|
72
|
+
sql_cstr = StringValuePtr(sql);
|
|
73
|
+
sql_len = RSTRING_LENINT(sql);
|
|
74
|
+
|
|
75
|
+
rb_sqlite3_push_callback_frame(&frame);
|
|
69
76
|
#ifdef HAVE_SQLITE3_PREPARE_V2
|
|
70
77
|
status = sqlite3_prepare_v2(
|
|
71
78
|
#else
|
|
72
79
|
status = sqlite3_prepare(
|
|
73
80
|
#endif
|
|
74
81
|
db_ctx->db,
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
sql_cstr,
|
|
83
|
+
sql_len,
|
|
77
84
|
&ctx->st,
|
|
78
85
|
&tail
|
|
79
86
|
);
|
|
87
|
+
if ((callback_state = rb_sqlite3_pop_callback_frame(&frame))) { rb_jump_tag(callback_state); }
|
|
80
88
|
|
|
81
89
|
CHECK_PREPARE(db_ctx->db, status, StringValuePtr(sql));
|
|
82
|
-
timespecclear(&db_ctx->stmt_deadline);
|
|
83
90
|
|
|
84
91
|
return rb_utf8_str_new_cstr(tail);
|
|
85
92
|
}
|
|
@@ -124,7 +131,8 @@ step(VALUE self)
|
|
|
124
131
|
{
|
|
125
132
|
sqlite3StmtRubyPtr ctx;
|
|
126
133
|
sqlite3_stmt *stmt;
|
|
127
|
-
|
|
134
|
+
rb_sqlite3_callback_frame_t frame;
|
|
135
|
+
int value, length, callback_state;
|
|
128
136
|
VALUE list;
|
|
129
137
|
rb_encoding *internal_encoding;
|
|
130
138
|
|
|
@@ -139,14 +147,17 @@ step(VALUE self)
|
|
|
139
147
|
|
|
140
148
|
stmt = ctx->st;
|
|
141
149
|
|
|
150
|
+
if (!sqlite3_stmt_busy(stmt)) {
|
|
151
|
+
timespecclear(&ctx->db->stmt_deadline);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
rb_sqlite3_push_callback_frame(&frame);
|
|
142
155
|
value = sqlite3_step(stmt);
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
rb_set_errinfo(Qnil);
|
|
149
|
-
rb_exc_raise(exception);
|
|
156
|
+
if ((callback_state = rb_sqlite3_pop_callback_frame(&frame))) { rb_jump_tag(callback_state); }
|
|
157
|
+
|
|
158
|
+
if (value == SQLITE_DONE) {
|
|
159
|
+
ctx->done_p = 1;
|
|
160
|
+
return Qnil;
|
|
150
161
|
}
|
|
151
162
|
|
|
152
163
|
length = sqlite3_column_count(stmt);
|
|
@@ -195,10 +206,6 @@ step(VALUE self)
|
|
|
195
206
|
}
|
|
196
207
|
}
|
|
197
208
|
break;
|
|
198
|
-
case SQLITE_DONE:
|
|
199
|
-
ctx->done_p = 1;
|
|
200
|
-
return Qnil;
|
|
201
|
-
break;
|
|
202
209
|
default:
|
|
203
210
|
sqlite3_reset(stmt);
|
|
204
211
|
ctx->done_p = 0;
|
|
@@ -216,6 +223,25 @@ step(VALUE self)
|
|
|
216
223
|
* Fixnum, it is treated as an index for a positional placeholder.
|
|
217
224
|
* Otherwise it is used as the name of the placeholder to bind to.
|
|
218
225
|
*
|
|
226
|
+
* Type mapping from Ruby to SQLite3:
|
|
227
|
+
* - +nil+ → NULL
|
|
228
|
+
* - Integer → INTEGER (or REAL when outside the signed int64 range)
|
|
229
|
+
* - Float → REAL
|
|
230
|
+
* - SQLite3::Blob → BLOB
|
|
231
|
+
* - String with Encoding::ASCII_8BIT (a.k.a. BINARY) → BLOB
|
|
232
|
+
* - String with Encoding::UTF_16LE or Encoding::UTF_16BE → TEXT (bound as UTF-16)
|
|
233
|
+
* - String (all other encodings) → TEXT (re-encoded to UTF-8 if necessary)
|
|
234
|
+
* - Any other type → raises RuntimeError
|
|
235
|
+
*
|
|
236
|
+
* Note: if you have a string with only ASCII characters but ASCII-8BIT
|
|
237
|
+
* encoding and want it bound as TEXT rather than BLOB, re-encode it first:
|
|
238
|
+
*
|
|
239
|
+
* stmt.bind_param(1, my_binary_str.encode(Encoding::UTF_8))
|
|
240
|
+
*
|
|
241
|
+
* The value that this method binds stays bound when you execute or reset the
|
|
242
|
+
* statement. To change the value, bind a new value to the same parameter. To
|
|
243
|
+
* clear all values, call #clear_bindings!.
|
|
244
|
+
*
|
|
219
245
|
* See also #bind_params.
|
|
220
246
|
*/
|
|
221
247
|
static VALUE
|
|
@@ -254,7 +280,7 @@ bind_param(VALUE self, VALUE key, VALUE value)
|
|
|
254
280
|
ctx->st,
|
|
255
281
|
index,
|
|
256
282
|
(const char *)StringValuePtr(value),
|
|
257
|
-
(
|
|
283
|
+
RSTRING_LENINT(value),
|
|
258
284
|
SQLITE_TRANSIENT
|
|
259
285
|
);
|
|
260
286
|
} else {
|
|
@@ -265,7 +291,7 @@ bind_param(VALUE self, VALUE key, VALUE value)
|
|
|
265
291
|
ctx->st,
|
|
266
292
|
index,
|
|
267
293
|
(const char *)StringValuePtr(value),
|
|
268
|
-
(
|
|
294
|
+
RSTRING_LENINT(value),
|
|
269
295
|
SQLITE_TRANSIENT
|
|
270
296
|
);
|
|
271
297
|
} else {
|
|
@@ -276,7 +302,7 @@ bind_param(VALUE self, VALUE key, VALUE value)
|
|
|
276
302
|
ctx->st,
|
|
277
303
|
index,
|
|
278
304
|
(const char *)StringValuePtr(value),
|
|
279
|
-
(
|
|
305
|
+
RSTRING_LENINT(value),
|
|
280
306
|
SQLITE_TRANSIENT
|
|
281
307
|
);
|
|
282
308
|
}
|
|
@@ -311,8 +337,11 @@ bind_param(VALUE self, VALUE key, VALUE value)
|
|
|
311
337
|
|
|
312
338
|
/* call-seq: stmt.reset!
|
|
313
339
|
*
|
|
314
|
-
* Resets the statement.
|
|
315
|
-
*
|
|
340
|
+
* Resets the statement. The library usually calls this method for you, but
|
|
341
|
+
* you can also call it yourself.
|
|
342
|
+
*
|
|
343
|
+
* This method does not clear the values that you bound to the parameters. To
|
|
344
|
+
* clear them, call #clear_bindings!.
|
|
316
345
|
*/
|
|
317
346
|
static VALUE
|
|
318
347
|
reset_bang(VALUE self)
|
|
@@ -333,8 +362,11 @@ reset_bang(VALUE self)
|
|
|
333
362
|
|
|
334
363
|
/* call-seq: stmt.clear_bindings!
|
|
335
364
|
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
365
|
+
* Sets the value of each parameter in the statement to NULL.
|
|
366
|
+
*
|
|
367
|
+
* The values that you bind with #bind_param, #bind_params, or #execute stay
|
|
368
|
+
* bound when you execute or reset the statement. Call this method to clear
|
|
369
|
+
* them.
|
|
338
370
|
*/
|
|
339
371
|
static VALUE
|
|
340
372
|
clear_bindings_bang(VALUE self)
|
|
@@ -398,6 +430,33 @@ interned_utf8_cstr(const char *str)
|
|
|
398
430
|
}
|
|
399
431
|
#endif
|
|
400
432
|
|
|
433
|
+
/* Like interned_utf8_cstr, but ASCII-downcases as it copies. */
|
|
434
|
+
static VALUE
|
|
435
|
+
interned_downcased_utf8_cstr(const char *str)
|
|
436
|
+
{
|
|
437
|
+
#if HAVE_RB_ENC_INTERNED_STR
|
|
438
|
+
VALUE tmp, ret;
|
|
439
|
+
char *buf;
|
|
440
|
+
size_t i, len;
|
|
441
|
+
|
|
442
|
+
len = strlen(str);
|
|
443
|
+
buf = ALLOCV_N(char, tmp, len);
|
|
444
|
+
|
|
445
|
+
for (i = 0; i < len; i++) {
|
|
446
|
+
unsigned char c = (unsigned char)str[i];
|
|
447
|
+
buf[i] = (char)((c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
ret = rb_enc_interned_str(buf, (long)len, rb_utf8_encoding());
|
|
451
|
+
ALLOCV_END(tmp);
|
|
452
|
+
return ret;
|
|
453
|
+
#else
|
|
454
|
+
VALUE rb_str = rb_funcall(rb_utf8_str_new_cstr(str), rb_intern("downcase"), 1,
|
|
455
|
+
ID2SYM(rb_intern("ascii")));
|
|
456
|
+
return rb_funcall(rb_str, rb_intern("-@"), 0);
|
|
457
|
+
#endif
|
|
458
|
+
}
|
|
459
|
+
|
|
401
460
|
/* call-seq: stmt.column_name(index)
|
|
402
461
|
*
|
|
403
462
|
* Get the column name at +index+. 0 based.
|
|
@@ -425,7 +484,8 @@ column_name(VALUE self, VALUE index)
|
|
|
425
484
|
|
|
426
485
|
/* call-seq: stmt.column_decltype(index)
|
|
427
486
|
*
|
|
428
|
-
* Get the
|
|
487
|
+
* Get the declared type of the column at +index+, as a UTF-8 string. 0 based.
|
|
488
|
+
* Returns +nil+ if the column has no declared type, such as an expression.
|
|
429
489
|
*/
|
|
430
490
|
static VALUE
|
|
431
491
|
column_decltype(VALUE self, VALUE index)
|
|
@@ -440,7 +500,24 @@ column_decltype(VALUE self, VALUE index)
|
|
|
440
500
|
|
|
441
501
|
name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
|
|
442
502
|
|
|
443
|
-
if (name) { return
|
|
503
|
+
if (name) { return rb_utf8_str_new_cstr(name); }
|
|
504
|
+
return Qnil;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
static VALUE
|
|
508
|
+
column_decltype_downcased(VALUE self, VALUE index)
|
|
509
|
+
{
|
|
510
|
+
sqlite3StmtRubyPtr ctx;
|
|
511
|
+
const char *name;
|
|
512
|
+
|
|
513
|
+
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
|
|
514
|
+
|
|
515
|
+
REQUIRE_LIVE_DB(ctx);
|
|
516
|
+
REQUIRE_OPEN_STMT(ctx);
|
|
517
|
+
|
|
518
|
+
name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
|
|
519
|
+
|
|
520
|
+
if (name) { return interned_downcased_utf8_cstr(name); }
|
|
444
521
|
return Qnil;
|
|
445
522
|
}
|
|
446
523
|
|
|
@@ -733,6 +810,7 @@ init_sqlite3_statement(void)
|
|
|
733
810
|
rb_define_method(cSqlite3Statement, "memused", memused, 0);
|
|
734
811
|
#endif
|
|
735
812
|
|
|
813
|
+
rb_define_private_method(cSqlite3Statement, "column_decltype_downcased", column_decltype_downcased, 1);
|
|
736
814
|
rb_define_private_method(cSqlite3Statement, "prepare", prepare, 2);
|
|
737
815
|
rb_define_private_method(cSqlite3Statement, "stats_as_hash", stats_as_hash, 0);
|
|
738
816
|
rb_define_private_method(cSqlite3Statement, "stat_for", stat_for, 1);
|
data/ext/sqlite3/timespec.h
CHANGED
|
@@ -15,6 +15,15 @@
|
|
|
15
15
|
(vsp)->tv_nsec += 1000000000L; \
|
|
16
16
|
} \
|
|
17
17
|
} while (0)
|
|
18
|
+
#define timespecadd(tsp, usp, vsp) \
|
|
19
|
+
do { \
|
|
20
|
+
(vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
|
|
21
|
+
(vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
|
|
22
|
+
if ((vsp)->tv_nsec >= 1000000000L) { \
|
|
23
|
+
(vsp)->tv_sec++; \
|
|
24
|
+
(vsp)->tv_nsec -= 1000000000L; \
|
|
25
|
+
} \
|
|
26
|
+
} while (0)
|
|
18
27
|
#define timespecafter(tsp, usp) \
|
|
19
28
|
(((tsp)->tv_sec > (usp)->tv_sec) || \
|
|
20
29
|
((tsp)->tv_sec == (usp)->tv_sec && (tsp)->tv_nsec > (usp)->tv_nsec))
|
data/lib/sqlite3/database.rb
CHANGED
|
@@ -579,6 +579,25 @@ module SQLite3
|
|
|
579
579
|
self
|
|
580
580
|
end
|
|
581
581
|
|
|
582
|
+
# The "handler" that #define_aggregator hands to define_aggregator2, which
|
|
583
|
+
# only needs something that responds to +new+ (a fresh aggregator for each
|
|
584
|
+
# aggregate invocation) and +arity+.
|
|
585
|
+
class AggregatorFactory # :nodoc:
|
|
586
|
+
def initialize(template)
|
|
587
|
+
@template = template
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
def new
|
|
591
|
+
@template.clone
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# this is what sqlite3_obj_method_arity did before
|
|
595
|
+
def arity
|
|
596
|
+
@template.method(:step).arity
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
private_constant :AggregatorFactory
|
|
600
|
+
|
|
582
601
|
# Define an aggregate function named +name+ using a object template
|
|
583
602
|
# object +aggregator+. +aggregator+ must respond to +step+ and +finalize+.
|
|
584
603
|
# +step+ will be called with row information and +finalize+ must return the
|
|
@@ -592,36 +611,7 @@ module SQLite3
|
|
|
592
611
|
def define_aggregator(name, aggregator)
|
|
593
612
|
# Previously, this has been implemented in C. Now this is just yet
|
|
594
613
|
# another compatibility shim
|
|
595
|
-
|
|
596
|
-
@template = aggregator
|
|
597
|
-
@name = name
|
|
598
|
-
|
|
599
|
-
def self.template
|
|
600
|
-
@template
|
|
601
|
-
end
|
|
602
|
-
|
|
603
|
-
def self.name
|
|
604
|
-
@name
|
|
605
|
-
end
|
|
606
|
-
|
|
607
|
-
def self.arity
|
|
608
|
-
# this is what sqlite3_obj_method_arity did before
|
|
609
|
-
@template.method(:step).arity
|
|
610
|
-
end
|
|
611
|
-
|
|
612
|
-
def initialize
|
|
613
|
-
@klass = self.class.template.clone
|
|
614
|
-
end
|
|
615
|
-
|
|
616
|
-
def step(*args)
|
|
617
|
-
@klass.step(*args)
|
|
618
|
-
end
|
|
619
|
-
|
|
620
|
-
def finalize
|
|
621
|
-
@klass.finalize
|
|
622
|
-
end
|
|
623
|
-
end
|
|
624
|
-
define_aggregator2(proxy, name)
|
|
614
|
+
define_aggregator2(AggregatorFactory.new(aggregator), name)
|
|
625
615
|
self
|
|
626
616
|
end
|
|
627
617
|
|
data/lib/sqlite3/resultset.rb
CHANGED
|
@@ -16,8 +16,12 @@ module SQLite3
|
|
|
16
16
|
@stmt = stmt
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
#
|
|
19
|
+
# Resets the cursor to the first row, so that you can iterate over the
|
|
20
|
+
# result set again after it reaches the end.
|
|
21
|
+
#
|
|
22
|
+
# This method binds each argument to the statement with
|
|
23
|
+
# Statement#bind_params. The values that you bound before stay bound
|
|
24
|
+
# unless you bind new values to the same parameters.
|
|
21
25
|
def reset(*bind_params)
|
|
22
26
|
@stmt.reset!
|
|
23
27
|
@stmt.bind_params(*bind_params)
|
|
@@ -43,20 +47,26 @@ module SQLite3
|
|
|
43
47
|
@stmt.step
|
|
44
48
|
end
|
|
45
49
|
|
|
46
|
-
#
|
|
47
|
-
#
|
|
50
|
+
# With a block given, iterates over the rows of the result set, passing each
|
|
51
|
+
# to the block. Returns self.
|
|
52
|
+
# With no block given, returns a new Enumerator.
|
|
48
53
|
def each
|
|
54
|
+
return enum_for(__method__) unless block_given?
|
|
49
55
|
while (node = self.next)
|
|
50
56
|
yield node
|
|
51
57
|
end
|
|
58
|
+
self
|
|
52
59
|
end
|
|
53
60
|
|
|
54
|
-
#
|
|
55
|
-
#
|
|
61
|
+
# With a block given, iterates over the rows of the result set, passing each
|
|
62
|
+
# to the block as a hash. Returns self.
|
|
63
|
+
# With no block given, returns a new Enumerator.
|
|
56
64
|
def each_hash
|
|
65
|
+
return enum_for(__method__) unless block_given?
|
|
57
66
|
while (node = next_hash)
|
|
58
67
|
yield node
|
|
59
68
|
end
|
|
69
|
+
self
|
|
60
70
|
end
|
|
61
71
|
|
|
62
72
|
# Closes the statement that spawned this result set.
|
data/lib/sqlite3/statement.rb
CHANGED
|
@@ -42,11 +42,33 @@ module SQLite3
|
|
|
42
42
|
# See Database#execute for a description of the valid placeholder
|
|
43
43
|
# syntaxes.
|
|
44
44
|
#
|
|
45
|
+
# Type mapping from Ruby to SQLite3:
|
|
46
|
+
# - +nil+ → NULL
|
|
47
|
+
# - Integer → INTEGER (or REAL when outside the signed int64 range)
|
|
48
|
+
# - Float → REAL
|
|
49
|
+
# - SQLite3::Blob → BLOB
|
|
50
|
+
# - String with Encoding::ASCII_8BIT
|
|
51
|
+
# (a.k.a. BINARY) → BLOB
|
|
52
|
+
# - String with Encoding::UTF_16LE
|
|
53
|
+
# or Encoding::UTF_16BE → TEXT (bound as UTF-16)
|
|
54
|
+
# - String (all other encodings) → TEXT (re-encoded to UTF-8 if needed)
|
|
55
|
+
# - Any other type → raises RuntimeError
|
|
56
|
+
#
|
|
57
|
+
# Note: a String with Encoding::ASCII_8BIT (BINARY) is always bound as a
|
|
58
|
+
# BLOB, even when its bytes are all valid ASCII. If you want such a string
|
|
59
|
+
# compared or stored as TEXT, re-encode it before binding:
|
|
60
|
+
#
|
|
61
|
+
# stmt.bind_params(my_binary_str.encode(Encoding::UTF_8))
|
|
62
|
+
#
|
|
45
63
|
# Example:
|
|
46
64
|
#
|
|
47
65
|
# stmt = db.prepare( "select * from table where a=? and b=?" )
|
|
48
66
|
# stmt.bind_params( 15, "hello" )
|
|
49
67
|
#
|
|
68
|
+
# The values that this method binds stay bound when you execute or reset
|
|
69
|
+
# the statement. To change a value, bind a new value to the same
|
|
70
|
+
# parameter. To clear all values, call #clear_bindings!.
|
|
71
|
+
#
|
|
50
72
|
# See also #execute, #bind_param, Statement#bind_param, and
|
|
51
73
|
# Statement#bind_params.
|
|
52
74
|
def bind_params(*bind_vars)
|
|
@@ -65,7 +87,17 @@ module SQLite3
|
|
|
65
87
|
# statement's virtual machine. If a block was given, the new ResultSet will
|
|
66
88
|
# be yielded to it; otherwise, the ResultSet will be returned.
|
|
67
89
|
#
|
|
68
|
-
#
|
|
90
|
+
# This method binds each argument to the statement with #bind_params. The
|
|
91
|
+
# values stay bound after this method returns. If a later call to #execute
|
|
92
|
+
# does not give a value for a parameter, the statement uses the value from
|
|
93
|
+
# the earlier call. To set all parameters to NULL before you execute the
|
|
94
|
+
# statement again, call #clear_bindings!:
|
|
95
|
+
#
|
|
96
|
+
# stmt = db.prepare( "select :a, :b" )
|
|
97
|
+
# stmt.execute( a: 1, b: 2 ).to_a # => [[1, 2]]
|
|
98
|
+
# stmt.execute( a: 3 ).to_a # => [[3, 2]]
|
|
99
|
+
# stmt.clear_bindings!
|
|
100
|
+
# stmt.execute( a: 3 ).to_a # => [[3, nil]]
|
|
69
101
|
#
|
|
70
102
|
# Example:
|
|
71
103
|
#
|
|
@@ -74,7 +106,7 @@ module SQLite3
|
|
|
74
106
|
# ...
|
|
75
107
|
# end
|
|
76
108
|
#
|
|
77
|
-
# See also #bind_params, #execute!.
|
|
109
|
+
# See also #bind_params, #execute!, #clear_bindings!.
|
|
78
110
|
def execute(*bind_vars)
|
|
79
111
|
reset! if active? || done?
|
|
80
112
|
|
|
@@ -91,7 +123,9 @@ module SQLite3
|
|
|
91
123
|
# rows returned by executing the statement. Otherwise, each row will be
|
|
92
124
|
# yielded to the block.
|
|
93
125
|
#
|
|
94
|
-
#
|
|
126
|
+
# This method binds each argument to the statement with #bind_params. The
|
|
127
|
+
# values stay bound after this method returns. For more information, see
|
|
128
|
+
# #execute.
|
|
95
129
|
#
|
|
96
130
|
# Example:
|
|
97
131
|
#
|
|
@@ -112,9 +146,9 @@ module SQLite3
|
|
|
112
146
|
!done?
|
|
113
147
|
end
|
|
114
148
|
|
|
115
|
-
# Return
|
|
116
|
-
# may execute the statement in order to
|
|
117
|
-
# a (potentially) expensive operation.
|
|
149
|
+
# Return a frozen array of the column names for this statement, as frozen
|
|
150
|
+
# UTF-8 strings. Note that this may execute the statement in order to
|
|
151
|
+
# obtain the metadata; this makes it a (potentially) expensive operation.
|
|
118
152
|
def columns
|
|
119
153
|
get_metadata unless @columns
|
|
120
154
|
@columns
|
|
@@ -128,9 +162,11 @@ module SQLite3
|
|
|
128
162
|
end
|
|
129
163
|
end
|
|
130
164
|
|
|
131
|
-
# Return
|
|
132
|
-
#
|
|
133
|
-
#
|
|
165
|
+
# Return a frozen array of the declared types of the columns in this
|
|
166
|
+
# statement, as frozen UTF-8 strings with ASCII letters downcased. A column
|
|
167
|
+
# with no declared type, such as an expression, has a +nil+ type. Note that
|
|
168
|
+
# this may execute the statement in order to obtain the metadata; this makes
|
|
169
|
+
# it a (potentially) expensive operation.
|
|
134
170
|
def types
|
|
135
171
|
must_be_open!
|
|
136
172
|
get_metadata unless @types
|
|
@@ -180,11 +216,10 @@ module SQLite3
|
|
|
180
216
|
def get_metadata
|
|
181
217
|
@columns = Array.new(column_count) do |column|
|
|
182
218
|
column_name column
|
|
183
|
-
end
|
|
219
|
+
end.freeze
|
|
184
220
|
@types = Array.new(column_count) do |column|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
end
|
|
221
|
+
column_decltype_downcased(column)
|
|
222
|
+
end.freeze
|
|
188
223
|
end
|
|
189
224
|
end
|
|
190
225
|
end
|