sqlite3 2.9.5-arm-linux-gnu → 2.10.0-arm-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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f7476f81dcc18cc1b6465d7bcf6030f102e3016c1155ec21af67dc2b0572b18
4
- data.tar.gz: 4e881e6f6eed6429c4562b5b0f180b710d0a68ce9b31c19ca28a8c805d0289e7
3
+ metadata.gz: f42bc8a3381da6725ec61704241104ad0cd48343eb6aba06d09053fadad214f3
4
+ data.tar.gz: 5cb519d5abaeb8af194a857dbeacdd2768ea774c709791466cf679a99a6e55c6
5
5
  SHA512:
6
- metadata.gz: dce3dc819f7a222df4a1db1948f60806ba55189b83516619b0f518f8d33f33ecab18d01fbb1db59ebf52e39b55cba2bc7099110fbe86656ef2936634290b85b6
7
- data.tar.gz: f4d37406430ee7ce85bc879ac05a810605dba3c3a71f344d05c337f9d45c794559e49e4f896d781ead72e483ec6cb50f070ec4e34ca75319c40d844d2363ca74
6
+ metadata.gz: 213d6a5b039be4919af7d22f3f9e461229b07a46ab3adbeefe0e89b673b26bfc2ea58a1461f93544c46a78b5c8f401234b9a7216818fb4f54920c3795c155071
7
+ data.tar.gz: 6d5b65e0947bac8565f609269253ced132437cfdcab5f29d0c69780cfbe263e48ba30a1c1c098d1b7f3f72bb2d5bb978b8ca19e9dbfff35777e49364bf8626f2
data/CHANGELOG.md CHANGED
@@ -1,10 +1,62 @@
1
1
  # sqlite3-ruby Changelog
2
2
 
