extralite-bundle 1.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,347 @@
1
+ #include <stdio.h>
2
+ #include "extralite.h"
3
+
4
+ static inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
5
+ switch (type) {
6
+ case SQLITE_NULL:
7
+ return Qnil;
8
+ case SQLITE_INTEGER:
9
+ return LL2NUM(sqlite3_column_int64(stmt, col));
10
+ case SQLITE_FLOAT:
11
+ return DBL2NUM(sqlite3_column_double(stmt, col));
12
+ case SQLITE_TEXT:
13
+ return rb_str_new_cstr((char *)sqlite3_column_text(stmt, col));
14
+ case SQLITE_BLOB:
15
+ return rb_str_new((const char *)sqlite3_column_blob(stmt, col), (long)sqlite3_column_bytes(stmt, col));
16
+ default:
17
+ rb_raise(cError, "Unknown column type: %d", type);
18
+ }
19
+
20
+ return Qnil;
21
+ }
22
+
23
+ void bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value);
24
+
25
+ void bind_hash_parameter_values(sqlite3_stmt *stmt, VALUE hash) {
26
+ VALUE keys = rb_funcall(hash, ID_KEYS, 0);
27
+ long len = RARRAY_LEN(keys);
28
+ for (long i = 0; i < len; i++) {
29
+ VALUE k = RARRAY_AREF(keys, i);
30
+ VALUE v = rb_hash_aref(hash, k);
31
+
32
+ switch (TYPE(k)) {
33
+ case T_FIXNUM:
34
+ bind_parameter_value(stmt, NUM2INT(k), v);
35
+ break;
36
+ case T_SYMBOL:
37
+ k = rb_funcall(k, ID_TO_S, 0);
38
+ case T_STRING:
39
+ if(RSTRING_PTR(k)[0] != ':') k = rb_str_plus(rb_str_new2(":"), k);
40
+ int pos = sqlite3_bind_parameter_index(stmt, StringValuePtr(k));
41
+ bind_parameter_value(stmt, pos, v);
42
+ break;
43
+ default:
44
+ rb_raise(cError, "Cannot bind hash key value idx %ld", i);
45
+ }
46
+ }
47
+ RB_GC_GUARD(keys);
48
+ }
49
+
50
+ inline void bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value) {
51
+ switch (TYPE(value)) {
52
+ case T_NIL:
53
+ sqlite3_bind_null(stmt, pos);
54
+ return;
55
+ case T_FIXNUM:
56
+ sqlite3_bind_int64(stmt, pos, NUM2LL(value));
57
+ return;
58
+ case T_FLOAT:
59
+ sqlite3_bind_double(stmt, pos, NUM2DBL(value));
60
+ return;
61
+ case T_TRUE:
62
+ sqlite3_bind_int(stmt, pos, 1);
63
+ return;
64
+ case T_FALSE:
65
+ sqlite3_bind_int(stmt, pos, 0);
66
+ return;
67
+ case T_STRING:
68
+ sqlite3_bind_text(stmt, pos, RSTRING_PTR(value), RSTRING_LEN(value), SQLITE_TRANSIENT);
69
+ return;
70
+ case T_HASH:
71
+ bind_hash_parameter_values(stmt, value);
72
+ return;
73
+ default:
74
+ rb_raise(cError, "Cannot bind parameter at position %d", pos);
75
+ }
76
+ }
77
+
78
+ void bind_all_parameters(sqlite3_stmt *stmt, int argc, VALUE *argv) {
79
+ for (int i = 0; i < argc; i++) {
80
+ bind_parameter_value(stmt, i + 1, argv[i]);
81
+ }
82
+ }
83
+
84
+ static inline VALUE get_column_names(sqlite3_stmt *stmt, int column_count) {
85
+ VALUE arr = rb_ary_new2(column_count);
86
+ for (int i = 0; i < column_count; i++) {
87
+ VALUE name = ID2SYM(rb_intern(sqlite3_column_name(stmt, i)));
88
+ rb_ary_push(arr, name);
89
+ }
90
+ return arr;
91
+ }
92
+
93
+ static inline VALUE row_to_hash(sqlite3_stmt *stmt, int column_count, VALUE column_names) {
94
+ VALUE row = rb_hash_new();
95
+ for (int i = 0; i < column_count; i++) {
96
+ VALUE value = get_column_value(stmt, i, sqlite3_column_type(stmt, i));
97
+ rb_hash_aset(row, RARRAY_AREF(column_names, i), value);
98
+ }
99
+ return row;
100
+ }
101
+
102
+ static inline VALUE row_to_ary(sqlite3_stmt *stmt, int column_count) {
103
+ VALUE row = rb_ary_new2(column_count);
104
+ for (int i = 0; i < column_count; i++) {
105
+ VALUE value = get_column_value(stmt, i, sqlite3_column_type(stmt, i));
106
+ rb_ary_push(row, value);
107
+ }
108
+ return row;
109
+ }
110
+
111
+ typedef struct {
112
+ sqlite3 *db;
113
+ sqlite3_stmt **stmt;
114
+ const char *str;
115
+ long len;
116
+ int rc;
117
+ } prepare_stmt_ctx;
118
+
119
+ void *prepare_multi_stmt_without_gvl(void *ptr) {
120
+ prepare_stmt_ctx *ctx = (prepare_stmt_ctx *)ptr;
121
+ const char *rest = NULL;
122
+ const char *str = ctx->str;
123
+ const char *end = ctx->str + ctx->len;
124
+ while (1) {
125
+ ctx->rc = sqlite3_prepare_v2(ctx->db, str, end - str, ctx->stmt, &rest);
126
+ if (ctx->rc) {
127
+ // error
128
+ sqlite3_finalize(*ctx->stmt);
129
+ return NULL;
130
+ }
131
+
132
+ if (rest == end) return NULL;
133
+
134
+ // perform current query, but discard its results
135
+ ctx->rc = sqlite3_step(*ctx->stmt);
136
+ sqlite3_finalize(*ctx->stmt);
137
+ switch (ctx->rc) {
138
+ case SQLITE_BUSY:
139
+ case SQLITE_ERROR:
140
+ case SQLITE_MISUSE:
141
+ return NULL;
142
+ }
143
+ str = rest;
144
+ }
145
+ return NULL;
146
+ }
147
+
148
+ /*
149
+ This function prepares a statement from an SQL string containing one or more SQL
150
+ statements. It will release the GVL while the statements are being prepared and
151
+ executed. All statements excluding the last one are executed. The last statement
152
+ is not executed, but instead handed back to the caller for looping over results.
153
+ */
154
+ void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
155
+ prepare_stmt_ctx ctx = {db, stmt, RSTRING_PTR(sql), RSTRING_LEN(sql), 0};
156
+ rb_thread_call_without_gvl(prepare_multi_stmt_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
157
+ RB_GC_GUARD(sql);
158
+
159
+ switch (ctx.rc) {
160
+ case 0:
161
+ return;
162
+ case SQLITE_BUSY:
163
+ rb_raise(cBusyError, "Database is busy");
164
+ case SQLITE_ERROR:
165
+ rb_raise(cSQLError, "%s", sqlite3_errmsg(db));
166
+ default:
167
+ rb_raise(cError, "Invalid return code for prepare_multi_stmt_without_gvl: %d (please open an issue on https://github.com/digital-fabric/extralite)", ctx.rc);
168
+ }
169
+ }
170
+
171
+ #define SQLITE_MULTI_STMT -1
172
+
173
+ void *prepare_single_stmt_without_gvl(void *ptr) {
174
+ prepare_stmt_ctx *ctx = (prepare_stmt_ctx *)ptr;
175
+ const char *rest = NULL;
176
+ const char *str = ctx->str;
177
+ const char *end = ctx->str + ctx->len;
178
+
179
+ ctx->rc = sqlite3_prepare_v2(ctx->db, str, end - str, ctx->stmt, &rest);
180
+ if (ctx->rc)
181
+ goto discard_stmt;
182
+ else if (rest != end) {
183
+ ctx->rc = SQLITE_MULTI_STMT;
184
+ goto discard_stmt;
185
+ }
186
+ goto end;
187
+ discard_stmt:
188
+ if (*ctx->stmt) {
189
+ sqlite3_finalize(*ctx->stmt);
190
+ *ctx->stmt = NULL;
191
+ }
192
+ end:
193
+ return NULL;
194
+ }
195
+
196
+ void prepare_single_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
197
+ prepare_stmt_ctx ctx = {db, stmt, RSTRING_PTR(sql), RSTRING_LEN(sql), 0};
198
+ rb_thread_call_without_gvl(prepare_single_stmt_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
199
+ RB_GC_GUARD(sql);
200
+
201
+ switch (ctx.rc) {
202
+ case 0:
203
+ return;
204
+ case SQLITE_BUSY:
205
+ rb_raise(cBusyError, "Database is busy");
206
+ case SQLITE_ERROR:
207
+ rb_raise(cSQLError, "%s", sqlite3_errmsg(db));
208
+ case SQLITE_MULTI_STMT:
209
+ rb_raise(cSQLError, "A prepared statement does not accept SQL strings with multiple queries");
210
+ default:
211
+ rb_raise(cError, "Invalid return code for prepare_multi_stmt_without_gvl: %d (please open an issue on https://github.com/digital-fabric/extralite)", ctx.rc);
212
+ }
213
+ }
214
+
215
+ struct step_ctx {
216
+ sqlite3_stmt *stmt;
217
+ int rc;
218
+ };
219
+
220
+ void *stmt_iterate_without_gvl(void *ptr) {
221
+ struct step_ctx *ctx = (struct step_ctx *)ptr;
222
+ ctx->rc = sqlite3_step(ctx->stmt);
223
+ return NULL;
224
+ }
225
+
226
+ static inline int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db) {
227
+ struct step_ctx ctx = {stmt, 0};
228
+ rb_thread_call_without_gvl(stmt_iterate_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
229
+ switch (ctx.rc) {
230
+ case SQLITE_ROW:
231
+ return 1;
232
+ case SQLITE_DONE:
233
+ return 0;
234
+ case SQLITE_BUSY:
235
+ rb_raise(cBusyError, "Database is busy");
236
+ case SQLITE_ERROR:
237
+ rb_raise(cSQLError, "%s", sqlite3_errmsg(db));
238
+ default:
239
+ rb_raise(cError, "Invalid return code for sqlite3_step: %d (please open an issue on https://github.com/digital-fabric/extralite)", ctx.rc);
240
+ }
241
+
242
+ return 0;
243
+ }
244
+
245
+ VALUE cleanup_stmt(query_ctx *ctx) {
246
+ if (ctx->stmt) sqlite3_finalize(ctx->stmt);
247
+ return Qnil;
248
+ }
249
+
250
+ VALUE safe_query_hash(query_ctx *ctx) {
251
+ VALUE result = ctx->self;
252
+ int yield_to_block = rb_block_given_p();
253
+ VALUE row;
254
+ int column_count;
255
+ VALUE column_names;
256
+
257
+ column_count = sqlite3_column_count(ctx->stmt);
258
+ column_names = get_column_names(ctx->stmt, column_count);
259
+
260
+ // block not given, so prepare the array of records to be returned
261
+ if (!yield_to_block) result = rb_ary_new();
262
+
263
+ while (stmt_iterate(ctx->stmt, ctx->sqlite3_db)) {
264
+ row = row_to_hash(ctx->stmt, column_count, column_names);
265
+ if (yield_to_block) rb_yield(row); else rb_ary_push(result, row);
266
+ }
267
+
268
+ RB_GC_GUARD(column_names);
269
+ RB_GC_GUARD(row);
270
+ RB_GC_GUARD(result);
271
+ return result;
272
+ }
273
+
274
+ VALUE safe_query_ary(query_ctx *ctx) {
275
+ int column_count;
276
+ VALUE result = ctx->self;
277
+ int yield_to_block = rb_block_given_p();
278
+ VALUE row;
279
+
280
+ column_count = sqlite3_column_count(ctx->stmt);
281
+
282
+ // block not given, so prepare the array of records to be returned
283
+ if (!yield_to_block) result = rb_ary_new();
284
+
285
+ while (stmt_iterate(ctx->stmt, ctx->sqlite3_db)) {
286
+ row = row_to_ary(ctx->stmt, column_count);
287
+ if (yield_to_block) rb_yield(row); else rb_ary_push(result, row);
288
+ }
289
+
290
+ RB_GC_GUARD(row);
291
+ RB_GC_GUARD(result);
292
+ return result;
293
+ }
294
+
295
+ VALUE safe_query_single_row(query_ctx *ctx) {
296
+ int column_count;
297
+ VALUE row = Qnil;
298
+ VALUE column_names;
299
+
300
+ column_count = sqlite3_column_count(ctx->stmt);
301
+ column_names = get_column_names(ctx->stmt, column_count);
302
+
303
+ if (stmt_iterate(ctx->stmt, ctx->sqlite3_db))
304
+ row = row_to_hash(ctx->stmt, column_count, column_names);
305
+
306
+ RB_GC_GUARD(row);
307
+ RB_GC_GUARD(column_names);
308
+ return row;
309
+ }
310
+
311
+ VALUE safe_query_single_column(query_ctx *ctx) {
312
+ int column_count;
313
+ VALUE result = ctx->self;
314
+ int yield_to_block = rb_block_given_p();
315
+ VALUE value;
316
+
317
+ column_count = sqlite3_column_count(ctx->stmt);
318
+ if (column_count != 1)
319
+ rb_raise(cError, "Expected query result to have 1 column");
320
+
321
+ // block not given, so prepare the array of records to be returned
322
+ if (!yield_to_block) result = rb_ary_new();
323
+
324
+ while (stmt_iterate(ctx->stmt, ctx->sqlite3_db)) {
325
+ value = get_column_value(ctx->stmt, 0, sqlite3_column_type(ctx->stmt, 0));
326
+ if (yield_to_block) rb_yield(value); else rb_ary_push(result, value);
327
+ }
328
+
329
+ RB_GC_GUARD(value);
330
+ RB_GC_GUARD(result);
331
+ return result;
332
+ }
333
+
334
+ VALUE safe_query_single_value(query_ctx *ctx) {
335
+ int column_count;
336
+ VALUE value = Qnil;
337
+
338
+ column_count = sqlite3_column_count(ctx->stmt);
339
+ if (column_count != 1)
340
+ rb_raise(cError, "Expected query result to have 1 column");
341
+
342
+ if (stmt_iterate(ctx->stmt, ctx->sqlite3_db))
343
+ value = get_column_value(ctx->stmt, 0, sqlite3_column_type(ctx->stmt, 0));
344
+
345
+ RB_GC_GUARD(value);
346
+ return value;
347
+ }