extralite 2.3 → 2.5

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.
@@ -3,6 +3,15 @@
3
3
 
4
4
  rb_encoding *UTF8_ENCODING;
5
5
 
6
+ inline void *gvl_call(enum gvl_mode mode, void *(*fn)(void *), void *data) {
7
+ switch (mode) {
8
+ case GVL_RELEASE:
9
+ return rb_thread_call_without_gvl(fn, data, RUBY_UBF_IO, 0);
10
+ default:
11
+ return fn(data);
12
+ }
13
+ }
14
+
6
15
  static inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
7
16
  switch (type) {
8
17
  case SQLITE_NULL:
@@ -22,7 +31,25 @@ static inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
22
31
  return Qnil;
23
32
  }
24
33
 
25
- void bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value);
34
+ int bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value);
35
+
36
+ static inline void bind_key_value(sqlite3_stmt *stmt, VALUE k, VALUE v) {
37
+ switch (TYPE(k)) {
38
+ case T_FIXNUM:
39
+ bind_parameter_value(stmt, FIX2INT(k), v);
40
+ break;
41
+ case T_SYMBOL:
42
+ k = rb_sym2str(k);
43
+ case T_STRING:
44
+ if (RSTRING_PTR(k)[0] != ':') k = rb_str_plus(rb_str_new2(":"), k);
45
+ int pos = sqlite3_bind_parameter_index(stmt, StringValuePtr(k));
46
+ bind_parameter_value(stmt, pos, v);
47
+ break;
48
+ default:
49
+ rb_raise(cParameterError, "Cannot bind parameter with a key of type %"PRIsVALUE"",
50
+ rb_class_name(rb_obj_class(k)));
51
+ }
52
+ }
26
53
 
27
54
  void bind_hash_parameter_values(sqlite3_stmt *stmt, VALUE hash) {
28
55
  VALUE keys = rb_funcall(hash, ID_keys, 0);
@@ -30,62 +57,79 @@ void bind_hash_parameter_values(sqlite3_stmt *stmt, VALUE hash) {
30
57
  for (long i = 0; i < len; i++) {
31
58
  VALUE k = RARRAY_AREF(keys, i);
32
59
  VALUE v = rb_hash_aref(hash, k);
33
-
34
- switch (TYPE(k)) {
35
- case T_FIXNUM:
36
- bind_parameter_value(stmt, FIX2INT(k), v);
37
- break;
38
- case T_SYMBOL:
39
- k = rb_funcall(k, ID_to_s, 0);
40
- case T_STRING:
41
- if(RSTRING_PTR(k)[0] != ':') k = rb_str_plus(rb_str_new2(":"), k);
42
- int pos = sqlite3_bind_parameter_index(stmt, StringValuePtr(k));
43
- bind_parameter_value(stmt, pos, v);
44
- break;
45
- default:
46
- rb_raise(cError, "Cannot bind hash key value idx %ld", i);
47
- }
60
+ bind_key_value(stmt, k, v);
48
61
  }
49
62
  RB_GC_GUARD(keys);
50
63
  }
51
64
 
52
- inline void bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value) {
65
+ void bind_struct_parameter_values(sqlite3_stmt *stmt, VALUE struct_obj) {
66
+ VALUE members = rb_struct_members(struct_obj);
67
+ for (long i = 0; i < RSTRUCT_LEN(struct_obj); i++) {
68
+ VALUE k = rb_ary_entry(members, i);
69
+ VALUE v = RSTRUCT_GET(struct_obj, i);
70
+ bind_key_value(stmt, k, v);
71
+ }
72
+ RB_GC_GUARD(members);
73
+ }
74
+
75
+ inline int bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value) {
53
76
  switch (TYPE(value)) {
54
77
  case T_NIL:
55
78
  sqlite3_bind_null(stmt, pos);
56
- return;
79
+ return 1;
57
80
  case T_FIXNUM:
81
+ case T_BIGNUM:
58
82
  sqlite3_bind_int64(stmt, pos, NUM2LL(value));
59
- return;
83
+ return 1;
60
84
  case T_FLOAT:
61
85
  sqlite3_bind_double(stmt, pos, NUM2DBL(value));
62
- return;
86
+ return 1;
63
87
  case T_TRUE:
64
88
  sqlite3_bind_int(stmt, pos, 1);
65
- return;
89
+ return 1;
66
90
  case T_FALSE:
67
91
  sqlite3_bind_int(stmt, pos, 0);
68
- return;
92
+ return 1;
93
+ case T_SYMBOL:
94
+ value = rb_sym2str(value);
69
95
  case T_STRING:
70
- sqlite3_bind_text(stmt, pos, RSTRING_PTR(value), RSTRING_LEN(value), SQLITE_TRANSIENT);
71
- return;
96
+ if (rb_enc_get_index(value) == rb_ascii8bit_encindex() || CLASS_OF(value) == cBlob)
97
+ sqlite3_bind_blob(stmt, pos, RSTRING_PTR(value), RSTRING_LEN(value), SQLITE_TRANSIENT);
98
+ else
99
+ sqlite3_bind_text(stmt, pos, RSTRING_PTR(value), RSTRING_LEN(value), SQLITE_TRANSIENT);
100
+ return 1;
101
+ case T_ARRAY:
102
+ {
103
+ int count = RARRAY_LEN(value);
104
+ for (int i = 0; i < count; i++)
105
+ bind_parameter_value(stmt, pos + i, RARRAY_AREF(value, i));
106
+ return count;
107
+ }
72
108
  case T_HASH:
73
109
  bind_hash_parameter_values(stmt, value);
74
- return;
110
+ return 0;
111
+ case T_STRUCT:
112
+ bind_struct_parameter_values(stmt, value);
113
+ return 0;
75
114
  default:
76
- rb_raise(cError, "Cannot bind parameter at position %d", pos);
115
+ rb_raise(cParameterError, "Cannot bind parameter at position %d of type %"PRIsVALUE"",
116
+ pos, rb_class_name(rb_obj_class(value)));
77
117
  }
78
118
  }