3
+ ## 2.10.0 / 2026-10-13
4
+
5
+ ### Dependencies
6
+
7
+ - Vendored sqlite is updated to [v3.54.0](https://www.sqlite.org/releaselog/3_54_0.html) (from v3.53.2). #750 @flavorjones
8
+
9
+ Notable changes in this version include:
10
+ - `ALTER TABLE` now raises an error when adding, renaming, or dropping a column named `ROWID`, `_ROWID_`, or `OID`.
11
+ - The authorizer callback is now invoked for SQL functions in the `DEFAULT` clauses of `CREATE TABLE` statements.
12
+ - SQLite no longer supports Windows XP or earlier. On Windows, the precompiled gem and any installation that compiles the vendored sqlite now require Windows Vista or later.
13
+
14
+ ### Added
15
+
16
+ - `ResultSet#each` and `#each_hash` return an `Enumerator` when called without a block. Previously they raised `LocalJumpError`. #656 @kch
17
+ - Introduce `Database#serialize`, which returns a database as a binary string, and `Database#deserialize`, which replaces a database with the contents of such a string. These wrap [`sqlite3_serialize()`](https://www.sqlite.org/c3ref/serialize.html) and [`sqlite3_deserialize()`](https://www.sqlite.org/c3ref/deserialize.html). Both methods are defined only when SQLite was compiled without `SQLITE_OMIT_DESERIALIZE`, and `Database#deserialize` also requires that the loaded SQLite is v3.54.0 or later. An image of a database in WAL mode cannot be queried after deserializing; see the `Database#deserialize` documentation. #685 @Maaarcocr
18
+
19
+ ### Fixed
20
+
21
+ - Fix `Database#statement_timeout=` interrupting statements almost immediately instead of after the timeout elapsed, including re-executed prepared statements and calls to `Database#execute_batch2`. #737 @palkan @flavorjones
22
+
23
+ ### Improved
24
+
25
+ - On CRuby, `Statement#types` allocates fewer strings. Each declared type is downcased and interned in C, instead of allocating two strings per column. #743 @jhawthorn
26
+
27
+ ### Changed
28
+
29
+ - `ResultSet#each` and `#each_hash` return the result set when called with a block. As a consequence, `Database#execute` and `#execute2` called with a block return the closed `ResultSet` instead of `nil`. #656 @kch
30
+ - `Statement#columns` and `Statement#types` return frozen arrays, and the strings in `Statement#types` are frozen. Code that mutates them now raises `FrozenError`. #743 @jhawthorn
31
+ - `Statement#column_decltype` and `Statement#types` return UTF-8 strings. Previously they returned ASCII-8BIT strings. #743 @jhawthorn
32
+
33
+
34
+ ## 2.9.6 / 2026-08-11
35
+
36
+ ### Security / Stability
37
+
38
+ - Fix a garbage collection bug where the argument array passed to a custom aggregate function's `step` was not visible to the GC, so arguments could be collected mid-conversion when the aggregate takes two or more arguments, corrupting the values passed to `step` or crashing the process. See [GHSA-mwm8-39rw-8826](https://github.com/sparklemotion/sqlite3-ruby/security/advisories/GHSA-mwm8-39rw-8826) for more information. #733 @jeremy
39
+
40
+ ### Fixed
41
+
42
+ - Fix a leak where custom aggregate handler instances were never released, so a connection accumulated one instance per `GROUP BY` group per query for its lifetime. #722 @djmb
43
+ - Fix GC compaction issues with custom functions, aggregates, collations, `#trace` and `#authorizer=`. These callbacks were registered with sqlite by passing a raw Ruby object pointer as user data; keeping the object reachable prevented collection but not relocation, after which sqlite held a stale address and the next call could raise `NoMethodError`, return a wrong result, or segfault. Affects applications that call `GC.compact` or run with `GC.auto_compact = true`. The equivalent issue in `#busy_handler` was fixed in #466. #723 @djmb
44
+ - Fix the private methods `Database#open_v2` and `#open16` silently replacing a live connection and leaking the previous connection handle when invoked via `send` on an open database. They now raise `SQLite3::Exception`. #729 @flavorjones
45
+ - Fix TEXT values containing an embedded NUL byte being truncated at the first NUL when passed as arguments to functions created with `Database#define_function`. #730 @flavorjones
46
+ - Fix an exception raised inside a `Database#define_function` block leaving the connection's sqlite mutex held, which deadlocked any other thread that later used the connection. The exception now propagates to the caller and the connection remains usable. #731 @flavorjones
47
+ - `Database.new` now raises `ArgumentError` when the filename or VFS name contains an embedded NUL byte (or an embedded 0x0000 code unit in a UTF-16 filename), instead of silently opening a path truncated at the NUL. #732 @flavorjones
48
+
49
+
50
+ ### Improved
51
+
52
+ - When `Database.new` fails to open the database file, the underlying sqlite3 connection handle is now closed immediately instead of waiting for the garbage collector to clean it up. #719 @katafrakt
53
+
54
+
3
55
  ## 2.9.5 / 2026-06-07
4
56
 
5
57
  ### Dependencies
6
58
 
7
- - Vendored sqlite is updated to [v3.53.2](https://www.sqlite.org/releaselog/3_53_3.html) (from v3.53.1). #709 @flavorjones
59
+ - Vendored sqlite is updated to [v3.53.2](https://www.sqlite.org/releaselog/3_53_2.html) (from v3.53.1). #709 @flavorjones
8
60
 
9
61
  ### Security / Stability
10
62
 
data/CONTRIBUTING.md CHANGED
@@ -27,6 +27,71 @@ explicitly closed, it will be closed when it is GCed.
27
27
  will be closed/finalized when it is GCed.
28
28
 
29
29
 
30
+ ## Debugging memory issues
31
+
32
+ Please install `valgrind` and `gdb` before you use the tools in this section.
33
+
34
+ Run the test suite under valgrind and [`ruby_memcheck`](https://github.com/Shopify/ruby_memcheck) to
35
+ look for memory leaks and other memory errors:
36
+
37
+ ``` sh
38
+ bundle exec rake compile test:valgrind
39
+ ```
40
+
41
+ If you can't install valgrind on your system, use the `sqlite3-dev` docker image, which contains
42
+ valgrind:
43
+
44
+ ``` sh
45
+ # build the image
46
+ bundle exec rake docker:dev:build
47
+
48
+ # run the test suite in a container
49
+ bundle exec rake docker:dev:test
50
+
51
+ # run the test suite under valgrind in a container
52
+ bundle exec rake docker:dev:test:valgrind
53
+ ```
54
+
55
+ Each `docker:dev:test` task builds the image first, then mounts your working copy at `/sqlite3` in
56
+ the container. Note that the container compiles into the mounted working copy, so please re-run
57
+ `rake compile` on your machine afterwards.
58
+
59
+ Run the test suite in the debugger:
60
+
61
+ ``` sh
62
+ bundle exec rake compile test:gdb
63
+ ```
64
+
65
+ You can also run the test suite with a variety of GC behaviors, which is useful to localize some
66
+ classes of memory bugs. Set the `SQLITE3_TEST_GC_LEVEL` environment variable (see `test/helper.rb`
67
+ for more info). A more stressful level finds more bugs, but makes the suite slower:
68
+
69
+ ``` sh
70
+ # ordinary GC behavior (the default)
71
+ SQLITE3_TEST_GC_LEVEL=normal bundle exec rake compile test
72
+
73
+ # minor GC after each test
74
+ SQLITE3_TEST_GC_LEVEL=minor bundle exec rake compile test
75
+
76
+ # major GC after each test
77
+ SQLITE3_TEST_GC_LEVEL=major bundle exec rake compile test
78
+
79
+ # major GC after each test, and GC compaction after every 20 tests
80
+ SQLITE3_TEST_GC_LEVEL=compact bundle exec rake compile test
81
+
82
+ # verify references after compaction, after every 20 tests
83
+ # (see https://alanwu.space/post/check-compaction/)
84
+ SQLITE3_TEST_GC_LEVEL=verify bundle exec rake compile test
85
+
86
+ # run each test with GC "stress mode" on
87
+ SQLITE3_TEST_GC_LEVEL=stress bundle exec rake compile test
88
+ ```
89
+
90
+ The `compact` and `verify` levels fall back to `normal` on a platform that does not support GC
91
+ compaction. The `stress` level makes the suite about 150 times slower, and it makes
92
+ timing-sensitive tests unreliable.
93
+
94
+
30
95
  ## Building gems
31
96
 
32
97
  As a prerequisite please make sure you have `docker` correctly installed, so that you're able to cross-compile the native gems.
data/dependencies.yml CHANGED
@@ -1,13 +1,13 @@
1
1
  sqlite3:
2
2
  # checksum verified by first checking the published sha3(256) checksum against https://sqlite.org/download.html:
3
- # 025328da165109f48abccc6e7478508060804412bed2bd81d47e98ba1b72983b
3
+ # f5e3e724e9c9b3b5d512c76b02082091a59eeea9becd169a9fbf4d376403acce
4
4
  #
5
- # $ sha3sum -a 256 ports/archives/sqlite-autoconf-3530200.tar.gz
6
- # 025328da165109f48abccc6e7478508060804412bed2bd81d47e98ba1b72983b ports/archives/sqlite-autoconf-3530200.tar.gz
5
+ # $ sha3sum -a 256 ports/archives/sqlite-autoconf-3540000.tar.gz
6
+ # f5e3e724e9c9b3b5d512c76b02082091a59eeea9becd169a9fbf4d376403acce ports/archives/sqlite-autoconf-3540000.tar.gz
7
7
  #
8
- # $ sha256sum ports/archives/sqlite-autoconf-3530200.tar.gz
9
- # 588ad51949419a56ebe81fe56193d510c559eb94c9a57748387860b5d3069316 ports/archives/sqlite-autoconf-3530200.tar.gz
10
- version: "3.53.2"
8
+ # $ sha256sum ports/archives/sqlite-autoconf-3540000.tar.gz
9
+ # 134ec0802dda5795816e25d25872d20b312cb3973438c49b30bc40b7705ea9ed ports/archives/sqlite-autoconf-3540000.tar.gz
10
+ version: "3.54.0"
11
11
  files:
12
- - url: "https://sqlite.org/2026/sqlite-autoconf-3530200.tar.gz"
13
- sha256: "588ad51949419a56ebe81fe56193d510c559eb94c9a57748387860b5d3069316"
12
+ - url: "https://sqlite.org/2026/sqlite-autoconf-3540000.tar.gz"
13
+ sha256: "134ec0802dda5795816e25d25872d20b312cb3973438c49b30bc40b7705ea9ed"
@@ -97,7 +97,7 @@ rb_sqlite3_aggregate_instance_destroy(sqlite3_context *ctx)
97
97
  VALUE *inst_ptr = sqlite3_aggregate_context(ctx, 0);
98
98
  VALUE inst;
99
99
 
100
- if (!inst_ptr || (inst = *inst_ptr)) {
100
+ if (!inst_ptr || !(inst = *inst_ptr)) {
101
101
  return;
102
102
  }
103
103
 
@@ -119,6 +119,7 @@ rb_sqlite3_aggregator_step(sqlite3_context *ctx, int argc, sqlite3_value **argv)
119
119
  VALUE inst = rb_sqlite3_aggregate_instance(ctx);
120
120
  VALUE handler_instance = rb_iv_get(inst, "-handler_instance");
121
121
  VALUE *params = NULL;
122
+ VALUE params_handle = 0;
122
123
  VALUE one_param;
123
124
  int exc_status = NUM2INT(rb_iv_get(inst, "-exc_status"));
124
125
  int i;
@@ -132,7 +133,9 @@ rb_sqlite3_aggregator_step(sqlite3_context *ctx, int argc, sqlite3_value **argv)
132
133
  params = &one_param;
133
134
  }
134
135
  if (argc > 1) {
135
- params = xcalloc((size_t)argc, sizeof(VALUE));
136
+ /* ALLOCV memory is conservatively marked, so stored VALUEs survive a
137
+ * GC raised by a later sqlite3val2rb call. */
138
+ params = ALLOCV_N(VALUE, params_handle, argc);
136
139
  for (i = 0; i < argc; i++) {
137
140
  params[i] = sqlite3val2rb(argv[i]);
138
141
  }
@@ -140,7 +143,7 @@ rb_sqlite3_aggregator_step(sqlite3_context *ctx, int argc, sqlite3_value **argv)
140
143
  rb_sqlite3_protected_funcall(
141
144
  handler_instance, rb_intern("step"), argc, params, &exc_status);
142
145
  if (argc > 1) {
143
- xfree(params);
146
+ ALLOCV_END(params_handle);
144
147
  }
145
148
 
146
149
  rb_iv_set(inst, "-exc_status", INT2NUM(exc_status));
@@ -254,10 +257,17 @@ rb_sqlite3_define_aggregator2(VALUE self, VALUE aggregator, VALUE ruby_name)
254
257
  CHECK(ctx->db, status);
255
258
 
256
259
  rb_ary_push(aggregators, aw);
260
+ RB_OBJ_WRITE(self, &ctx->aggregators, aggregators);
257
261
 
258
262
  return self;
259
263
  }
260
264
 
265
+ void
266
+ rb_sqlite3_aggregator_pin_instances(VALUE aw)
267
+ {
268
+ rb_sqlite3_pin_array_and_contents(rb_iv_get(aw, "-instances"));
269
+ }
270
+
261
271
  void
262
272
  rb_sqlite3_aggregator_init(void)
263
273
  {
@@ -7,4 +7,8 @@ VALUE rb_sqlite3_define_aggregator2(VALUE self, VALUE aggregator, VALUE ruby_nam
7
7
 
8
8
  void rb_sqlite3_aggregator_init(void);
9
9
 
10
+ /* sqlite stores each live instance's VALUE in its aggregate context, so those
11
+ * must not move either. */
12
+ void rb_sqlite3_aggregator_pin_instances(VALUE aw);
13
+
10
14
  #endif
@@ -10,6 +10,10 @@
10
10
  if(!_ctxt->db) \
11
11
  rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed database");
12
12
 
13
+ #define REQUIRE_CLOSED_DB(_ctxt) \
14
+ if(_ctxt->db) \
15
+ rb_raise(rb_path2class("SQLite3::Exception"), "cannot open a database that is already open");
16
+
13
17
  VALUE cSqlite3Database;
14
18
 
15
19
  /* See adr/2024-09-fork-safety.md */
@@ -70,11 +74,64 @@ close_or_discard_db(sqlite3RubyPtr ctx)
70
74
  }
71
75
 
72
76
 
77
+ void
78
+ rb_sqlite3_pin_array_and_contents(VALUE ary)
79
+ {
80
+ long i;
81
+
82
+ if (NIL_P(ary) || !ary) { return; }
83
+
84
+ rb_gc_mark(ary);
85
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
86
+ rb_gc_mark(RARRAY_AREF(ary, i));
87
+ }
88
+ }
89
+
90
+ static int
91
+ pin_hash_value(VALUE UNUSED(key), VALUE value, VALUE UNUSED(arg))
92
+ {
93
+ rb_gc_mark(value);
94
+ return ST_CONTINUE;
95
+ }
96
+
97
+ static void
98
+ pin_hash_and_contents(VALUE hash)
99
+ {
100
+ if (NIL_P(hash) || !hash) { return; }
101
+
102
+ rb_gc_mark(hash);
103
+ rb_hash_foreach(hash, pin_hash_value, 0);
104
+ }
105
+
106
+ /* Each wrapper also owns live aggregate instances, whose VALUEs sqlite keeps in
107
+ * its own aggregate contexts. */
108
+ static void
109
+ pin_aggregators(VALUE aggregators)
110
+ {
111
+ long i;
112
+
113
+ rb_sqlite3_pin_array_and_contents(aggregators);
114
+
115
+ if (NIL_P(aggregators) || !aggregators) { return; }
116
+
117
+ for (i = 0; i < RARRAY_LEN(aggregators); i++) {
118
+ rb_sqlite3_aggregator_pin_instances(RARRAY_AREF(aggregators, i));
119
+ }
120
+ }
121
+
73
122
  static void
74
123
  database_mark(void *ctx)
75
124
  {
76
125
  sqlite3RubyPtr c = (sqlite3RubyPtr)ctx;
126
+
127
+ /* sqlite holds raw pointers to these, so they must not move. */
77
128
  rb_gc_mark(c->busy_handler);
129
+ rb_gc_mark(c->trace_handler);
130
+ rb_gc_mark(c->authorizer);
131
+
132
+ rb_sqlite3_pin_array_and_contents(c->functions);
133
+ pin_hash_and_contents(c->collations);
134
+ pin_aggregators(c->aggregators);
78
135
  }
79
136
 
80
137
  static void
@@ -114,7 +171,21 @@ allocate(VALUE klass)
114
171
  static char *
115
172
  utf16_string_value_ptr(VALUE str)
116
173
  {
174
+ VALUE utf16str, codepoints;
175
+
117
176
  StringValue(str);
177
+
178
+ utf16str = rb_str_dup(str);
179
+ if (!UTF16_LE_P(utf16str) && !UTF16_BE_P(utf16str)) {
180
+ /* an untagged string (the utf16 option) holds native-byte-order UTF-16 */
181
+ const union { uint16_t u16; uint8_t u8[2]; } native = { 1 };
182
+ rb_enc_associate_index(utf16str, rb_enc_find_index(native.u8[0] ? "UTF-16LE" : "UTF-16BE"));
183
+ }
184
+ codepoints = rb_funcall(utf16str, rb_intern("codepoints"), 0);
185
+ if (RTEST(rb_funcall(codepoints, rb_intern("include?"), 1, INT2FIX(0)))) {
186
+ rb_raise(rb_eArgError, "string contains null char");
187
+ }
188
+
118
189
  rb_str_buf_cat(str, "\x00\x00", 2L);
119
190
  return RSTRING_PTR(str);
120
191
  }
@@ -135,6 +206,7 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs)
135
206
  int flags;
136
207
 
137
208
  TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
209
+ REQUIRE_CLOSED_DB(ctx);
138
210
 
139
211
  #if defined TAINTING_SUPPORT
140
212
  # if defined StringValueCStr
@@ -147,13 +219,19 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs)
147
219
 
148
220
  flags = NUM2INT(mode);
149
221
  status = sqlite3_open_v2(
150
- StringValuePtr(file),
222
+ StringValueCStr(file),
151
223
  &ctx->db,
152
224
  flags,
153
- NIL_P(zvfs) ? NULL : StringValuePtr(zvfs)
225
+ NIL_P(zvfs) ? NULL : StringValueCStr(zvfs)
154
226
  );
155
227
 
156
- CHECK(ctx->db, status);
228
+ if (status != SQLITE_OK) {
229
+ char *msg = sqlite3_mprintf("%s", sqlite3_errmsg(ctx->db));
230
+ sqlite3_close_v2(ctx->db);
231
+ ctx->db = NULL;
232
+ CHECK_MSG(ctx->db, status, msg);
233
+ }
234
+
157
235
  if (flags & SQLITE_OPEN_READONLY) {
158
236
  ctx->flags |= SQLITE3_RB_DATABASE_READONLY;
159
237
  }
@@ -246,9 +324,8 @@ total_changes(VALUE self)
246
324
  static void
247
325
  tracefunc(void *data, const char *sql)
248
326
  {
249
- VALUE self = (VALUE)data;
250
- VALUE thing = rb_iv_get(self, "@tracefunc");
251
- rb_funcall(thing, rb_intern("call"), 1, rb_str_new2(sql));
327
+ sqlite3RubyPtr ctx = (sqlite3RubyPtr)data;
328
+ rb_funcall(ctx->trace_handler, rb_intern("call"), 1, rb_str_new2(sql));
252
329
  }
253
330
 
254
331
  /* call-seq:
@@ -272,9 +349,9 @@ trace(int argc, VALUE *argv, VALUE self)
272
349
 
273
350
  if (NIL_P(block) && rb_block_given_p()) { block = rb_block_proc(); }
274
351
 
275
- rb_iv_set(self, "@tracefunc", block);
352
+ RB_OBJ_WRITE(self, &ctx->trace_handler, block);
276
353
 
277
- sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)self);
354
+ sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)ctx);
278
355
 
279
356
  return self;
280
357
  }
@@ -340,8 +417,10 @@ rb_sqlite3_statement_timeout(void *context)
340
417
  clock_gettime(CLOCK_MONOTONIC, &currentTime);
341
418
 
342
419
  if (!timespecisset(&ctx->stmt_deadline)) {
343
- // Set stmt_deadline if not already set
344
- ctx->stmt_deadline = currentTime;
420
+ struct timespec timeout;
421
+ timeout.tv_sec = ctx->stmt_timeout / 1000;
422
+ timeout.tv_nsec = (ctx->stmt_timeout % 1000) * 1000000L;
423
+ timespecadd(&currentTime, &timeout, &ctx->stmt_deadline);
345
424
  } else if (timespecafter(&currentTime, &ctx->stmt_deadline)) {
346
425
  return 1;
347
426
  }
@@ -398,7 +477,9 @@ sqlite3val2rb(sqlite3_value *val)
398
477
  rb_val = rb_float_new(sqlite3_value_double(val));
399
478
  break;
400
479
  case SQLITE_TEXT: {
401
- rb_val = rb_utf8_str_new_cstr((const char *)sqlite3_value_text(val));
480
+ const char *text = (const char *)sqlite3_value_text(val);
481
+ int len = sqlite3_value_bytes(val);
482
+ rb_val = rb_utf8_str_new(text, len);
402
483
  rb_obj_freeze(rb_val);
403
484
  break;
404
485
  }
@@ -466,24 +547,46 @@ set_sqlite3_func_result(sqlite3_context *ctx, VALUE result)
466
547
  }
467
548
  }
468
549
 
469
- static void
470
- rb_sqlite3_func(sqlite3_context *ctx, int argc, sqlite3_value **argv)
550
+ typedef struct {
551
+ sqlite3_context *ctx;
552
+ int argc;
553
+ sqlite3_value **argv;
554
+ } rb_sqlite3_func_args_t;
555
+
556
+ static VALUE
557
+ rb_sqlite3_func_protected(VALUE func_args_value)
471
558
  {
472
- VALUE callable = (VALUE)sqlite3_user_data(ctx);
473
- VALUE params = rb_ary_new2(argc);
559
+ rb_sqlite3_func_args_t *args = (rb_sqlite3_func_args_t *)func_args_value;
560
+ VALUE callable = (VALUE)sqlite3_user_data(args->ctx);
561
+ VALUE params = rb_ary_new2(args->argc);
474
562
  VALUE result;
475
563
  int i;
476
564
 
477
- if (argc > 0) {
478
- for (i = 0; i < argc; i++) {
479
- VALUE param = sqlite3val2rb(argv[i]);
480
- rb_ary_push(params, param);
481
- }
565
+ for (i = 0; i < args->argc; i++) {
566
+ VALUE param = sqlite3val2rb(args->argv[i]);
567
+ rb_ary_push(params, param);
482
568
  }
483
569
 
484
570
  result = rb_apply(callable, rb_intern("call"), params);
485
571
 
486
- set_sqlite3_func_result(ctx, result);
572
+ set_sqlite3_func_result(args->ctx, result);
573
+
574
+ return Qnil;
575
+ }
576
+
577
+ static void
578
+ rb_sqlite3_func(sqlite3_context *ctx, int argc, sqlite3_value **argv)
579
+ {
580
+ rb_sqlite3_func_args_t args = { .ctx = ctx, .argc = argc, .argv = argv };
581
+ int exc_status;
582
+
583
+ rb_protect(rb_sqlite3_func_protected, (VALUE)&args, &exc_status);
584
+
585
+ if (exc_status) {
586
+ /* the user should never see this message, because Statement#step will
587
+ * re-raise the exception still in rb_errinfo */
588
+ sqlite3_result_error(ctx, "Ruby Exception occurred", -1);
589
+ }
487
590
  }
488
591
 
489
592
  #ifndef HAVE_RB_PROC_ARITY
@@ -503,7 +606,7 @@ static VALUE
503
606
  define_function_with_flags(VALUE self, VALUE name, VALUE flags)
504
607
  {
505
608
  sqlite3RubyPtr ctx;
506
- VALUE block;
609
+ VALUE block, functions;
507
610
  int status;
508
611
 
509
612
  TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
@@ -524,7 +627,9 @@ define_function_with_flags(VALUE self, VALUE name, VALUE flags)
524
627
 
525
628
  CHECK(ctx->db, status);
526
629
 
527
- rb_ary_push(rb_iv_get(self, "@functions"), block);
630
+ functions = rb_iv_get(self, "@functions");
631
+ rb_ary_push(functions, block);
632
+ RB_OBJ_WRITE(self, &ctx->functions, functions);
528
633
 
529
634
  return self;
530
635
  }
@@ -626,14 +731,13 @@ rb_sqlite3_auth(
626
731
  const char *_c,
627
732
  const char *_d)
628
733
  {
629
- VALUE self = (VALUE)ctx;
734
+ sqlite3RubyPtr db_ctx = (sqlite3RubyPtr)ctx;
630
735
  VALUE action = INT2NUM(_action);
631
736
  VALUE a = _a ? rb_str_new2(_a) : Qnil;
632
737
  VALUE b = _b ? rb_str_new2(_b) : Qnil;
633
738
  VALUE c = _c ? rb_str_new2(_c) : Qnil;
634
739
  VALUE d = _d ? rb_str_new2(_d) : Qnil;
635
- VALUE callback = rb_iv_get(self, "@authorizer");
636
- VALUE result = rb_funcall(callback, rb_intern("call"), 5, action, a, b, c, d);
740
+ VALUE result = rb_funcall(db_ctx->authorizer, rb_intern("call"), 5, action, a, b, c, d);
637
741
 
638
742
  if (T_FIXNUM == TYPE(result)) { return (int)NUM2INT(result); }
639
743
  if (Qtrue == result) { return SQLITE_OK; }
@@ -662,12 +766,12 @@ set_authorizer(VALUE self, VALUE authorizer)
662
766
  REQUIRE_OPEN_DB(ctx);
663
767
 
664
768
  status = sqlite3_set_authorizer(
665
- ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)self
769
+ ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)ctx
666
770
  );
