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/database.c
CHANGED
|
@@ -74,51 +74,6 @@ close_or_discard_db(sqlite3RubyPtr ctx)
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
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
|
-
|
|
122
77
|
static void
|
|
123
78
|
database_mark(void *ctx)
|
|
124
79
|
{
|
|
@@ -130,8 +85,8 @@ database_mark(void *ctx)
|
|
|
130
85
|
rb_gc_mark(c->authorizer);
|
|
131
86
|
|
|
132
87
|
rb_sqlite3_pin_array_and_contents(c->functions);
|
|
133
|
-
|
|
134
|
-
|
|
88
|
+
rb_sqlite3_pin_array_and_contents(c->collations);
|
|
89
|
+
rb_sqlite3_aggregator_pin(c->aggregators);
|
|
135
90
|
}
|
|
136
91
|
|
|
137
92
|
static void
|
|
@@ -150,7 +105,7 @@ database_memsize(const void *ctx)
|
|
|
150
105
|
}
|
|
151
106
|
|
|
152
107
|
static const rb_data_type_t database_type = {
|
|
153
|
-
.wrap_struct_name = "SQLite3::
|
|
108
|
+
.wrap_struct_name = "SQLite3::Database",
|
|
154
109
|
.function = {
|
|
155
110
|
.dmark = database_mark,
|
|
156
111
|
.dfree = deallocate,
|
|
@@ -321,11 +276,21 @@ total_changes(VALUE self)
|
|
|
321
276
|
return INT2NUM(sqlite3_total_changes(ctx->db));
|
|
322
277
|
}
|
|
323
278
|
|
|
279
|
+
static VALUE
|
|
280
|
+
tracefunc_protected(VALUE args_value)
|
|
281
|
+
{
|
|
282
|
+
VALUE *args = (VALUE *)args_value;
|
|
283
|
+
return rb_funcall(args[0], rb_intern("call"), 1, rb_str_new2((const char *)args[1]));
|
|
284
|
+
}
|
|
285
|
+
|
|
324
286
|
static void
|
|
325
287
|
tracefunc(void *data, const char *sql)
|
|
326
288
|
{
|
|
327
289
|
sqlite3RubyPtr ctx = (sqlite3RubyPtr)data;
|
|
328
|
-
|
|
290
|
+
VALUE args[] = { ctx->trace_handler, (VALUE)sql };
|
|
291
|
+
int failed;
|
|
292
|
+
|
|
293
|
+
rb_sqlite3_protect_callback(tracefunc_protected, (VALUE)args, &failed);
|
|
329
294
|
}
|
|
330
295
|
|
|
331
296
|
/* call-seq:
|
|
@@ -356,15 +321,24 @@ trace(int argc, VALUE *argv, VALUE self)
|
|
|
356
321
|
return self;
|
|
357
322
|
}
|
|
358
323
|
|
|
324
|
+
static VALUE
|
|
325
|
+
rb_sqlite3_busy_handler_protected(VALUE args_value)
|
|
326
|
+
{
|
|
327
|
+
VALUE *args = (VALUE *)args_value;
|
|
328
|
+
return rb_funcall(args[0], rb_intern("call"), 1, INT2NUM((int)args[1]));
|
|
329
|
+
}
|
|
330
|
+
|
|
359
331
|
static int
|
|
360
332
|
rb_sqlite3_busy_handler(void *context, int count)
|
|
361
333
|
{
|
|
362
334
|
sqlite3RubyPtr ctx = (sqlite3RubyPtr)context;
|
|
335
|
+
VALUE args[] = { ctx->busy_handler, (VALUE)count };
|
|
336
|
+
VALUE result;
|
|
337
|
+
int failed;
|
|
363
338
|
|
|
364
|
-
|
|
365
|
-
VALUE result = rb_funcall(handle, rb_intern("call"), 1, INT2NUM(count));
|
|
339
|
+
result = rb_sqlite3_protect_callback(rb_sqlite3_busy_handler_protected, (VALUE)args, &failed);
|
|
366
340
|
|
|
367
|
-
if (Qfalse == result) { return 0; }
|
|
341
|
+
if (failed || Qfalse == result) { return 0; }
|
|
368
342
|
|
|
369
343
|
return 1;
|
|
370
344
|
}
|
|
@@ -417,8 +391,10 @@ rb_sqlite3_statement_timeout(void *context)
|
|
|
417
391
|
clock_gettime(CLOCK_MONOTONIC, ¤tTime);
|
|
418
392
|
|
|
419
393
|
if (!timespecisset(&ctx->stmt_deadline)) {
|
|
420
|
-
|
|
421
|
-
ctx->
|
|
394
|
+
struct timespec timeout;
|
|
395
|
+
timeout.tv_sec = ctx->stmt_timeout / 1000;
|
|
396
|
+
timeout.tv_nsec = (ctx->stmt_timeout % 1000) * 1000000L;
|
|
397
|
+
timespecadd(¤tTime, &timeout, &ctx->stmt_deadline);
|
|
422
398
|
} else if (timespecafter(¤tTime, &ctx->stmt_deadline)) {
|
|
423
399
|
return 1;
|
|
424
400
|
}
|
|
@@ -527,14 +503,14 @@ set_sqlite3_func_result(sqlite3_context *ctx, VALUE result)
|
|
|
527
503
|
sqlite3_result_blob(
|
|
528
504
|
ctx,
|
|
529
505
|
(const void *)StringValuePtr(result),
|
|
530
|
-
(
|
|
506
|
+
RSTRING_LENINT(result),
|
|
531
507
|
SQLITE_TRANSIENT
|
|
532
508
|
);
|
|
533
509
|
} else {
|
|
534
510
|
sqlite3_result_text(
|
|
535
511
|
ctx,
|
|
536
512
|
(const char *)StringValuePtr(result),
|
|
537
|
-
(
|
|
513
|
+
RSTRING_LENINT(result),
|
|
538
514
|
SQLITE_TRANSIENT
|
|
539
515
|
);
|
|
540
516
|
}
|
|
@@ -576,13 +552,13 @@ static void
|
|
|
576
552
|
rb_sqlite3_func(sqlite3_context *ctx, int argc, sqlite3_value **argv)
|
|
577
553
|
{
|
|
578
554
|
rb_sqlite3_func_args_t args = { .ctx = ctx, .argc = argc, .argv = argv };
|
|
579
|
-
int
|
|
555
|
+
int failed;
|
|
580
556
|
|
|
581
|
-
|
|
557
|
+
rb_sqlite3_protect_callback(rb_sqlite3_func_protected, (VALUE)&args, &failed);
|
|
582
558
|
|
|
583
|
-
if (
|
|
559
|
+
if (failed) {
|
|
584
560
|
/* the user should never see this message, because Statement#step will
|
|
585
|
-
*
|
|
561
|
+
* resume the Ruby exception instead */
|
|
586
562
|
sqlite3_result_error(ctx, "Ruby Exception occurred", -1);
|
|
587
563
|
}
|
|
588
564
|
}
|
|
@@ -720,6 +696,33 @@ changes(VALUE self)
|
|
|
720
696
|
return INT2NUM(sqlite3_changes(ctx->db));
|
|
721
697
|
}
|
|
722
698
|
|
|
699
|
+
typedef struct {
|
|
700
|
+
VALUE authorizer;
|
|
701
|
+
int action;
|
|
702
|
+
const char *a;
|
|
703
|
+
const char *b;
|
|
704
|
+
const char *c;
|
|
705
|
+
const char *d;
|
|
706
|
+
} rb_sqlite3_auth_args_t;
|
|
707
|
+
|
|
708
|
+
static VALUE
|
|
709
|
+
rb_sqlite3_auth_protected(VALUE auth_args_value)
|
|
710
|
+
{
|
|
711
|
+
rb_sqlite3_auth_args_t *args = (rb_sqlite3_auth_args_t *)auth_args_value;
|
|
712
|
+
VALUE action = INT2NUM(args->action);
|
|
713
|
+
VALUE a = args->a ? rb_str_new2(args->a) : Qnil;
|
|
714
|
+
VALUE b = args->b ? rb_str_new2(args->b) : Qnil;
|
|
715
|
+
VALUE c = args->c ? rb_str_new2(args->c) : Qnil;
|
|
716
|
+
VALUE d = args->d ? rb_str_new2(args->d) : Qnil;
|
|
717
|
+
VALUE result = rb_funcall(args->authorizer, rb_intern("call"), 5, action, a, b, c, d);
|
|
718
|
+
|
|
719
|
+
if (T_FIXNUM == TYPE(result)) { return INT2FIX(NUM2INT(result)); }
|
|
720
|
+
if (Qtrue == result) { return INT2FIX(SQLITE_OK); }
|
|
721
|
+
if (Qfalse == result) { return INT2FIX(SQLITE_DENY); }
|
|
722
|
+
|
|
723
|
+
return INT2FIX(SQLITE_IGNORE);
|
|
724
|
+
}
|
|
725
|
+
|
|
723
726
|
static int
|
|
724
727
|
rb_sqlite3_auth(
|
|
725
728
|
void *ctx,
|
|
@@ -730,18 +733,17 @@ rb_sqlite3_auth(
|
|
|
730
733
|
const char *_d)
|
|
731
734
|
{
|
|
732
735
|
sqlite3RubyPtr db_ctx = (sqlite3RubyPtr)ctx;
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
VALUE
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
if (
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
return SQLITE_IGNORE;
|
|
736
|
+
rb_sqlite3_auth_args_t args = {
|
|
737
|
+
.authorizer = db_ctx->authorizer, .action = _action, .a = _a, .b = _b, .c = _c, .d = _d
|
|
738
|
+
};
|
|
739
|
+
VALUE result;
|
|
740
|
+
int failed;
|
|
741
|
+
|
|
742
|
+
result = rb_sqlite3_protect_callback(rb_sqlite3_auth_protected, (VALUE)&args, &failed);
|
|
743
|
+
|
|
744
|
+
if (failed) { return SQLITE_DENY; }
|
|
745
|
+
|
|
746
|
+
return FIX2INT(result);
|
|
745
747
|
}
|
|
746
748
|
|
|
747
749
|
/* call-seq: set_authorizer = auth
|
|
@@ -813,10 +815,19 @@ set_extended_result_codes(VALUE self, VALUE enable)
|
|
|
813
815
|
return self;
|
|
814
816
|
}
|
|
815
817
|
|
|
816
|
-
|
|
817
|
-
rb_comparator_func(void *ctx, int a_len, const void *a, int b_len, const void *b)
|
|
818
|
-
{
|
|
818
|
+
typedef struct {
|
|
819
819
|
VALUE comparator;
|
|
820
|
+
int a_len;
|
|
821
|
+
const void *a;
|
|
822
|
+
int b_len;
|
|
823
|
+
const void *b;
|
|
824
|
+
int comparison;
|
|
825
|
+
} rb_comparator_args_t;
|
|
826
|
+
|
|
827
|
+
static VALUE
|
|
828
|
+
rb_comparator_protected(VALUE comparator_args_value)
|
|
829
|
+
{
|
|
830
|
+
rb_comparator_args_t *args = (rb_comparator_args_t *)comparator_args_value;
|
|
820
831
|
VALUE a_str;
|
|
821
832
|
VALUE b_str;
|
|
822
833
|
VALUE comparison;
|
|
@@ -824,9 +835,8 @@ rb_comparator_func(void *ctx, int a_len, const void *a, int b_len, const void *b
|
|
|
824
835
|
|
|
825
836
|
internal_encoding = rb_default_internal_encoding();
|
|
826
837
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
b_str = rb_str_new((const char *)b, b_len);
|
|
838
|
+
a_str = rb_str_new((const char *)args->a, args->a_len);
|
|
839
|
+
b_str = rb_str_new((const char *)args->b, args->b_len);
|
|
830
840
|
|
|
831
841
|
rb_enc_associate_index(a_str, rb_utf8_encindex());
|
|
832
842
|
rb_enc_associate_index(b_str, rb_utf8_encindex());
|
|
@@ -836,9 +846,30 @@ rb_comparator_func(void *ctx, int a_len, const void *a, int b_len, const void *b
|
|
|
836
846
|
b_str = rb_str_export_to_enc(b_str, internal_encoding);
|
|
837
847
|
}
|
|
838
848
|
|
|
839
|
-
comparison = rb_funcall(comparator, rb_intern("compare"), 2, a_str, b_str);
|
|
849
|
+
comparison = rb_funcall(args->comparator, rb_intern("compare"), 2, a_str, b_str);
|
|
850
|
+
|
|
851
|
+
args->comparison = NUM2INT(comparison);
|
|
840
852
|
|
|
841
|
-
return
|
|
853
|
+
return Qnil;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
int
|
|
857
|
+
rb_comparator_func(void *ctx, int a_len, const void *a, int b_len, const void *b)
|
|
858
|
+
{
|
|
859
|
+
rb_comparator_args_t args = { .comparator = (VALUE)ctx, .a_len = a_len, .a = a, .b_len = b_len, .b = b };
|
|
860
|
+
int failed;
|
|
861
|
+
|
|
862
|
+
rb_sqlite3_protect_callback(rb_comparator_protected, (VALUE)&args, &failed);
|
|
863
|
+
|
|
864
|
+
/* sqlite has no way to report a comparator error */
|
|
865
|
+
return failed ? 0 : args.comparison;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
static int
|
|
869
|
+
push_hash_value(VALUE UNUSED(key), VALUE value, VALUE ary)
|
|
870
|
+
{
|
|
871
|
+
rb_ary_push(ary, value);
|
|
872
|
+
return ST_CONTINUE;
|
|
842
873
|
}
|
|
843
874
|
|
|
844
875
|
/* call-seq: db.collation(name, comparator)
|
|
@@ -852,21 +883,42 @@ static VALUE
|
|
|
852
883
|
collation(VALUE self, VALUE name, VALUE comparator)
|
|
853
884
|
{
|
|
854
885
|
sqlite3RubyPtr ctx;
|
|
855
|
-
VALUE
|
|
886
|
+
VALUE registry, key, comparators;
|
|
887
|
+
char *key_ptr;
|
|
888
|
+
long i;
|
|
856
889
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
857
890
|
REQUIRE_OPEN_DB(ctx);
|
|
858
891
|
|
|
892
|
+
/* sqlite holds a raw pointer to the comparator, so keep it alive and
|
|
893
|
+
* unmoved. The registry is private because callers can mutate the public
|
|
894
|
+
* @collations hash, and the snapshot is an array so database_mark can pin
|
|
895
|
+
* it without iterating a hash. sqlite matches collation names without
|
|
896
|
+
* regard to ASCII case, so the registry does too. The key and registry
|
|
897
|
+
* are prepared before sqlite takes the pointer, and the snapshot is
|
|
898
|
+
* published before the public hash, whose write can raise. */
|
|
899
|
+
key = rb_str_new_cstr(StringValuePtr(name));
|
|
900
|
+
key_ptr = RSTRING_PTR(key);
|
|
901
|
+
for (i = 0; i < RSTRING_LEN(key); i++) {
|
|
902
|
+
if (key_ptr[i] >= 'A' && key_ptr[i] <= 'Z') { key_ptr[i] += 'a' - 'A'; }
|
|
903
|
+
}
|
|
904
|
+
if (!rb_ivar_defined(self, rb_intern("-collations"))) {
|
|
905
|
+
rb_iv_set(self, "-collations", rb_hash_new());
|
|
906
|
+
}
|
|
907
|
+
registry = rb_iv_get(self, "-collations");
|
|
908
|
+
|
|
859
909
|
CHECK(ctx->db, sqlite3_create_collation(
|
|
860
910
|
ctx->db,
|
|
861
|
-
|
|
911
|
+
RSTRING_PTR(name),
|
|
862
912
|
SQLITE_UTF8,
|
|
863
913
|
(void *)comparator,
|
|
864
914
|
NIL_P(comparator) ? NULL : rb_comparator_func));
|
|
865
915
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
RB_OBJ_WRITE(self, &ctx->collations,
|
|
916
|
+
rb_hash_aset(registry, key, comparator);
|
|
917
|
+
comparators = rb_ary_new();
|
|
918
|
+
rb_hash_foreach(registry, push_hash_value, comparators);
|
|
919
|
+
RB_OBJ_WRITE(self, &ctx->collations, comparators);
|
|
920
|
+
|
|
921
|
+
rb_hash_aset(rb_iv_get(self, "@collations"), name, comparator);
|
|
870
922
|
|
|
871
923
|
return self;
|
|
872
924
|
}
|
|
@@ -932,42 +984,68 @@ transaction_active_p(VALUE self)
|
|
|
932
984
|
return sqlite3_get_autocommit(ctx->db) ? Qfalse : Qtrue;
|
|
933
985
|
}
|
|
934
986
|
|
|
935
|
-
|
|
936
|
-
|
|
987
|
+
typedef struct {
|
|
988
|
+
VALUE callback_ary;
|
|
989
|
+
VALUE (*build_row)(VALUE);
|
|
990
|
+
} rb_sqlite3_exec_batch_t;
|
|
991
|
+
|
|
992
|
+
typedef struct {
|
|
993
|
+
VALUE callback_ary;
|
|
994
|
+
int count;
|
|
995
|
+
char **data;
|
|
996
|
+
char **columns;
|
|
997
|
+
} rb_sqlite3_exec_row_t;
|
|
998
|
+
|
|
999
|
+
static VALUE
|
|
1000
|
+
hash_callback_protected(VALUE row_value)
|
|
937
1001
|
{
|
|
1002
|
+
rb_sqlite3_exec_row_t *row = (rb_sqlite3_exec_row_t *)row_value;
|
|
938
1003
|
VALUE new_hash = rb_hash_new();
|
|
939
1004
|
int i;
|
|
940
1005
|
|
|
941
|
-
for (i = 0; i < count; i++) {
|
|
942
|
-
if (data[i] == NULL) {
|
|
943
|
-
rb_hash_aset(new_hash, rb_str_new_cstr(columns[i]), Qnil);
|
|
1006
|
+
for (i = 0; i < row->count; i++) {
|
|
1007
|
+
if (row->data[i] == NULL) {
|
|
1008
|
+
rb_hash_aset(new_hash, rb_str_new_cstr(row->columns[i]), Qnil);
|
|
944
1009
|
} else {
|
|
945
|
-
rb_hash_aset(new_hash, rb_str_new_cstr(columns[i]), rb_str_new_cstr(data[i]));
|
|
1010
|
+
rb_hash_aset(new_hash, rb_str_new_cstr(row->columns[i]), rb_str_new_cstr(row->data[i]));
|
|
946
1011
|
}
|
|
947
1012
|
}
|
|
948
1013
|
|
|
949
|
-
rb_ary_push(callback_ary, new_hash);
|
|
1014
|
+
rb_ary_push(row->callback_ary, new_hash);
|
|
950
1015
|
|
|
951
|
-
return
|
|
1016
|
+
return Qnil;
|
|
952
1017
|
}
|
|
953
1018
|
|
|
954
|
-
static
|
|
955
|
-
|
|
1019
|
+
static VALUE
|
|
1020
|
+
regular_callback_protected(VALUE row_value)
|
|
956
1021
|
{
|
|
1022
|
+
rb_sqlite3_exec_row_t *row = (rb_sqlite3_exec_row_t *)row_value;
|
|
957
1023
|
VALUE new_ary = rb_ary_new();
|
|
958
1024
|
int i;
|
|
959
1025
|
|
|
960
|
-
for (i = 0; i < count; i++) {
|
|
961
|
-
if (data[i] == NULL) {
|
|
1026
|
+
for (i = 0; i < row->count; i++) {
|
|
1027
|
+
if (row->data[i] == NULL) {
|
|
962
1028
|
rb_ary_push(new_ary, Qnil);
|
|
963
1029
|
} else {
|
|
964
|
-
rb_ary_push(new_ary, rb_str_new_cstr(data[i]));
|
|
1030
|
+
rb_ary_push(new_ary, rb_str_new_cstr(row->data[i]));
|
|
965
1031
|
}
|
|
966
1032
|
}
|
|
967
1033
|
|
|
968
|
-
rb_ary_push(callback_ary, new_ary);
|
|
1034
|
+
rb_ary_push(row->callback_ary, new_ary);
|
|
969
1035
|
|
|
970
|
-
return
|
|
1036
|
+
return Qnil;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
static int
|
|
1040
|
+
exec_batch_callback_function(void *batch_ptr, int count, char **data, char **columns)
|
|
1041
|
+
{
|
|
1042
|
+
rb_sqlite3_exec_batch_t *batch = (rb_sqlite3_exec_batch_t *)batch_ptr;
|
|
1043
|
+
rb_sqlite3_exec_row_t row = { .callback_ary = batch->callback_ary, .count = count, .data = data, .columns = columns };
|
|
1044
|
+
int failed;
|
|
1045
|
+
|
|
1046
|
+
rb_sqlite3_protect_callback(batch->build_row, (VALUE)&row, &failed);
|
|
1047
|
+
|
|
1048
|
+
return failed;
|
|
971
1049
|
}
|
|
972
1050
|
|
|
973
1051
|
|
|
@@ -985,19 +1063,27 @@ exec_batch(VALUE self, VALUE sql, VALUE results_as_hash)
|
|
|
985
1063
|
sqlite3RubyPtr ctx;
|
|
986
1064
|
int status;
|
|
987
1065
|
VALUE callback_ary = rb_ary_new();
|
|
1066
|
+
rb_sqlite3_exec_batch_t batch = {
|
|
1067
|
+
.callback_ary = callback_ary,
|
|
1068
|
+
.build_row = results_as_hash == Qtrue ? hash_callback_protected : regular_callback_protected
|
|
1069
|
+
};
|
|
1070
|
+
rb_sqlite3_callback_frame_t frame;
|
|
1071
|
+
int callback_state;
|
|
1072
|
+
const char *sql_cstr;
|
|
988
1073
|
char *errMsg;
|
|
989
1074
|
|
|
990
1075
|
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
991
1076
|
REQUIRE_OPEN_DB(ctx);
|
|
992
1077
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1078
|
+
sql_cstr = StringValuePtr(sql);
|
|
1079
|
+
timespecclear(&ctx->stmt_deadline);
|
|
1080
|
+
|
|
1081
|
+
rb_sqlite3_push_callback_frame(&frame);
|
|
1082
|
+
status = sqlite3_exec(ctx->db, sql_cstr, exec_batch_callback_function, (void *)&batch, &errMsg);
|
|
1083
|
+
|
|
1084
|
+
if ((callback_state = rb_sqlite3_pop_callback_frame(&frame))) {
|
|
1085
|
+
sqlite3_free(errMsg);
|
|
1086
|
+
rb_jump_tag(callback_state);
|
|
1001
1087
|
}
|
|
1002
1088
|
|
|
1003
1089
|
CHECK_MSG(ctx->db, status, errMsg);
|
|
@@ -1024,6 +1110,137 @@ db_filename(VALUE self, VALUE db_name)
|
|
|
1024
1110
|
return Qnil;
|
|
1025
1111
|
}
|
|
1026
1112
|
|
|
1113
|
+
#ifdef HAVE_SQLITE3_SERIALIZE
|
|
1114
|
+
/* call-seq: db.serialize(database_name = "main")
|
|
1115
|
+
*
|
|
1116
|
+
* Serialize the database into a binary string.
|
|
1117
|
+
*
|
|
1118
|
+
* [database_name] The name of the database to serialize. The default value
|
|
1119
|
+
* is +main+ which is the main database file. For ATTACHed
|
|
1120
|
+
* databases, use the name given in the ATTACH statement.
|
|
1121
|
+
*
|
|
1122
|
+
* Returns the contents of the database as a binary string. For an ordinary
|
|
1123
|
+
* on-disk database file, this is just a copy of the disk file. For an
|
|
1124
|
+
* in-memory database or a "TEMP" database, this is the same sequence of bytes
|
|
1125
|
+
* which would be written to disk if that database were backed up to disk.
|
|
1126
|
+
*
|
|
1127
|
+
* Note: This method will only be defined if SQLite was compiled without
|
|
1128
|
+
* +SQLITE_OMIT_DESERIALIZE+.
|
|
1129
|
+
*
|
|
1130
|
+
* See also: https://www.sqlite.org/c3ref/serialize.html
|
|
1131
|
+
*/
|
|
1132
|
+
static VALUE
|
|
1133
|
+
serialize(int argc, VALUE *argv, VALUE self)
|
|
1134
|
+
{
|
|
1135
|
+
sqlite3RubyPtr ctx;
|
|
1136
|
+
VALUE db_name;
|
|
1137
|
+
const char *zSchema;
|
|
1138
|
+
sqlite3_int64 size;
|
|
1139
|
+
unsigned char *data;
|
|
1140
|
+
VALUE result;
|
|
1141
|
+
|
|
1142
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
1143
|
+
REQUIRE_OPEN_DB(ctx);
|
|
1144
|
+
|
|
1145
|
+
rb_scan_args(argc, argv, "01", &db_name);
|
|
1146
|
+
|
|
1147
|
+
zSchema = NIL_P(db_name) ? NULL : StringValueCStr(db_name);
|
|
1148
|
+
|
|
1149
|
+
data = sqlite3_serialize(ctx->db, zSchema, &size, 0);
|
|
1150
|
+
|
|
1151
|
+
if (data == NULL && size < 0) {
|
|
1152
|
+
rb_raise(rb_path2class("SQLite3::Exception"), "unable to serialize database \"%s\"",
|
|
1153
|
+
zSchema ? zSchema : "main");
|
|
1154
|
+
}
|
|
1155
|
+
if (data == NULL && size > 0) {
|
|
1156
|
+
rb_raise(rb_eNoMemError, "failed to allocate %lld bytes for serialization", (long long)size);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
result = rb_str_new((const char *)data, (long)size);
|
|
1160
|
+
sqlite3_free(data);
|
|
1161
|
+
|
|
1162
|
+
return result;
|
|
1163
|
+
}
|
|
1164
|
+
#endif
|
|
1165
|
+
|
|
1166
|
+
#ifdef HAVE_SQLITE3_DESERIALIZE
|
|
1167
|
+
/* call-seq: db.deserialize(data, database_name = "main")
|
|
1168
|
+
*
|
|
1169
|
+
* Deserialize a binary string into the database. The database is replaced with
|
|
1170
|
+
* the contents of the serialized data.
|
|
1171
|
+
*
|
|
1172
|
+
* [data] A binary string containing the serialized database content.
|
|
1173
|
+
* This is typically obtained from a prior call to #serialize.
|
|
1174
|
+
*
|
|
1175
|
+
* [database_name] The name of the database to replace. The default value is
|
|
1176
|
+
* +main+ which is the main database file. For ATTACHed
|
|
1177
|
+
* databases, use the name given in the ATTACH statement.
|
|
1178
|
+
*
|
|
1179
|
+
* Raises SQLite3::BusyException if the database has an open read or write
|
|
1180
|
+
* transaction, including one held by an unfinished statement, or is involved in
|
|
1181
|
+
* a backup.
|
|
1182
|
+
*
|
|
1183
|
+
* Deserializing into a database backed by a file detaches the connection from
|
|
1184
|
+
* that file. The file is left unchanged, and #filename(database_name) returns an
|
|
1185
|
+
* empty string afterwards.
|
|
1186
|
+
*
|
|
1187
|
+
* ⚠ An image of a database in WAL mode deserializes without error, but later
|
|
1188
|
+
* statements, even <tt>SELECT 1</tt>, raise SQLite3::CantOpenException. Rails
|
|
1189
|
+
* enables WAL mode by default.
|
|
1190
|
+
*
|
|
1191
|
+
* ⚠ Deserializing untrusted data carries the same risks as opening an untrusted
|
|
1192
|
+
* database file. See https://www.sqlite.org/security.html
|
|
1193
|
+
*
|
|
1194
|
+
* Note: This method will only be defined if the loaded SQLite is version 3.54.0
|
|
1195
|
+
* or later and was compiled without +SQLITE_OMIT_DESERIALIZE+. Older versions do
|
|
1196
|
+
* not raise SQLite3::BusyException; they free memory that an unfinished statement
|
|
1197
|
+
* still uses.
|
|
1198
|
+
*
|
|
1199
|
+
* See also: https://www.sqlite.org/c3ref/deserialize.html
|
|
1200
|
+
*/
|
|
1201
|
+
static VALUE
|
|
1202
|
+
deserialize(int argc, VALUE *argv, VALUE self)
|
|
1203
|
+
{
|
|
1204
|
+
sqlite3RubyPtr ctx;
|
|
1205
|
+
VALUE data, db_name;
|
|
1206
|
+
const char *zSchema;
|
|
1207
|
+
unsigned char *pData;
|
|
1208
|
+
sqlite3_int64 szDb, szBuf;
|
|
1209
|
+
int status;
|
|
1210
|
+
|
|
1211
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
|
1212
|
+
REQUIRE_OPEN_DB(ctx);
|
|
1213
|
+
|
|
1214
|
+
rb_scan_args(argc, argv, "11", &data, &db_name);
|
|
1215
|
+
|
|
1216
|
+
StringValue(data);
|
|
1217
|
+
zSchema = NIL_P(db_name) ? NULL : StringValueCStr(db_name);
|
|
1218
|
+
szDb = RSTRING_LEN(data);
|
|
1219
|
+
|
|
1220
|
+
/* SQLite's docs advise padding the buffer with about 20 extra bytes, because a maliciously
|
|
1221
|
+
* malformed image may make SQLite read past its end. See https://www.sqlite.org/c3ref/deserialize.html
|
|
1222
|
+
*
|
|
1223
|
+
* With SQLITE_DESERIALIZE_RESIZEABLE set, SQLite copies pages out of the buffer with bounds
|
|
1224
|
+
* checks instead of reading it in place, so the padding is defensive. It also keeps the
|
|
1225
|
+
* allocation nonzero for an empty string, for which sqlite3_malloc64(0) returns NULL.
|
|
1226
|
+
*/
|
|
1227
|
+
szBuf = szDb + 20;
|
|
1228
|
+
pData = sqlite3_malloc64(szBuf);
|
|
1229
|
+
if (pData == NULL) {
|
|
1230
|
+
rb_raise(rb_eNoMemError, "failed to allocate %lld bytes for deserialization", (long long)szBuf);
|
|
1231
|
+
}
|
|
1232
|
+
memcpy(pData, RSTRING_PTR(data), (size_t)szDb);
|
|
1233
|
+
memset(pData + szDb, 0, (size_t)(szBuf - szDb));
|
|
1234
|
+
|
|
1235
|
+
status = sqlite3_deserialize(ctx->db, zSchema, pData, szDb, szDb,
|
|
1236
|
+
SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_RESIZEABLE);
|
|
1237
|
+
|
|
1238
|
+
CHECK(ctx->db, status);
|
|
1239
|
+
|
|
1240
|
+
return self;
|
|
1241
|
+
}
|
|
1242
|
+
#endif
|
|
1243
|
+
|
|
1027
1244
|
static VALUE
|
|
1028
1245
|
rb_sqlite3_open16(VALUE self, VALUE file)
|
|
1029
1246
|
{
|
|
@@ -1104,6 +1321,16 @@ init_sqlite3_database(void)
|
|
|
1104
1321
|
#endif
|
|
1105
1322
|
#endif
|
|
1106
1323
|
|
|
1324
|
+
#ifdef HAVE_SQLITE3_SERIALIZE
|
|
1325
|
+
rb_define_method(cSqlite3Database, "serialize", serialize, -1);
|
|
1326
|
+
#endif
|
|
1327
|
+
|
|
1328
|
+
#ifdef HAVE_SQLITE3_DESERIALIZE
|
|
1329
|
+
/* Before 3.54.0, sqlite3_deserialize frees memory that an unfinished statement still uses. */
|
|
1330
|
+
if (sqlite3_libversion_number() >= 3054000) {
|
|
1331
|
+
rb_define_method(cSqlite3Database, "deserialize", deserialize, -1);
|
|
1332
|
+
}
|
|
1333
|
+
#endif
|
|
1107
1334
|
|
|
1108
1335
|
rb_sqlite3_aggregator_init();
|
|
1109
1336
|
}
|
data/ext/sqlite3/database.h
CHANGED
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
struct _sqlite3Ruby {
|
|
11
11
|
sqlite3 *db;
|
|
12
12
|
VALUE busy_handler;
|
|
13
|
+
/* sqlite holds raw pointers to everything in these arrays. functions and
|
|
14
|
+
* aggregators are append-only, so redefining a name keeps the old object
|
|
15
|
+
* for the life of the connection: sqlite keys them by name and arity, and
|
|
16
|
+
* releasing on name alone was GHSA-28hh-pr2h-2w89. collations is rebuilt
|
|
17
|
+
* from a private hash keyed by name without regard to ASCII case, as
|
|
18
|
+
* sqlite keys them, so it releases a replaced comparator. */
|
|
13
19
|
VALUE functions;
|
|
14
20
|
VALUE collations;
|
|
15
21
|
VALUE aggregators;
|
|
@@ -24,9 +30,6 @@ struct _sqlite3Ruby {
|
|
|
24
30
|
typedef struct _sqlite3Ruby sqlite3Ruby;
|
|
25
31
|
typedef sqlite3Ruby *sqlite3RubyPtr;
|
|
26
32
|
|
|
27
|
-
/* Pinning a collection doesn't pin what's in it, hence both. */
|
|
28
|
-
void rb_sqlite3_pin_array_and_contents(VALUE ary);
|
|
29
|
-
|
|
30
33
|
void init_sqlite3_database();
|
|
31
34
|
void set_sqlite3_func_result(sqlite3_context *ctx, VALUE result);
|
|
32
35
|
|