79
119
 
80
120
  inline void bind_all_parameters(sqlite3_stmt *stmt, int argc, VALUE *argv) {
81
- for (int i = 0; i < argc; i++) bind_parameter_value(stmt, i + 1, argv[i]);
121
+ int pos = 1;
122
+ for (int i = 0; i < argc; i++) {
123
+ pos += bind_parameter_value(stmt, pos, argv[i]);
124
+ }
82
125
  }
83
126
 
84
127
  inline void bind_all_parameters_from_object(sqlite3_stmt *stmt, VALUE obj) {
85
128
  if (TYPE(obj) == T_ARRAY) {
129
+ int pos = 1;
86
130
  int count = RARRAY_LEN(obj);
87
131
  for (int i = 0; i < count; i++)
88
- bind_parameter_value(stmt, i + 1, RARRAY_AREF(obj, i));
132
+ pos += bind_parameter_value(stmt, pos, RARRAY_AREF(obj, i));
89
133
  }
90
134
  else
91
135
  bind_parameter_value(stmt, 1, obj);
@@ -126,7 +170,7 @@ typedef struct {
126
170
  int rc;
127
171
  } prepare_stmt_ctx;
128
172
 
129
- void *prepare_multi_stmt_without_gvl(void *ptr) {
173
+ void *prepare_multi_stmt_impl(void *ptr) {
130
174
  prepare_stmt_ctx *ctx = (prepare_stmt_ctx *)ptr;
131
175
  const char *rest = NULL;
132
176
  const char *str = ctx->str;
@@ -163,7 +207,7 @@ is not executed, but instead handed back to the caller for looping over results.
163
207
  */
164
208
  void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
165
209
  prepare_stmt_ctx ctx = {db, stmt, RSTRING_PTR(sql), RSTRING_LEN(sql), 0};
166
- rb_thread_call_without_gvl(prepare_multi_stmt_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
210
+ gvl_call(GVL_RELEASE, prepare_multi_stmt_impl, (void *)&ctx);
167
211
  RB_GC_GUARD(sql);
168
212
 
169
213
  switch (ctx.rc) {
@@ -180,7 +224,7 @@ void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
180
224
 
181
225
  #define SQLITE_MULTI_STMT -1
182
226
 
183
- void *prepare_single_stmt_without_gvl(void *ptr) {
227
+ void *prepare_single_stmt_impl(void *ptr) {
184
228
  prepare_stmt_ctx *ctx = (prepare_stmt_ctx *)ptr;
185
229
  const char *rest = NULL;
186
230
  const char *str = ctx->str;
@@ -205,7 +249,7 @@ end:
205
249
 
206
250
  void prepare_single_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
207
251
  prepare_stmt_ctx ctx = {db, stmt, RSTRING_PTR(sql), RSTRING_LEN(sql), 0};
208
- rb_thread_call_without_gvl(prepare_single_stmt_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
252
+ gvl_call(GVL_RELEASE, prepare_single_stmt_impl, (void *)&ctx);
209
253
  RB_GC_GUARD(sql);
210
254
 
211
255
  switch (ctx.rc) {
@@ -227,15 +271,26 @@ struct step_ctx {
227
271
  int rc;
228
272
  };
229
273
 
230
- void *stmt_iterate_without_gvl(void *ptr) {
274
+ void *stmt_iterate_step(void *ptr) {
231
275
  struct step_ctx *ctx = (struct step_ctx *)ptr;
232
276
  ctx->rc = sqlite3_step(ctx->stmt);
233
277
  return NULL;
234
278
  }
235
279
 
280
+ inline enum gvl_mode stepwise_gvl_mode(query_ctx *ctx) {
281
+ // a negative or zero threshold means the GVL is always held during iteration.
282
+ if (ctx->gvl_release_threshold <= 0) return GVL_HOLD;
283
+
284
+ if (!sqlite3_stmt_busy(ctx->stmt)) return GVL_RELEASE;
285
+
286
+ // if positive, the GVL is normally held, and release every <threshold> steps.
287
+ return (ctx->step_count % ctx->gvl_release_threshold) ? GVL_HOLD : GVL_RELEASE;
288
+ }
289
+
236
290
  inline int stmt_iterate(query_ctx *ctx) {
237
291
  struct step_ctx step_ctx = {ctx->stmt, 0};
238
- rb_thread_call_without_gvl(stmt_iterate_without_gvl, (void *)&step_ctx, RUBY_UBF_IO, 0);
292
+ ctx->step_count += 1;
293
+ gvl_call(stepwise_gvl_mode(ctx), stmt_iterate_step, (void *)&step_ctx);
239
294
  switch (step_ctx.rc) {
240
295
  case SQLITE_ROW:
241
296
  return 1;
@@ -379,8 +434,80 @@ VALUE safe_query_single_value(query_ctx *ctx) {
379
434
  return value;
380
435
  }
381
436
 
382
- VALUE safe_execute_multi(query_ctx *ctx) {
437
+ enum batch_mode {
438
+ BATCH_EXECUTE,
439
+ BATCH_QUERY_ARY,
440
+ BATCH_QUERY_HASH,
441
+ BATCH_QUERY_SINGLE_COLUMN
442
+ };
443
+
444
+ static inline VALUE batch_iterate_hash(query_ctx *ctx) {
445
+ VALUE rows = rb_ary_new();
446
+ VALUE row = Qnil;
447
+ int column_count = sqlite3_column_count(ctx->stmt);
448
+ VALUE column_names = get_column_names(ctx->stmt, column_count);
449
+
450
+ while (stmt_iterate(ctx)) {
451
+ row = row_to_hash(ctx->stmt, column_count, column_names);
452
+ rb_ary_push(rows, row);
453
+ }
454
+
455
+ RB_GC_GUARD(column_names);
456
+ RB_GC_GUARD(rows);
457
+ return rows;
458
+ }
459
+
460
+ static inline VALUE batch_iterate_ary(query_ctx *ctx) {
461
+ VALUE rows = rb_ary_new();
462
+ VALUE row = Qnil;
463
+ int column_count = sqlite3_column_count(ctx->stmt);
464
+
465
+ while (stmt_iterate(ctx)) {
466
+ row = row_to_ary(ctx->stmt, column_count);
467
+ rb_ary_push(rows, row);
468
+ }
469
+
470
+ RB_GC_GUARD(rows);
471
+ return rows;
472
+ }
473
+
474
+ static inline VALUE batch_iterate_single_column(query_ctx *ctx) {
475
+ VALUE rows = rb_ary_new();
476
+ VALUE value = Qnil;
477
+ int column_count = sqlite3_column_count(ctx->stmt);
478
+ if (column_count != 1) rb_raise(cError, "Expected query result to have 1 column");
479
+
480
+ while (stmt_iterate(ctx)) {
481
+ value = get_column_value(ctx->stmt, 0, sqlite3_column_type(ctx->stmt, 0));
482
+ rb_ary_push(rows, value);
483
+ }
484
+
485
+ RB_GC_GUARD(rows);
486
+ return rows;
487
+ }
488
+
489
+ static inline void batch_iterate(query_ctx *ctx, enum batch_mode mode, VALUE *rows) {
490
+ switch (mode) {
491
+ case BATCH_EXECUTE:
492
+ while (stmt_iterate(ctx));
493
+ break;
494
+ case BATCH_QUERY_ARY:
495
+ *rows = batch_iterate_ary(ctx);
496
+ break;
497
+ case BATCH_QUERY_HASH:
498
+ *rows = batch_iterate_hash(ctx);
499
+ break;
500
+ case BATCH_QUERY_SINGLE_COLUMN:
501
+ *rows = batch_iterate_single_column(ctx);
502
+ break;
503
+ }
504
+ }
505
+
506
+ static inline VALUE batch_run_array(query_ctx *ctx, enum batch_mode mode) {
383
507
  int count = RARRAY_LEN(ctx->params);
508
+ int block_given = rb_block_given_p();
509
+ VALUE results = (mode != BATCH_EXECUTE) && !block_given ? rb_ary_new() : Qnil;
510
+ VALUE rows = Qnil;
384
511
  int changes = 0;
385
512
 
386
513
  for (int i = 0; i < count; i++) {
@@ -388,11 +515,135 @@ VALUE safe_execute_multi(query_ctx *ctx) {
388
515
  sqlite3_clear_bindings(ctx->stmt);
389
516
  bind_all_parameters_from_object(ctx->stmt, RARRAY_AREF(ctx->params, i));
390
517
 
391
- while (stmt_iterate(ctx));
518
+ batch_iterate(ctx, mode, &rows);
392
519
  changes += sqlite3_changes(ctx->sqlite3_db);
520
+
521
+ if (mode != BATCH_EXECUTE) {
522
+ if (block_given)
523
+ rb_yield(rows);
524
+ else
525
+ rb_ary_push(results, rows);
526
+ }
393
527
  }
394
528
 
395
- return INT2FIX(changes);
529
+ RB_GC_GUARD(rows);
530
+ RB_GC_GUARD(results);
531
+
532
+ if (mode == BATCH_EXECUTE || block_given)
533
+ return INT2FIX(changes);
534
+ else
535
+ return results;
536
+ }
537
+
538
+ struct batch_execute_each_ctx {
539
+ query_ctx *ctx;
540
+ enum batch_mode mode;
541
+ int block_given;
542
+ VALUE results;
543
+ int changes;
544
+ };
545
+
546
+ static VALUE batch_run_each_iter(RB_BLOCK_CALL_FUNC_ARGLIST(yield_value, vctx)) {
547
+ struct batch_execute_each_ctx *each_ctx = (struct batch_execute_each_ctx*)vctx;
548
+ VALUE rows = Qnil;
549
+
550
+ sqlite3_reset(each_ctx->ctx->stmt);
551
+ sqlite3_clear_bindings(each_ctx->ctx->stmt);
552
+ bind_all_parameters_from_object(each_ctx->ctx->stmt, yield_value);
553
+
554
+ batch_iterate(each_ctx->ctx, each_ctx->mode, &rows);
555
+ each_ctx->changes += sqlite3_changes(each_ctx->ctx->sqlite3_db);
556
+
557
+ if (each_ctx->mode != BATCH_EXECUTE) {
558
+ if (each_ctx->block_given)
559
+ rb_yield(rows);
560
+ else
561
+ rb_ary_push(each_ctx->results, rows);
562
+ }
563
+ RB_GC_GUARD(rows);
564
+
565
+ return Qnil;
566
+ }
567
+
568
+ static inline VALUE batch_run_each(query_ctx *ctx, enum batch_mode mode) {
569
+ struct batch_execute_each_ctx each_ctx = {
570
+ .ctx = ctx,
571
+ .mode = mode,
572
+ .block_given = rb_block_given_p(),
573
+ .results = ((mode != BATCH_EXECUTE) && !rb_block_given_p() ? rb_ary_new() : Qnil),
574
+ .changes = 0
575
+ };
576
+ rb_block_call(ctx->params, ID_each, 0, 0, batch_run_each_iter, (VALUE)&each_ctx);
577
+
578
+ if (mode == BATCH_EXECUTE || each_ctx.block_given)
579
+ return INT2FIX(each_ctx.changes);
580
+ else
581
+ return each_ctx.results;
582
+ }
583
+
584
+ static inline VALUE batch_run_proc(query_ctx *ctx, enum batch_mode mode) {
585
+ VALUE params = Qnil;
586
+ int block_given = rb_block_given_p();
587
+ VALUE results = (mode != BATCH_EXECUTE) && !block_given ? rb_ary_new() : Qnil;
588
+ VALUE rows = Qnil;
589
+ int changes = 0;
590
+
591
+ while (1) {
592
+ params = rb_funcall(ctx->params, ID_call, 0);
593
+ if (NIL_P(params)) break;
594
+
595
+ sqlite3_reset(ctx->stmt);
596
+ sqlite3_clear_bindings(ctx->stmt);
597
+ bind_all_parameters_from_object(ctx->stmt, params);
598
+
599
+ batch_iterate(ctx, mode, &rows);
600
+ changes += sqlite3_changes(ctx->sqlite3_db);
601
+
602
+ if (mode != BATCH_EXECUTE) {
603
+ if (block_given)
604
+ rb_yield(rows);
605
+ else
606
+ rb_ary_push(results, rows);
607
+ }
608
+ }
609
+
610
+ RB_GC_GUARD(rows);
611
+ RB_GC_GUARD(results);
612
+ RB_GC_GUARD(params);
613
+
614
+ if (mode == BATCH_EXECUTE || block_given)
615
+ return INT2FIX(changes);
616
+ else
617
+ return results;
618
+ }
619
+
620
+ static inline VALUE batch_run(query_ctx *ctx, enum batch_mode mode) {
621
+ if (TYPE(ctx->params) == T_ARRAY)
622
+ return batch_run_array(ctx, mode);
623
+
624
+ if (rb_respond_to(ctx->params, ID_each))
625
+ return batch_run_each(ctx, mode);
626
+
627
+ if (rb_respond_to(ctx->params, ID_call))
628
+ return batch_run_proc(ctx, mode);
629
+
630
+ rb_raise(cParameterError, "Invalid parameter source supplied to #batch_execute");
631
+ }
632
+
633
+ VALUE safe_batch_execute(query_ctx *ctx) {
634
+ return batch_run(ctx, BATCH_EXECUTE);
635
+ }
636
+
637
+ VALUE safe_batch_query(query_ctx *ctx) {
638
+ return batch_run(ctx, BATCH_QUERY_HASH);
639
+ }
640
+
641
+ VALUE safe_batch_query_ary(query_ctx *ctx) {
642
+ return batch_run(ctx, BATCH_QUERY_ARY);
643
+ }
644
+
645
+ VALUE safe_batch_query_single_column(query_ctx *ctx) {
646
+ return batch_run(ctx, BATCH_QUERY_SINGLE_COLUMN);
396
647
  }
397
648
 
398
649
  VALUE safe_query_columns(query_ctx *ctx) {