667
771
 
668
772
  CHECK(ctx->db, status);
669
773
 
670
- rb_iv_set(self, "@authorizer", authorizer);
774
+ RB_OBJ_WRITE(self, &ctx->authorizer, authorizer);
671
775
 
672
776
  return self;
673
777
  }
@@ -750,6 +854,7 @@ static VALUE
750
854
  collation(VALUE self, VALUE name, VALUE comparator)
751
855
  {
752
856
  sqlite3RubyPtr ctx;
857
+ VALUE collations;
753
858
  TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
754
859
  REQUIRE_OPEN_DB(ctx);
755
860
 
@@ -760,8 +865,10 @@ collation(VALUE self, VALUE name, VALUE comparator)
760
865
  (void *)comparator,
761
866
  NIL_P(comparator) ? NULL : rb_comparator_func));
762
867
 
763
- /* Make sure our comparator doesn't get garbage collected. */
764
- rb_hash_aset(rb_iv_get(self, "@collations"), name, comparator);
868
+ /* sqlite holds a raw pointer to the comparator, so keep it alive and unmoved. */
869
+ collations = rb_iv_get(self, "@collations");
870
+ rb_hash_aset(collations, name, comparator);
871
+ RB_OBJ_WRITE(self, &ctx->collations, collations);
765
872
 
766
873
  return self;
767
874
  }
@@ -885,6 +992,8 @@ exec_batch(VALUE self, VALUE sql, VALUE results_as_hash)
885
992
  TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
886
993
  REQUIRE_OPEN_DB(ctx);
887
994
 
995
+ timespecclear(&ctx->stmt_deadline);
996
+
888
997
  if (results_as_hash == Qtrue) {
889
998
  status = sqlite3_exec(ctx->db, StringValuePtr(sql), (sqlite3_callback)hash_callback_function,
890
999
  (void *)callback_ary,
@@ -919,6 +1028,137 @@ db_filename(VALUE self, VALUE db_name)
919
1028
  return Qnil;
920
1029
  }
921
1030
 
1031
+ #ifdef HAVE_SQLITE3_SERIALIZE
1032
+ /* call-seq: db.serialize(database_name = "main")
1033
+ *
1034
+ * Serialize the database into a binary string.
1035
+ *
1036
+ * [database_name] The name of the database to serialize. The default value
1037
+ * is +main+ which is the main database file. For ATTACHed
1038
+ * databases, use the name given in the ATTACH statement.
1039
+ *
1040
+ * Returns the contents of the database as a binary string. For an ordinary
1041
+ * on-disk database file, this is just a copy of the disk file. For an
1042
+ * in-memory database or a "TEMP" database, this is the same sequence of bytes
1043
+ * which would be written to disk if that database were backed up to disk.
1044
+ *
1045
+ * Note: This method will only be defined if SQLite was compiled without
1046
+ * +SQLITE_OMIT_DESERIALIZE+.
1047
+ *
1048
+ * See also: https://www.sqlite.org/c3ref/serialize.html
1049
+ */
1050
+ static VALUE
1051
+ serialize(int argc, VALUE *argv, VALUE self)
1052
+ {
1053
+ sqlite3RubyPtr ctx;
1054
+ VALUE db_name;
1055
+ const char *zSchema;
1056
+ sqlite3_int64 size;
1057
+ unsigned char *data;
1058
+ VALUE result;
1059
+
1060
+ TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
1061
+ REQUIRE_OPEN_DB(ctx);
1062
+
1063
+ rb_scan_args(argc, argv, "01", &db_name);
1064
+
1065
+ zSchema = NIL_P(db_name) ? NULL : StringValueCStr(db_name);
1066
+
1067
+ data = sqlite3_serialize(ctx->db, zSchema, &size, 0);
1068
+
1069
+ if (data == NULL && size < 0) {
1070
+ rb_raise(rb_path2class("SQLite3::Exception"), "unable to serialize database \"%s\"",
1071
+ zSchema ? zSchema : "main");
1072
+ }
1073
+ if (data == NULL && size > 0) {
1074
+ rb_raise(rb_eNoMemError, "failed to allocate %lld bytes for serialization", (long long)size);
1075
+ }
1076
+
1077
+ result = rb_str_new((const char *)data, (long)size);
1078
+ sqlite3_free(data);
1079
+
1080
+ return result;
1081
+ }
1082
+ #endif
1083
+
1084
+ #ifdef HAVE_SQLITE3_DESERIALIZE
1085
+ /* call-seq: db.deserialize(data, database_name = "main")
1086
+ *
1087
+ * Deserialize a binary string into the database. The database is replaced with
1088
+ * the contents of the serialized data.
1089
+ *
1090
+ * [data] A binary string containing the serialized database content.
1091
+ * This is typically obtained from a prior call to #serialize.
1092
+ *
1093
+ * [database_name] The name of the database to replace. The default value is
1094
+ * +main+ which is the main database file. For ATTACHed
1095
+ * databases, use the name given in the ATTACH statement.
1096
+ *
1097
+ * Raises SQLite3::BusyException if the database has an open read or write
1098
+ * transaction, including one held by an unfinished statement, or is involved in
1099
+ * a backup.
1100
+ *
1101
+ * Deserializing into a database backed by a file detaches the connection from
1102
+ * that file. The file is left unchanged, and #filename(database_name) returns an
1103
+ * empty string afterwards.
1104
+ *
1105
+ * ⚠ An image of a database in WAL mode deserializes without error, but later
1106
+ * statements, even <tt>SELECT 1</tt>, raise SQLite3::CantOpenException. Rails
1107
+ * enables WAL mode by default.
1108
+ *
1109
+ * ⚠ Deserializing untrusted data carries the same risks as opening an untrusted
1110
+ * database file. See https://www.sqlite.org/security.html
1111
+ *
1112
+ * Note: This method will only be defined if the loaded SQLite is version 3.54.0
1113
+ * or later and was compiled without +SQLITE_OMIT_DESERIALIZE+. Older versions do
1114
+ * not raise SQLite3::BusyException; they free memory that an unfinished statement
1115
+ * still uses.
1116
+ *
1117
+ * See also: https://www.sqlite.org/c3ref/deserialize.html
1118
+ */
1119
+ static VALUE
1120
+ deserialize(int argc, VALUE *argv, VALUE self)
1121
+ {
1122
+ sqlite3RubyPtr ctx;
1123
+ VALUE data, db_name;
1124
+ const char *zSchema;
1125
+ unsigned char *pData;
1126
+ sqlite3_int64 szDb, szBuf;
1127
+ int status;
1128
+
1129
+ TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
1130
+ REQUIRE_OPEN_DB(ctx);
1131
+
1132
+ rb_scan_args(argc, argv, "11", &data, &db_name);
1133
+
1134
+ StringValue(data);
1135
+ zSchema = NIL_P(db_name) ? NULL : StringValueCStr(db_name);
1136
+ szDb = RSTRING_LEN(data);
1137
+
1138
+ /* SQLite's docs advise padding the buffer with about 20 extra bytes, because a maliciously
1139
+ * malformed image may make SQLite read past its end. See https://www.sqlite.org/c3ref/deserialize.html
1140
+ *
1141
+ * With SQLITE_DESERIALIZE_RESIZEABLE set, SQLite copies pages out of the buffer with bounds
1142
+ * checks instead of reading it in place, so the padding is defensive. It also keeps the
1143
+ * allocation nonzero for an empty string, for which sqlite3_malloc64(0) returns NULL.
1144
+ */
1145
+ szBuf = szDb + 20;
1146
+ pData = sqlite3_malloc64(szBuf);
1147
+ if (pData == NULL) {
1148
+ rb_raise(rb_eNoMemError, "failed to allocate %lld bytes for deserialization", (long long)szBuf);
1149
+ }
1150
+ memcpy(pData, RSTRING_PTR(data), (size_t)szDb);
1151
+ memset(pData + szDb, 0, (size_t)(szBuf - szDb));
1152
+
1153
+ status = sqlite3_deserialize(ctx->db, zSchema, pData, szDb, szDb,
1154
+ SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_RESIZEABLE);
1155
+
1156
+ CHECK(ctx->db, status);
1157
+
1158
+ return self;
1159
+ }
1160
+ #endif
1161
+
922
1162
  static VALUE
923
1163
  rb_sqlite3_open16(VALUE self, VALUE file)
924
1164
  {
@@ -926,6 +1166,7 @@ rb_sqlite3_open16(VALUE self, VALUE file)
926
1166
  sqlite3RubyPtr ctx;
927
1167
 
928
1168
  TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
1169
+ REQUIRE_CLOSED_DB(ctx);
929
1170
 
930
1171
  #if defined TAINTING_SUPPORT
931
1172
  #if defined StringValueCStr
@@ -941,7 +1182,12 @@ rb_sqlite3_open16(VALUE self, VALUE file)
941
1182
  // so we do not ever set SQLITE3_RB_DATABASE_READONLY in ctx->flags
942
1183
  status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
943
1184
 
944
- CHECK(ctx->db, status)
1185
+ if (status != SQLITE_OK) {
1186
+ char *msg = sqlite3_mprintf("%s", sqlite3_errmsg(ctx->db));
1187
+ sqlite3_close_v2(ctx->db);
1188
+ ctx->db = NULL;
1189
+ CHECK_MSG(ctx->db, status, msg);
1190
+ }
945
1191
 
946
1192
  return INT2NUM(status);
947
1193
  }
@@ -993,6 +1239,16 @@ init_sqlite3_database(void)
993
1239
  #endif
994
1240
  #endif
995
1241
 
1242
+ #ifdef HAVE_SQLITE3_SERIALIZE
1243
+ rb_define_method(cSqlite3Database, "serialize", serialize, -1);
1244
+ #endif
1245
+
1246
+ #ifdef HAVE_SQLITE3_DESERIALIZE
1247
+ /* Before 3.54.0, sqlite3_deserialize frees memory that an unfinished statement still uses. */
1248
+ if (sqlite3_libversion_number() >= 3054000) {
1249
+ rb_define_method(cSqlite3Database, "deserialize", deserialize, -1);
1250
+ }
1251
+ #endif
996
1252
 
997
1253
  rb_sqlite3_aggregator_init();
998
1254
  }
@@ -10,6 +10,11 @@
10
10
  struct _sqlite3Ruby {
11
11
  sqlite3 *db;
12
12
  VALUE busy_handler;
13
+ VALUE functions;
14
+ VALUE collations;
15
+ VALUE aggregators;
16
+ VALUE trace_handler;
17
+ VALUE authorizer;
13
18
  int stmt_timeout;
14
19
  struct timespec stmt_deadline;
15
20
  rb_pid_t owner;
@@ -19,6 +24,9 @@ struct _sqlite3Ruby {
19
24
  typedef struct _sqlite3Ruby sqlite3Ruby;
20
25
  typedef sqlite3Ruby *sqlite3RubyPtr;
21
26
 
27
+ /* Pinning a collection doesn't pin what's in it, hence both. */
28
+ void rb_sqlite3_pin_array_and_contents(VALUE ary);
29
+
22
30
  void init_sqlite3_database();
23
31
  void set_sqlite3_func_result(sqlite3_context *ctx, VALUE result);
24
32
 
@@ -65,8 +65,11 @@ module Sqlite3
65
65
  "-DSQLITE_ENABLE_DBPAGE_VTAB=1",
66
66
  "-DSQLITE_ENABLE_DBSTAT_VTAB=1"
67
67
  ]
68
- env["CFLAGS"] = [user_cflags, env["CFLAGS"], more_cflags].flatten.join(" ")
69
- recipe.configure_options += env.select { |k, v| ENV_ALLOWLIST.include?(k) }
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(" ")
72
+ recipe.configure_options += env.slice(*ENV_ALLOWLIST)
70
73
  .map { |key, value| "#{key}=#{value.strip}" }
71
74
  end
72
75
 
@@ -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
- # Truffle Ruby doesn't support this yet:
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")
@@ -66,6 +66,8 @@ prepare(VALUE self, VALUE db, VALUE sql)
66
66
  * variable will keep it from being GCed. */
67
67
  ctx->db = db_ctx;
68
68
 
69
+ timespecclear(&db_ctx->stmt_deadline);
70
+
69
71
  #ifdef HAVE_SQLITE3_PREPARE_V2
70
72
  status = sqlite3_prepare_v2(
71
73
  #else
@@ -79,7 +81,6 @@ prepare(VALUE self, VALUE db, VALUE sql)
79
81
  );
80
82
 
81
83
  CHECK_PREPARE(db_ctx->db, status, StringValuePtr(sql));
82
- timespecclear(&db_ctx->stmt_deadline);
83
84
 
84
85
  return rb_utf8_str_new_cstr(tail);
85
86
  }
@@ -139,6 +140,10 @@ step(VALUE self)
139
140
 
140
141
  stmt = ctx->st;
141
142
 
143
+ if (!sqlite3_stmt_busy(stmt)) {
144
+ timespecclear(&ctx->db->stmt_deadline);
145
+ }
146
+
142
147
  value = sqlite3_step(stmt);
143
148
  if (rb_errinfo() != Qnil) {
144
149
  /* some user defined function was invoked as a callback during step and
@@ -216,6 +221,21 @@ step(VALUE self)
216
221
  * Fixnum, it is treated as an index for a positional placeholder.
217
222
  * Otherwise it is used as the name of the placeholder to bind to.
218
223
  *
224
+ * Type mapping from Ruby to SQLite3:
225
+ * - +nil+ → NULL
226
+ * - Integer → INTEGER (or REAL when outside the signed int64 range)
227
+ * - Float → REAL
228
+ * - SQLite3::Blob → BLOB
229
+ * - String with Encoding::ASCII_8BIT (a.k.a. BINARY) → BLOB
230
+ * - String with Encoding::UTF_16LE or Encoding::UTF_16BE → TEXT (bound as UTF-16)
231
+ * - String (all other encodings) → TEXT (re-encoded to UTF-8 if necessary)
232
+ * - Any other type → raises RuntimeError
233
+ *
234
+ * Note: if you have a string with only ASCII characters but ASCII-8BIT
235
+ * encoding and want it bound as TEXT rather than BLOB, re-encode it first:
236
+ *
237
+ * stmt.bind_param(1, my_binary_str.encode(Encoding::UTF_8))
238
+ *
219
239
  * See also #bind_params.
220
240
  */
221
241
  static VALUE
@@ -398,6 +418,33 @@ interned_utf8_cstr(const char *str)
398
418
  }
399
419
  #endif
400
420
 
421
+ /* Like interned_utf8_cstr, but ASCII-downcases as it copies. */
422
+ static VALUE
423
+ interned_downcased_utf8_cstr(const char *str)
424
+ {
425
+ #if HAVE_RB_ENC_INTERNED_STR
426
+ VALUE tmp, ret;
427
+ char *buf;
428
+ size_t i, len;
429
+
430
+ len = strlen(str);
431
+ buf = ALLOCV_N(char, tmp, len);
432
+
433
+ for (i = 0; i < len; i++) {
434
+ unsigned char c = (unsigned char)str[i];
435
+ buf[i] = (char)((c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c);
436
+ }
437
+
438
+ ret = rb_enc_interned_str(buf, (long)len, rb_utf8_encoding());
439
+ ALLOCV_END(tmp);
440
+ return ret;
441
+ #else
442
+ VALUE rb_str = rb_funcall(rb_utf8_str_new_cstr(str), rb_intern("downcase"), 1,
443
+ ID2SYM(rb_intern("ascii")));
444
+ return rb_funcall(rb_str, rb_intern("-@"), 0);
445
+ #endif
446
+ }
447
+
401
448
  /* call-seq: stmt.column_name(index)
402
449
  *
403
450
  * Get the column name at +index+. 0 based.
@@ -425,7 +472,8 @@ column_name(VALUE self, VALUE index)
425
472
 
426
473
  /* call-seq: stmt.column_decltype(index)
427
474
  *
428
- * Get the column type at +index+. 0 based.
475
+ * Get the declared type of the column at +index+, as a UTF-8 string. 0 based.
476
+ * Returns +nil+ if the column has no declared type, such as an expression.
429
477
  */
430
478
  static VALUE
431
479
  column_decltype(VALUE self, VALUE index)
@@ -440,7 +488,24 @@ column_decltype(VALUE self, VALUE index)
440
488
 
441
489
  name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
442
490
 
443
- if (name) { return rb_str_new2(name); }
491
+ if (name) { return rb_utf8_str_new_cstr(name); }
492
+ return Qnil;
493
+ }
494
+
495
+ static VALUE
496
+ column_decltype_downcased(VALUE self, VALUE index)
497
+ {
498
+ sqlite3StmtRubyPtr ctx;
499
+ const char *name;
500
+
501
+ TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
502
+
503
+ REQUIRE_LIVE_DB(ctx);
504
+ REQUIRE_OPEN_STMT(ctx);
505
+
506
+ name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
507
+
508
+ if (name) { return interned_downcased_utf8_cstr(name); }
444
509
  return Qnil;
445
510
  }
446
511
 
@@ -733,6 +798,7 @@ init_sqlite3_statement(void)
733
798
  rb_define_method(cSqlite3Statement, "memused", memused, 0);
734
799
  #endif
735
800
 
801
+ rb_define_private_method(cSqlite3Statement, "column_decltype_downcased", column_decltype_downcased, 1);
736
802
  rb_define_private_method(cSqlite3Statement, "prepare", prepare, 2);
737
803
  rb_define_private_method(cSqlite3Statement, "stats_as_hash", stats_as_hash, 0);
738
804
  rb_define_private_method(cSqlite3Statement, "stat_for", stat_for, 1);
@@ -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))
Binary file
Binary file
Binary file
Binary file
@@ -171,8 +171,6 @@ module SQLite3
171
171
  end
172
172
  end
173
173
 
174
- @tracefunc = nil
175
- @authorizer = nil
176
174
  @progress_handler = nil
177
175
  @collations = {}
178
176
  @functions = []
@@ -458,9 +456,7 @@ module SQLite3
458
456
  #
459
457
  # See also #create_aggregate_handler for a more object-oriented approach to
460
458
  # aggregate functions.
461
- def create_aggregate(name, arity, step = nil, finalize = nil,
462
- text_rep = Constants::TextRep::ANY, &block)
463
-
459
+ def create_aggregate(name, arity, step = nil, finalize = nil, text_rep = Constants::TextRep::ANY, &block)
464
460
  proxy = Class.new do
465
461
  def self.step(&block)
466
462
  define_method(:step_with_ctx, &block)
@@ -43,20 +43,26 @@ module SQLite3
43
43
  @stmt.step
44
44
  end
45
45
 
46
- # Required by the Enumerable mixin. Provides an internal iterator over the
47
- # rows of the result set.
46
+ # With a block given, iterates over the rows of the result set, passing each
47
+ # to the block. Returns self.
48
+ # With no block given, returns a new Enumerator.
48
49
  def each
50
+ return enum_for(__method__) unless block_given?
49
51
  while (node = self.next)
50
52
  yield node
51
53
  end
54
+ self
52
55
  end
53
56
 
54
- # Provides an internal iterator over the rows of the result set where
55
- # each row is yielded as a hash.
57
+ # With a block given, iterates over the rows of the result set, passing each
58
+ # to the block as a hash. Returns self.
59
+ # With no block given, returns a new Enumerator.
56
60
  def each_hash
61
+ return enum_for(__method__) unless block_given?
57
62
  while (node = next_hash)
58
63
  yield node
59
64
  end
65
+ self
60
66
  end
61
67
 
62
68
  # Closes the statement that spawned this result set.
@@ -42,6 +42,24 @@ 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=?" )
@@ -112,9 +130,9 @@ module SQLite3
112
130
  !done?
113
131
  end
114
132
 
115
- # Return an array of the column names for this statement. Note that this
116
- # may execute the statement in order to obtain the metadata; this makes it
117
- # a (potentially) expensive operation.
133
+ # Return a frozen array of the column names for this statement, as frozen
134
+ # UTF-8 strings. Note that this may execute the statement in order to
135
+ # obtain the metadata; this makes it a (potentially) expensive operation.
118
136
  def columns
119
137
  get_metadata unless @columns
120
138
  @columns
@@ -128,9 +146,11 @@ module SQLite3
128
146
  end
129
147
  end
130
148
 
131
- # Return an array of the data types for each column in this statement. Note
132
- # that this may execute the statement in order to obtain the metadata; this
133
- # makes it a (potentially) expensive operation.
149
+ # Return a frozen array of the declared types of the columns in this
150
+ # statement, as frozen UTF-8 strings with ASCII letters downcased. A column
151
+ # with no declared type, such as an expression, has a +nil+ type. Note that
152
+ # this may execute the statement in order to obtain the metadata; this makes
153
+ # it a (potentially) expensive operation.
134
154
  def types
135
155
  must_be_open!
136
156
  get_metadata unless @types
@@ -180,11 +200,10 @@ module SQLite3
180
200
  def get_metadata
181
201
  @columns = Array.new(column_count) do |column|
182
202
  column_name column
183
- end
203
+ end.freeze
184
204
  @types = Array.new(column_count) do |column|
185
- val = column_decltype(column)
186
- val&.downcase
187
- end
205
+ column_decltype_downcased(column)
206
+ end.freeze
188
207
  end
189
208
  end
190
209
  end
@@ -1,4 +1,4 @@
1
1
  module SQLite3
2
2
  # (String) the version of the sqlite3 gem, e.g. "2.1.1"
3
- VERSION = "2.9.5"
3
+ VERSION = "2.10.0"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqlite3
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.5
4
+ version: 2.10.0
5
5
  platform: arm-linux-gnu
6
6
  authors:
7
7
  - Jamis Buck