sqlite3 1.7.3 → 2.5.0
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 +292 -0
- data/CONTRIBUTING.md +33 -7
- data/FAQ.md +43 -77
- data/INSTALLATION.md +14 -6
- data/LICENSE +18 -22
- data/README.md +97 -9
- data/dependencies.yml +10 -11
- data/ext/sqlite3/aggregator.c +142 -145
- data/ext/sqlite3/aggregator.h +2 -4
- data/ext/sqlite3/backup.c +74 -65
- data/ext/sqlite3/backup.h +2 -2
- data/ext/sqlite3/database.c +621 -493
- data/ext/sqlite3/database.h +13 -4
- data/ext/sqlite3/exception.c +116 -92
- data/ext/sqlite3/exception.h +5 -1
- data/ext/sqlite3/extconf.rb +33 -24
- data/ext/sqlite3/sqlite3.c +176 -115
- data/ext/sqlite3/sqlite3_ruby.h +2 -2
- data/ext/sqlite3/statement.c +553 -300
- data/ext/sqlite3/statement.h +4 -3
- data/ext/sqlite3/timespec.h +20 -0
- data/lib/sqlite3/constants.rb +195 -47
- data/lib/sqlite3/database.rb +223 -187
- data/lib/sqlite3/errors.rb +54 -1
- data/lib/sqlite3/fork_safety.rb +66 -0
- data/lib/sqlite3/pragmas.rb +140 -136
- data/lib/sqlite3/resultset.rb +14 -97
- data/lib/sqlite3/statement.rb +58 -13
- data/lib/sqlite3/value.rb +17 -20
- data/lib/sqlite3/version.rb +2 -21
- data/lib/sqlite3/version_info.rb +17 -0
- data/lib/sqlite3.rb +8 -4
- data/ports/archives/sqlite-autoconf-3470200.tar.gz +0 -0
- metadata +9 -37
- data/API_CHANGES.md +0 -49
- data/ChangeLog.cvs +0 -88
- data/Gemfile +0 -10
- data/LICENSE-DEPENDENCIES +0 -20
- data/lib/sqlite3/translator.rb +0 -117
- data/ports/archives/sqlite-autoconf-3450200.tar.gz +0 -0
- data/test/helper.rb +0 -27
- data/test/test_backup.rb +0 -33
- data/test/test_collation.rb +0 -82
- data/test/test_database.rb +0 -668
- data/test/test_database_flags.rb +0 -95
- data/test/test_database_readonly.rb +0 -36
- data/test/test_database_readwrite.rb +0 -41
- data/test/test_deprecated.rb +0 -49
- data/test/test_encoding.rb +0 -165
- data/test/test_integration.rb +0 -507
- data/test/test_integration_aggregate.rb +0 -336
- data/test/test_integration_open_close.rb +0 -30
- data/test/test_integration_pending.rb +0 -115
- data/test/test_integration_resultset.rb +0 -142
- data/test/test_integration_statement.rb +0 -194
- data/test/test_pragmas.rb +0 -22
- data/test/test_result_set.rb +0 -47
- data/test/test_sqlite3.rb +0 -30
- data/test/test_statement.rb +0 -290
- data/test/test_statement_execute.rb +0 -39
data/ext/sqlite3/database.c
CHANGED
@@ -12,133 +12,224 @@
|
|
12
12
|
|
13
13
|
VALUE cSqlite3Database;
|
14
14
|
|
15
|
-
|
15
|
+
/* See adr/2024-09-fork-safety.md */
|
16
|
+
static void
|
17
|
+
discard_db(sqlite3RubyPtr ctx)
|
18
|
+
{
|
19
|
+
sqlite3_file *sfile;
|
20
|
+
int status;
|
21
|
+
|
22
|
+
// release as much heap memory as possible by deallocating non-essential memory
|
23
|
+
// allocations held by the database library. Memory used to cache database pages to
|
24
|
+
// improve performance is an example of non-essential memory.
|
25
|
+
// on my development machine, this reduces the lost memory from 152k to 69k.
|
26
|
+
sqlite3_db_release_memory(ctx->db);
|
27
|
+
|
28
|
+
// release file descriptors
|
29
|
+
#ifdef HAVE_SQLITE3_DB_NAME
|
30
|
+
const char *db_name;
|
31
|
+
int j_db = 0;
|
32
|
+
while ((db_name = sqlite3_db_name(ctx->db, j_db)) != NULL) {
|
33
|
+
status = sqlite3_file_control(ctx->db, db_name, SQLITE_FCNTL_FILE_POINTER, &sfile);
|
34
|
+
if (status == 0 && sfile->pMethods != NULL) {
|
35
|
+
sfile->pMethods->xClose(sfile);
|
36
|
+
}
|
37
|
+
j_db++;
|
38
|
+
}
|
39
|
+
#else
|
40
|
+
status = sqlite3_file_control(ctx->db, NULL, SQLITE_FCNTL_FILE_POINTER, &sfile);
|
41
|
+
if (status == 0 && sfile->pMethods != NULL) {
|
42
|
+
sfile->pMethods->xClose(sfile);
|
43
|
+
}
|
44
|
+
#endif
|
45
|
+
|
46
|
+
status = sqlite3_file_control(ctx->db, NULL, SQLITE_FCNTL_JOURNAL_POINTER, &sfile);
|
47
|
+
if (status == 0 && sfile->pMethods != NULL) {
|
48
|
+
sfile->pMethods->xClose(sfile);
|
49
|
+
}
|
50
|
+
|
51
|
+
ctx->db = NULL;
|
52
|
+
ctx->flags |= SQLITE3_RB_DATABASE_DISCARDED;
|
53
|
+
}
|
54
|
+
|
55
|
+
static void
|
56
|
+
close_or_discard_db(sqlite3RubyPtr ctx)
|
16
57
|
{
|
17
|
-
|
18
|
-
|
58
|
+
if (ctx->db) {
|
59
|
+
int is_readonly = (ctx->flags & SQLITE3_RB_DATABASE_READONLY);
|
19
60
|
|
20
|
-
|
21
|
-
|
61
|
+
if (is_readonly || ctx->owner == getpid()) {
|
62
|
+
// Ordinary close.
|
63
|
+
sqlite3_close_v2(ctx->db);
|
64
|
+
ctx->db = NULL;
|
65
|
+
} else {
|
66
|
+
// This is an open connection carried across a fork(). "Discard" it.
|
67
|
+
discard_db(ctx);
|
68
|
+
}
|
69
|
+
}
|
22
70
|
}
|
23
71
|
|
24
|
-
|
72
|
+
|
73
|
+
static void
|
74
|
+
database_mark(void *ctx)
|
25
75
|
{
|
26
|
-
|
27
|
-
|
28
|
-
|
76
|
+
sqlite3RubyPtr c = (sqlite3RubyPtr)ctx;
|
77
|
+
rb_gc_mark(c->busy_handler);
|
78
|
+
}
|
79
|
+
|
80
|
+
static void
|
81
|
+
deallocate(void *ctx)
|
82
|
+
{
|
83
|
+
close_or_discard_db((sqlite3RubyPtr)ctx);
|
84
|
+
xfree(ctx);
|
85
|
+
}
|
86
|
+
|
87
|
+
static size_t
|
88
|
+
database_memsize(const void *ctx)
|
89
|
+
{
|
90
|
+
const sqlite3RubyPtr c = (const sqlite3RubyPtr)ctx;
|
91
|
+
// NB: can't account for ctx->db because the type is incomplete.
|
92
|
+
return sizeof(*c);
|
29
93
|
}
|
30
94
|
|
31
95
|
static const rb_data_type_t database_type = {
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
0,
|
40
|
-
RUBY_TYPED_WB_PROTECTED, // Not freed immediately because the dfree function do IOs.
|
96
|
+
.wrap_struct_name = "SQLite3::Backup",
|
97
|
+
.function = {
|
98
|
+
.dmark = database_mark,
|
99
|
+
.dfree = deallocate,
|
100
|
+
.dsize = database_memsize,
|
101
|
+
},
|
102
|
+
.flags = RUBY_TYPED_WB_PROTECTED, // Not freed immediately because the dfree function do IOs.
|
41
103
|
};
|
42
104
|
|
43
|
-
static VALUE
|
105
|
+
static VALUE
|
106
|
+
allocate(VALUE klass)
|
44
107
|
{
|
45
|
-
|
46
|
-
|
108
|
+
sqlite3RubyPtr ctx;
|
109
|
+
VALUE object = TypedData_Make_Struct(klass, sqlite3Ruby, &database_type, ctx);
|
110
|
+
ctx->owner = getpid();
|
111
|
+
return object;
|
47
112
|
}
|
48
113
|
|
49
114
|
static char *
|
50
115
|
utf16_string_value_ptr(VALUE str)
|
51
116
|
{
|
52
|
-
|
53
|
-
|
54
|
-
|
117
|
+
StringValue(str);
|
118
|
+
rb_str_buf_cat(str, "\x00\x00", 2L);
|
119
|
+
return RSTRING_PTR(str);
|
55
120
|
}
|
56
121
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
122
|
+
sqlite3RubyPtr
|
123
|
+
sqlite3_database_unwrap(VALUE database)
|
124
|
+
{
|
125
|
+
sqlite3RubyPtr ctx;
|
126
|
+
TypedData_Get_Struct(database, sqlite3Ruby, &database_type, ctx);
|
127
|
+
return ctx;
|
63
128
|
}
|
64
129
|
|
65
|
-
static VALUE
|
130
|
+
static VALUE
|
131
|
+
rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs)
|
66
132
|
{
|
67
|
-
|
68
|
-
|
133
|
+
sqlite3RubyPtr ctx;
|
134
|
+
int status;
|
135
|
+
int flags;
|
69
136
|
|
70
|
-
|
137
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
71
138
|
|
72
139
|
#if defined TAINTING_SUPPORT
|
73
140
|
# if defined StringValueCStr
|
74
|
-
|
75
|
-
|
141
|
+
StringValuePtr(file);
|
142
|
+
rb_check_safe_obj(file);
|
76
143
|
# else
|
77
|
-
|
144
|
+
Check_SafeStr(file);
|
78
145
|
# endif
|
79
146
|
#endif
|
80
147
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
148
|
+
flags = NUM2INT(mode);
|
149
|
+
status = sqlite3_open_v2(
|
150
|
+
StringValuePtr(file),
|
151
|
+
&ctx->db,
|
152
|
+
flags,
|
153
|
+
NIL_P(zvfs) ? NULL : StringValuePtr(zvfs)
|
154
|
+
);
|
155
|
+
|
156
|
+
CHECK(ctx->db, status);
|
157
|
+
if (flags & SQLITE_OPEN_READONLY) {
|
158
|
+
ctx->flags |= SQLITE3_RB_DATABASE_READONLY;
|
159
|
+
}
|
89
160
|
|
90
|
-
|
161
|
+
return self;
|
91
162
|
}
|
92
163
|
|
93
|
-
static VALUE
|
164
|
+
static VALUE
|
165
|
+
rb_sqlite3_disable_quirk_mode(VALUE self)
|
94
166
|
{
|
95
167
|
#if defined SQLITE_DBCONFIG_DQS_DDL
|
96
|
-
|
97
|
-
|
168
|
+
sqlite3RubyPtr ctx;
|
169
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
98
170
|
|
99
|
-
|
171
|
+
if (!ctx->db) { return Qfalse; }
|
100
172
|
|
101
|
-
|
102
|
-
|
173
|
+
sqlite3_db_config(ctx->db, SQLITE_DBCONFIG_DQS_DDL, 0, (void *)0);
|
174
|
+
sqlite3_db_config(ctx->db, SQLITE_DBCONFIG_DQS_DML, 0, (void *)0);
|
103
175
|
|
104
|
-
|
176
|
+
return Qtrue;
|
105
177
|
#else
|
106
|
-
|
178
|
+
return Qfalse;
|
107
179
|
#endif
|
108
180
|
}
|
109
181
|
|
110
|
-
/*
|
182
|
+
/*
|
183
|
+
* Close the database and release all associated resources.
|
184
|
+
*
|
185
|
+
* ⚠ Writable connections that are carried across a <tt>fork()</tt> are not completely
|
186
|
+
* closed. {Sqlite does not support forking}[https://www.sqlite.org/howtocorrupt.html],
|
187
|
+
* and fully closing a writable connection that has been carried across a fork may corrupt the
|
188
|
+
* database. Since it is an incomplete close, not all memory resources are freed, but this is safer
|
189
|
+
* than risking data loss.
|
111
190
|
*
|
112
|
-
*
|
191
|
+
* See rdoc-ref:adr/2024-09-fork-safety.md for more information on fork safety.
|
113
192
|
*/
|
114
|
-
static VALUE
|
193
|
+
static VALUE
|
194
|
+
sqlite3_rb_close(VALUE self)
|
115
195
|
{
|
116
|
-
|
117
|
-
|
118
|
-
|
196
|
+
sqlite3RubyPtr ctx;
|
197
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
198
|
+
|
199
|
+
close_or_discard_db(ctx);
|
200
|
+
|
201
|
+
rb_iv_set(self, "-aggregators", Qnil);
|
202
|
+
|
203
|
+
return self;
|
204
|
+
}
|
119
205
|
|
120
|
-
|
121
|
-
|
206
|
+
/* private method, primarily for testing */
|
207
|
+
static VALUE
|
208
|
+
sqlite3_rb_discard(VALUE self)
|
209
|
+
{
|
210
|
+
sqlite3RubyPtr ctx;
|
211
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
122
212
|
|
123
|
-
|
213
|
+
discard_db(ctx);
|
124
214
|
|
125
|
-
|
215
|
+
rb_iv_set(self, "-aggregators", Qnil);
|
126
216
|
|
127
|
-
|
217
|
+
return self;
|
128
218
|
}
|
129
219
|
|
130
220
|
/* call-seq: db.closed?
|
131
221
|
*
|
132
222
|
* Returns +true+ if this database instance has been closed (see #close).
|
133
223
|
*/
|
134
|
-
static VALUE
|
224
|
+
static VALUE
|
225
|
+
closed_p(VALUE self)
|
135
226
|
{
|
136
|
-
|
137
|
-
|
227
|
+
sqlite3RubyPtr ctx;
|
228
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
138
229
|
|
139
|
-
|
230
|
+
if (!ctx->db) { return Qtrue; }
|
140
231
|
|
141
|
-
|
232
|
+
return Qfalse;
|
142
233
|
}
|
143
234
|
|
144
235
|
/* call-seq: total_changes
|
@@ -146,20 +237,22 @@ static VALUE closed_p(VALUE self)
|
|
146
237
|
* Returns the total number of changes made to this database instance
|
147
238
|
* since it was opened.
|
148
239
|
*/
|
149
|
-
static VALUE
|
240
|
+
static VALUE
|
241
|
+
total_changes(VALUE self)
|
150
242
|
{
|
151
|
-
|
152
|
-
|
153
|
-
|
243
|
+
sqlite3RubyPtr ctx;
|
244
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
245
|
+
REQUIRE_OPEN_DB(ctx);
|
154
246
|
|
155
|
-
|
247
|
+
return INT2NUM(sqlite3_total_changes(ctx->db));
|
156
248
|
}
|
157
249
|
|
158
|
-
static void
|
250
|
+
static void
|
251
|
+
tracefunc(void *data, const char *sql)
|
159
252
|
{
|
160
|
-
|
161
|
-
|
162
|
-
|
253
|
+
VALUE self = (VALUE)data;
|
254
|
+
VALUE thing = rb_iv_get(self, "@tracefunc");
|
255
|
+
rb_funcall(thing, rb_intern("call"), 1, rb_str_new2(sql));
|
163
256
|
}
|
164
257
|
|
165
258
|
/* call-seq:
|
@@ -170,34 +263,37 @@ static void tracefunc(void * data, const char *sql)
|
|
170
263
|
* statement executed. The block receives one parameter: the SQL statement
|
171
264
|
* executed. If the block is +nil+, any existing tracer will be uninstalled.
|
172
265
|
*/
|
173
|
-
static VALUE
|
266
|
+
static VALUE
|
267
|
+
trace(int argc, VALUE *argv, VALUE self)
|
174
268
|
{
|
175
|
-
|
176
|
-
|
269
|
+
sqlite3RubyPtr ctx;
|
270
|
+
VALUE block;
|
177
271
|
|
178
|
-
|
179
|
-
|
272
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
273
|
+
REQUIRE_OPEN_DB(ctx);
|
180
274
|
|
181
|
-
|
275
|
+
rb_scan_args(argc, argv, "01", &block);
|
182
276
|
|
183
|
-
|
277
|
+
if (NIL_P(block) && rb_block_given_p()) { block = rb_block_proc(); }
|
184
278
|
|
185
|
-
|
279
|
+
rb_iv_set(self, "@tracefunc", block);
|
186
280
|
|
187
|
-
|
281
|
+
sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)self);
|
188
282
|
|
189
|
-
|
283
|
+
return self;
|
190
284
|
}
|
191
285
|
|
192
|
-
static int
|
286
|
+
static int
|
287
|
+
rb_sqlite3_busy_handler(void *context, int count)
|
193
288
|
{
|
194
|
-
|
195
|
-
|
196
|
-
|
289
|
+
sqlite3RubyPtr ctx = (sqlite3RubyPtr)context;
|
290
|
+
|
291
|
+
VALUE handle = ctx->busy_handler;
|
292
|
+
VALUE result = rb_funcall(handle, rb_intern("call"), 1, INT2NUM(count));
|
197
293
|
|
198
|
-
|
294
|
+
if (Qfalse == result) { return 0; }
|
199
295
|
|
200
|
-
|
296
|
+
return 1;
|
201
297
|
}
|
202
298
|
|
203
299
|
/* call-seq:
|
@@ -214,27 +310,68 @@ static int rb_sqlite3_busy_handler(void * ctx, int count)
|
|
214
310
|
*
|
215
311
|
* See also the mutually exclusive #busy_timeout.
|
216
312
|
*/
|
217
|
-
static VALUE
|
313
|
+
static VALUE
|
314
|
+
busy_handler(int argc, VALUE *argv, VALUE self)
|
218
315
|
{
|
219
|
-
|
220
|
-
|
221
|
-
|
316
|
+
sqlite3RubyPtr ctx;
|
317
|
+
VALUE block;
|
318
|
+
int status;
|
319
|
+
|
320
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
321
|
+
REQUIRE_OPEN_DB(ctx);
|
322
|
+
|
323
|
+
rb_scan_args(argc, argv, "01", &block);
|
222
324
|
|
223
|
-
|
224
|
-
|
325
|
+
if (NIL_P(block) && rb_block_given_p()) { block = rb_block_proc(); }
|
326
|
+
RB_OBJ_WRITE(self, &ctx->busy_handler, block);
|
225
327
|
|
226
|
-
|
328
|
+
status = sqlite3_busy_handler(
|
329
|
+
ctx->db,
|
330
|
+
NIL_P(block) ? NULL : rb_sqlite3_busy_handler,
|
331
|
+
(void *)ctx
|
332
|
+
);
|
227
333
|
|
228
|
-
|
334
|
+
CHECK(ctx->db, status);
|
229
335
|
|
230
|
-
|
336
|
+
return self;
|
337
|
+
}
|
231
338
|
|
232
|
-
|
233
|
-
|
339
|
+
static int
|
340
|
+
rb_sqlite3_statement_timeout(void *context)
|
341
|
+
{
|
342
|
+
sqlite3RubyPtr ctx = (sqlite3RubyPtr)context;
|
343
|
+
struct timespec currentTime;
|
344
|
+
clock_gettime(CLOCK_MONOTONIC, ¤tTime);
|
234
345
|
|
235
|
-
|
346
|
+
if (!timespecisset(&ctx->stmt_deadline)) {
|
347
|
+
// Set stmt_deadline if not already set
|
348
|
+
ctx->stmt_deadline = currentTime;
|
349
|
+
} else if (timespecafter(¤tTime, &ctx->stmt_deadline)) {
|
350
|
+
return 1;
|
351
|
+
}
|
236
352
|
|
237
|
-
|
353
|
+
return 0;
|
354
|
+
}
|
355
|
+
|
356
|
+
/* call-seq: db.statement_timeout = ms
|
357
|
+
*
|
358
|
+
* Indicates that if a query lasts longer than the indicated number of
|
359
|
+
* milliseconds, SQLite should interrupt that query and return an error.
|
360
|
+
* By default, SQLite does not interrupt queries. To restore the default
|
361
|
+
* behavior, send 0 as the +ms+ parameter.
|
362
|
+
*/
|
363
|
+
static VALUE
|
364
|
+
set_statement_timeout(VALUE self, VALUE milliseconds)
|
365
|
+
{
|
366
|
+
sqlite3RubyPtr ctx;
|
367
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
368
|
+
|
369
|
+
ctx->stmt_timeout = NUM2INT(milliseconds);
|
370
|
+
int n = NUM2INT(milliseconds) == 0 ? -1 : 1000;
|
371
|
+
|
372
|
+
sqlite3_progress_handler(ctx->db, n, rb_sqlite3_statement_timeout, (void *)ctx);
|
373
|
+
|
374
|
+
return self;
|
238
375
|
}
|
239
376
|
|
240
377
|
/* call-seq: last_insert_row_id
|
@@ -242,115 +379,122 @@ static VALUE busy_handler(int argc, VALUE *argv, VALUE self)
|
|
242
379
|
* Obtains the unique row ID of the last row to be inserted by this Database
|
243
380
|
* instance.
|
244
381
|
*/
|
245
|
-
static VALUE
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
382
|
+
static VALUE
|
383
|
+
last_insert_row_id(VALUE self)
|
384
|
+
{
|
385
|
+
sqlite3RubyPtr ctx;
|
386
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
387
|
+
REQUIRE_OPEN_DB(ctx);
|
388
|
+
|
389
|
+
return LL2NUM(sqlite3_last_insert_rowid(ctx->db));
|
390
|
+
}
|
391
|
+
|
392
|
+
VALUE
|
393
|
+
sqlite3val2rb(sqlite3_value *val)
|
394
|
+
{
|
395
|
+
VALUE rb_val;
|
396
|
+
|
397
|
+
switch (sqlite3_value_type(val)) {
|
398
|
+
case SQLITE_INTEGER:
|
399
|
+
rb_val = LL2NUM(sqlite3_value_int64(val));
|
400
|
+
break;
|
401
|
+
case SQLITE_FLOAT:
|
402
|
+
rb_val = rb_float_new(sqlite3_value_double(val));
|
403
|
+
break;
|
404
|
+
case SQLITE_TEXT: {
|
405
|
+
rb_val = rb_utf8_str_new_cstr((const char *)sqlite3_value_text(val));
|
406
|
+
rb_obj_freeze(rb_val);
|
407
|
+
break;
|
408
|
+
}
|
409
|
+
case SQLITE_BLOB: {
|
410
|
+
int len = sqlite3_value_bytes(val);
|
411
|
+
rb_val = rb_str_new((const char *)sqlite3_value_blob(val), len);
|
412
|
+
rb_obj_freeze(rb_val);
|
413
|
+
break;
|
414
|
+
}
|
415
|
+
case SQLITE_NULL:
|
416
|
+
rb_val = Qnil;
|
417
|
+
break;
|
418
|
+
default:
|
419
|
+
rb_raise(rb_eRuntimeError, "bad type");
|
275
420
|
}
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
sqlite3_result_int64(ctx, (sqlite3_int64)FIX2LONG(result));
|
292
|
-
break;
|
293
|
-
case T_BIGNUM: {
|
421
|
+
|
422
|
+
return rb_val;
|
423
|
+
}
|
424
|
+
|
425
|
+
void
|
426
|
+
set_sqlite3_func_result(sqlite3_context *ctx, VALUE result)
|
427
|
+
{
|
428
|
+
switch (TYPE(result)) {
|
429
|
+
case T_NIL:
|
430
|
+
sqlite3_result_null(ctx);
|
431
|
+
break;
|
432
|
+
case T_FIXNUM:
|
433
|
+
sqlite3_result_int64(ctx, (sqlite3_int64)FIX2LONG(result));
|
434
|
+
break;
|
435
|
+
case T_BIGNUM: {
|
294
436
|
#if SIZEOF_LONG < 8
|
295
|
-
|
437
|
+
sqlite3_int64 num64;
|
296
438
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
439
|
+
if (bignum_to_int64(result, &num64)) {
|
440
|
+
sqlite3_result_int64(ctx, num64);
|
441
|
+
break;
|
442
|
+
}
|
301
443
|
#endif
|
444
|
+
}
|
445
|
+
case T_FLOAT:
|
446
|
+
sqlite3_result_double(ctx, NUM2DBL(result));
|
447
|
+
break;
|
448
|
+
case T_STRING:
|
449
|
+
if (CLASS_OF(result) == cSqlite3Blob
|
450
|
+
|| rb_enc_get_index(result) == rb_ascii8bit_encindex()
|
451
|
+
) {
|
452
|
+
sqlite3_result_blob(
|
453
|
+
ctx,
|
454
|
+
(const void *)StringValuePtr(result),
|
455
|
+
(int)RSTRING_LEN(result),
|
456
|
+
SQLITE_TRANSIENT
|
457
|
+
);
|
458
|
+
} else {
|
459
|
+
sqlite3_result_text(
|
460
|
+
ctx,
|
461
|
+
(const char *)StringValuePtr(result),
|
462
|
+
(int)RSTRING_LEN(result),
|
463
|
+
SQLITE_TRANSIENT
|
464
|
+
);
|
465
|
+
}
|
466
|
+
break;
|
467
|
+
default:
|
468
|
+
rb_raise(rb_eRuntimeError, "can't return %s",
|
469
|
+
rb_class2name(CLASS_OF(result)));
|
302
470
|
}
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
ctx,
|
319
|
-
(const char *)StringValuePtr(result),
|
320
|
-
(int)RSTRING_LEN(result),
|
321
|
-
SQLITE_TRANSIENT
|
322
|
-
);
|
323
|
-
}
|
324
|
-
break;
|
325
|
-
default:
|
326
|
-
rb_raise(rb_eRuntimeError, "can't return %s",
|
327
|
-
rb_class2name(CLASS_OF(result)));
|
328
|
-
}
|
329
|
-
}
|
330
|
-
|
331
|
-
static void rb_sqlite3_func(sqlite3_context * ctx, int argc, sqlite3_value **argv)
|
332
|
-
{
|
333
|
-
VALUE callable = (VALUE)sqlite3_user_data(ctx);
|
334
|
-
VALUE params = rb_ary_new2(argc);
|
335
|
-
VALUE result;
|
336
|
-
int i;
|
337
|
-
|
338
|
-
if (argc > 0) {
|
339
|
-
for(i = 0; i < argc; i++) {
|
340
|
-
VALUE param = sqlite3val2rb(argv[i]);
|
341
|
-
rb_ary_push(params, param);
|
471
|
+
}
|
472
|
+
|
473
|
+
static void
|
474
|
+
rb_sqlite3_func(sqlite3_context *ctx, int argc, sqlite3_value **argv)
|
475
|
+
{
|
476
|
+
VALUE callable = (VALUE)sqlite3_user_data(ctx);
|
477
|
+
VALUE params = rb_ary_new2(argc);
|
478
|
+
VALUE result;
|
479
|
+
int i;
|
480
|
+
|
481
|
+
if (argc > 0) {
|
482
|
+
for (i = 0; i < argc; i++) {
|
483
|
+
VALUE param = sqlite3val2rb(argv[i]);
|
484
|
+
rb_ary_push(params, param);
|
485
|
+
}
|
342
486
|
}
|
343
|
-
}
|
344
487
|
|
345
|
-
|
488
|
+
result = rb_apply(callable, rb_intern("call"), params);
|
346
489
|
|
347
|
-
|
490
|
+
set_sqlite3_func_result(ctx, result);
|
348
491
|
}
|
349
492
|
|
350
493
|
#ifndef HAVE_RB_PROC_ARITY
|
351
|
-
int
|
494
|
+
int
|
495
|
+
rb_proc_arity(VALUE self)
|
352
496
|
{
|
353
|
-
|
497
|
+
return (int)NUM2INT(rb_funcall(self, rb_intern("arity"), 0));
|
354
498
|
}
|
355
499
|
#endif
|
356
500
|
|
@@ -359,33 +503,34 @@ int rb_proc_arity(VALUE self)
|
|
359
503
|
* Define a function named +name+ with +args+ using TextRep bitflags +flags+. The arity of the block
|
360
504
|
* will be used as the arity for the function defined.
|
361
505
|
*/
|
362
|
-
static VALUE
|
506
|
+
static VALUE
|
507
|
+
define_function_with_flags(VALUE self, VALUE name, VALUE flags)
|
363
508
|
{
|
364
|
-
|
365
|
-
|
366
|
-
|
509
|
+
sqlite3RubyPtr ctx;
|
510
|
+
VALUE block;
|
511
|
+
int status;
|
367
512
|
|
368
|
-
|
369
|
-
|
513
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
514
|
+
REQUIRE_OPEN_DB(ctx);
|
370
515
|
|
371
|
-
|
516
|
+
block = rb_block_proc();
|
372
517
|
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
518
|
+
status = sqlite3_create_function(
|
519
|
+
ctx->db,
|
520
|
+
StringValuePtr(name),
|
521
|
+
rb_proc_arity(block),
|
522
|
+
NUM2INT(flags),
|
523
|
+
(void *)block,
|
524
|
+
rb_sqlite3_func,
|
525
|
+
NULL,
|
526
|
+
NULL
|
527
|
+
);
|
383
528
|
|
384
|
-
|
529
|
+
CHECK(ctx->db, status);
|
385
530
|
|
386
|
-
|
531
|
+
rb_hash_aset(rb_iv_get(self, "@functions"), name, block);
|
387
532
|
|
388
|
-
|
533
|
+
return self;
|
389
534
|
}
|
390
535
|
|
391
536
|
/* call-seq: define_function(name) { |args,...| }
|
@@ -393,24 +538,26 @@ static VALUE define_function_with_flags(VALUE self, VALUE name, VALUE flags)
|
|
393
538
|
* Define a function named +name+ with +args+. The arity of the block
|
394
539
|
* will be used as the arity for the function defined.
|
395
540
|
*/
|
396
|
-
static VALUE
|
541
|
+
static VALUE
|
542
|
+
define_function(VALUE self, VALUE name)
|
397
543
|
{
|
398
|
-
|
544
|
+
return define_function_with_flags(self, name, INT2FIX(SQLITE_UTF8));
|
399
545
|
}
|
400
546
|
|
401
547
|
/* call-seq: interrupt
|
402
548
|
*
|
403
549
|
* Interrupts the currently executing operation, causing it to abort.
|
404
550
|
*/
|
405
|
-
static VALUE
|
551
|
+
static VALUE
|
552
|
+
interrupt(VALUE self)
|
406
553
|
{
|
407
|
-
|
408
|
-
|
409
|
-
|
554
|
+
sqlite3RubyPtr ctx;
|
555
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
556
|
+
REQUIRE_OPEN_DB(ctx);
|
410
557
|
|
411
|
-
|
558
|
+
sqlite3_interrupt(ctx->db);
|
412
559
|
|
413
|
-
|
560
|
+
return self;
|
414
561
|
}
|
415
562
|
|
416
563
|
/* call-seq: errmsg
|
@@ -418,13 +565,14 @@ static VALUE interrupt(VALUE self)
|
|
418
565
|
* Return a string describing the last error to have occurred with this
|
419
566
|
* database.
|
420
567
|
*/
|
421
|
-
static VALUE
|
568
|
+
static VALUE
|
569
|
+
errmsg(VALUE self)
|
422
570
|
{
|
423
|
-
|
424
|
-
|
425
|
-
|
571
|
+
sqlite3RubyPtr ctx;
|
572
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
573
|
+
REQUIRE_OPEN_DB(ctx);
|
426
574
|
|
427
|
-
|
575
|
+
return rb_str_new2(sqlite3_errmsg(ctx->db));
|
428
576
|
}
|
429
577
|
|
430
578
|
/* call-seq: errcode
|
@@ -432,13 +580,14 @@ static VALUE errmsg(VALUE self)
|
|
432
580
|
* Return an integer representing the last error to have occurred with this
|
433
581
|
* database.
|
434
582
|
*/
|
435
|
-
static VALUE
|
583
|
+
static VALUE
|
584
|
+
errcode_(VALUE self)
|
436
585
|
{
|
437
|
-
|
438
|
-
|
439
|
-
|
586
|
+
sqlite3RubyPtr ctx;
|
587
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
588
|
+
REQUIRE_OPEN_DB(ctx);
|
440
589
|
|
441
|
-
|
590
|
+
return INT2NUM(sqlite3_errcode(ctx->db));
|
442
591
|
}
|
443
592
|
|
444
593
|
/* call-seq: complete?(sql)
|
@@ -446,12 +595,14 @@ static VALUE errcode_(VALUE self)
|
|
446
595
|
* Return +true+ if the string is a valid (ie, parsable) SQL statement, and
|
447
596
|
* +false+ otherwise.
|
448
597
|
*/
|
449
|
-
static VALUE
|
598
|
+
static VALUE
|
599
|
+
complete_p(VALUE UNUSED(self), VALUE sql)
|
450
600
|
{
|
451
|
-
|
452
|
-
|
601
|
+
if (sqlite3_complete(StringValuePtr(sql))) {
|
602
|
+
return Qtrue;
|
603
|
+
}
|
453
604
|
|
454
|
-
|
605
|
+
return Qfalse;
|
455
606
|
}
|
456
607
|
|
457
608
|
/* call-seq: changes
|
@@ -460,37 +611,39 @@ static VALUE complete_p(VALUE UNUSED(self), VALUE sql)
|
|
460
611
|
* operation performed. Note that a "delete from table" without a where
|
461
612
|
* clause will not affect this value.
|
462
613
|
*/
|
463
|
-
static VALUE
|
614
|
+
static VALUE
|
615
|
+
changes(VALUE self)
|
464
616
|
{
|
465
|
-
|
466
|
-
|
467
|
-
|
617
|
+
sqlite3RubyPtr ctx;
|
618
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
619
|
+
REQUIRE_OPEN_DB(ctx);
|
468
620
|
|
469
|
-
|
621
|
+
return INT2NUM(sqlite3_changes(ctx->db));
|
470
622
|
}
|
471
623
|
|
472
|
-
static int
|
624
|
+
static int
|
625
|
+
rb_sqlite3_auth(
|
473
626
|
void *ctx,
|
474
627
|
int _action,
|
475
|
-
const char *
|
476
|
-
const char *
|
477
|
-
const char *
|
478
|
-
const char *
|
628
|
+
const char *_a,
|
629
|
+
const char *_b,
|
630
|
+
const char *_c,
|
631
|
+
const char *_d)
|
479
632
|
{
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
633
|
+
VALUE self = (VALUE)ctx;
|
634
|
+
VALUE action = INT2NUM(_action);
|
635
|
+
VALUE a = _a ? rb_str_new2(_a) : Qnil;
|
636
|
+
VALUE b = _b ? rb_str_new2(_b) : Qnil;
|
637
|
+
VALUE c = _c ? rb_str_new2(_c) : Qnil;
|
638
|
+
VALUE d = _d ? rb_str_new2(_d) : Qnil;
|
639
|
+
VALUE callback = rb_iv_get(self, "@authorizer");
|
640
|
+
VALUE result = rb_funcall(callback, rb_intern("call"), 5, action, a, b, c, d);
|
488
641
|
|
489
|
-
|
490
|
-
|
491
|
-
|
642
|
+
if (T_FIXNUM == TYPE(result)) { return (int)NUM2INT(result); }
|
643
|
+
if (Qtrue == result) { return SQLITE_OK; }
|
644
|
+
if (Qfalse == result) { return SQLITE_DENY; }
|
492
645
|
|
493
|
-
|
646
|
+
return SQLITE_IGNORE;
|
494
647
|
}
|
495
648
|
|
496
649
|
/* call-seq: set_authorizer = auth
|
@@ -503,23 +656,24 @@ static int rb_sqlite3_auth(
|
|
503
656
|
* is allowed to proceed. Returning 1 or false causes an authorization error to
|
504
657
|
* occur, and returning 2 or nil causes the access to be silently denied.
|
505
658
|
*/
|
506
|
-
static VALUE
|
659
|
+
static VALUE
|
660
|
+
set_authorizer(VALUE self, VALUE authorizer)
|
507
661
|
{
|
508
|
-
|
509
|
-
|
662
|
+
sqlite3RubyPtr ctx;
|
663
|
+
int status;
|
510
664
|
|
511
|
-
|
512
|
-
|
665
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
666
|
+
REQUIRE_OPEN_DB(ctx);
|
513
667
|
|
514
|
-
|
515
|
-
|
516
|
-
|
668
|
+
status = sqlite3_set_authorizer(
|
669
|
+
ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)self
|
670
|
+
);
|
517
671
|
|
518
|
-
|
672
|
+
CHECK(ctx->db, status);
|
519
673
|
|
520
|
-
|
674
|
+
rb_iv_set(self, "@authorizer", authorizer);
|
521
675
|
|
522
|
-
|
676
|
+
return self;
|
523
677
|
}
|
524
678
|
|
525
679
|
/* call-seq: db.busy_timeout = ms
|
@@ -532,15 +686,16 @@ static VALUE set_authorizer(VALUE self, VALUE authorizer)
|
|
532
686
|
*
|
533
687
|
* See also the mutually exclusive #busy_handler.
|
534
688
|
*/
|
535
|
-
static VALUE
|
689
|
+
static VALUE
|
690
|
+
set_busy_timeout(VALUE self, VALUE timeout)
|
536
691
|
{
|
537
|
-
|
538
|
-
|
539
|
-
|
692
|
+
sqlite3RubyPtr ctx;
|
693
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
694
|
+
REQUIRE_OPEN_DB(ctx);
|
540
695
|
|
541
|
-
|
696
|
+
CHECK(ctx->db, sqlite3_busy_timeout(ctx->db, (int)NUM2INT(timeout)));
|
542
697
|
|
543
|
-
|
698
|
+
return self;
|
544
699
|
}
|
545
700
|
|
546
701
|
/* call-seq: db.extended_result_codes = true
|
@@ -548,42 +703,44 @@ static VALUE set_busy_timeout(VALUE self, VALUE timeout)
|
|
548
703
|
* Enable extended result codes in SQLite. These result codes allow for more
|
549
704
|
* detailed exception reporting, such a which type of constraint is violated.
|
550
705
|
*/
|
551
|
-
static VALUE
|
706
|
+
static VALUE
|
707
|
+
set_extended_result_codes(VALUE self, VALUE enable)
|
552
708
|
{
|
553
|
-
|
554
|
-
|
555
|
-
|
709
|
+
sqlite3RubyPtr ctx;
|
710
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
711
|
+
REQUIRE_OPEN_DB(ctx);
|
556
712
|
|
557
|
-
|
713
|
+
CHECK(ctx->db, sqlite3_extended_result_codes(ctx->db, RTEST(enable) ? 1 : 0));
|
558
714
|
|
559
|
-
|
715
|
+
return self;
|
560
716
|
}
|
561
717
|
|
562
|
-
int
|
718
|
+
int
|
719
|
+
rb_comparator_func(void *ctx, int a_len, const void *a, int b_len, const void *b)
|
563
720
|
{
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
721
|
+
VALUE comparator;
|
722
|
+
VALUE a_str;
|
723
|
+
VALUE b_str;
|
724
|
+
VALUE comparison;
|
725
|
+
rb_encoding *internal_encoding;
|
569
726
|
|
570
|
-
|
727
|
+
internal_encoding = rb_default_internal_encoding();
|
571
728
|
|
572
|
-
|
573
|
-
|
574
|
-
|
729
|
+
comparator = (VALUE)ctx;
|
730
|
+
a_str = rb_str_new((const char *)a, a_len);
|
731
|
+
b_str = rb_str_new((const char *)b, b_len);
|
575
732
|
|
576
|
-
|
577
|
-
|
733
|
+
rb_enc_associate_index(a_str, rb_utf8_encindex());
|
734
|
+
rb_enc_associate_index(b_str, rb_utf8_encindex());
|
578
735
|
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
736
|
+
if (internal_encoding) {
|
737
|
+
a_str = rb_str_export_to_enc(a_str, internal_encoding);
|
738
|
+
b_str = rb_str_export_to_enc(b_str, internal_encoding);
|
739
|
+
}
|
583
740
|
|
584
|
-
|
741
|
+
comparison = rb_funcall(comparator, rb_intern("compare"), 2, a_str, b_str);
|
585
742
|
|
586
|
-
|
743
|
+
return NUM2INT(comparison);
|
587
744
|
}
|
588
745
|
|
589
746
|
/* call-seq: db.collation(name, comparator)
|
@@ -593,50 +750,42 @@ int rb_comparator_func(void * ctx, int a_len, const void * a, int b_len, const v
|
|
593
750
|
* two parameters and returns an integer less than, equal to, or greater than
|
594
751
|
* 0.
|
595
752
|
*/
|
596
|
-
static VALUE
|
753
|
+
static VALUE
|
754
|
+
collation(VALUE self, VALUE name, VALUE comparator)
|
597
755
|
{
|
598
|
-
|
599
|
-
|
600
|
-
|
756
|
+
sqlite3RubyPtr ctx;
|
757
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
758
|
+
REQUIRE_OPEN_DB(ctx);
|
601
759
|
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
760
|
+
CHECK(ctx->db, sqlite3_create_collation(
|
761
|
+
ctx->db,
|
762
|
+
StringValuePtr(name),
|
763
|
+
SQLITE_UTF8,
|
764
|
+
(void *)comparator,
|
765
|
+
NIL_P(comparator) ? NULL : rb_comparator_func));
|
608
766
|
|
609
|
-
|
610
|
-
|
767
|
+
/* Make sure our comparator doesn't get garbage collected. */
|
768
|
+
rb_hash_aset(rb_iv_get(self, "@collations"), name, comparator);
|
611
769
|
|
612
|
-
|
770
|
+
return self;
|
613
771
|
}
|
614
772
|
|
615
773
|
#ifdef HAVE_SQLITE3_LOAD_EXTENSION
|
616
|
-
|
617
|
-
|
618
|
-
* Loads an SQLite extension library from the named file. Extension
|
619
|
-
* loading must be enabled using db.enable_load_extension(true) prior
|
620
|
-
* to calling this API.
|
621
|
-
*/
|
622
|
-
static VALUE load_extension(VALUE self, VALUE file)
|
774
|
+
static VALUE
|
775
|
+
load_extension_internal(VALUE self, VALUE file)
|
623
776
|
{
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
777
|
+
sqlite3RubyPtr ctx;
|
778
|
+
int status;
|
779
|
+
char *errMsg;
|
780
|
+
|
781
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
782
|
+
REQUIRE_OPEN_DB(ctx);
|
630
783
|
|
631
|
-
|
632
|
-
if (status != SQLITE_OK)
|
633
|
-
{
|
634
|
-
errexp = rb_exc_new2(rb_eRuntimeError, errMsg);
|
635
|
-
sqlite3_free(errMsg);
|
636
|
-
rb_exc_raise(errexp);
|
637
|
-
}
|
784
|
+
status = sqlite3_load_extension(ctx->db, StringValuePtr(file), 0, &errMsg);
|
638
785
|
|
639
|
-
|
786
|
+
CHECK_MSG(ctx->db, status, errMsg);
|
787
|
+
|
788
|
+
return self;
|
640
789
|
}
|
641
790
|
#endif
|
642
791
|
|
@@ -645,107 +794,79 @@ static VALUE load_extension(VALUE self, VALUE file)
|
|
645
794
|
*
|
646
795
|
* Enable or disable extension loading.
|
647
796
|
*/
|
648
|
-
static VALUE
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
797
|
+
static VALUE
|
798
|
+
enable_load_extension(VALUE self, VALUE onoff)
|
799
|
+
{
|
800
|
+
sqlite3RubyPtr ctx;
|
801
|
+
int onoffparam;
|
802
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
803
|
+
REQUIRE_OPEN_DB(ctx);
|
804
|
+
|
805
|
+
if (Qtrue == onoff) {
|
806
|
+
onoffparam = 1;
|
807
|
+
} else if (Qfalse == onoff) {
|
808
|
+
onoffparam = 0;
|
809
|
+
} else {
|
810
|
+
onoffparam = (int)NUM2INT(onoff);
|
811
|
+
}
|
662
812
|
|
663
|
-
|
813
|
+
CHECK(ctx->db, sqlite3_enable_load_extension(ctx->db, onoffparam));
|
664
814
|
|
665
|
-
|
815
|
+
return self;
|
666
816
|
}
|
667
817
|
#endif
|
668
818
|
|
669
|
-
static int enc_cb(void * _self, int UNUSED(columns), char **data, char **UNUSED(names))
|
670
|
-
{
|
671
|
-
VALUE self = (VALUE)_self;
|
672
|
-
|
673
|
-
int index = rb_enc_find_index(data[0]);
|
674
|
-
rb_encoding * e = rb_enc_from_index(index);
|
675
|
-
rb_iv_set(self, "@encoding", rb_enc_from_encoding(e));
|
676
|
-
|
677
|
-
return 0;
|
678
|
-
}
|
679
|
-
|
680
|
-
/* call-seq: db.encoding
|
681
|
-
*
|
682
|
-
* Fetch the encoding set on this database
|
683
|
-
*/
|
684
|
-
static VALUE db_encoding(VALUE self)
|
685
|
-
{
|
686
|
-
sqlite3RubyPtr ctx;
|
687
|
-
VALUE enc;
|
688
|
-
|
689
|
-
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
690
|
-
REQUIRE_OPEN_DB(ctx);
|
691
|
-
|
692
|
-
enc = rb_iv_get(self, "@encoding");
|
693
|
-
|
694
|
-
if(NIL_P(enc)) {
|
695
|
-
sqlite3_exec(ctx->db, "PRAGMA encoding", enc_cb, (void *)self, NULL);
|
696
|
-
}
|
697
|
-
|
698
|
-
return rb_iv_get(self, "@encoding");
|
699
|
-
}
|
700
|
-
|
701
819
|
/* call-seq: db.transaction_active?
|
702
820
|
*
|
703
821
|
* Returns +true+ if there is a transaction active, and +false+ otherwise.
|
704
822
|
*
|
705
823
|
*/
|
706
|
-
static VALUE
|
824
|
+
static VALUE
|
825
|
+
transaction_active_p(VALUE self)
|
707
826
|
{
|
708
|
-
|
709
|
-
|
710
|
-
|
827
|
+
sqlite3RubyPtr ctx;
|
828
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
829
|
+
REQUIRE_OPEN_DB(ctx);
|
711
830
|
|
712
|
-
|
831
|
+
return sqlite3_get_autocommit(ctx->db) ? Qfalse : Qtrue;
|
713
832
|
}
|
714
833
|
|
715
|
-
static int
|
834
|
+
static int
|
835
|
+
hash_callback_function(VALUE callback_ary, int count, char **data, char **columns)
|
716
836
|
{
|
717
|
-
|
718
|
-
|
837
|
+
VALUE new_hash = rb_hash_new();
|
838
|
+
int i;
|
719
839
|
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
840
|
+
for (i = 0; i < count; i++) {
|
841
|
+
if (data[i] == NULL) {
|
842
|
+
rb_hash_aset(new_hash, rb_str_new_cstr(columns[i]), Qnil);
|
843
|
+
} else {
|
844
|
+
rb_hash_aset(new_hash, rb_str_new_cstr(columns[i]), rb_str_new_cstr(data[i]));
|
845
|
+
}
|
725
846
|
}
|
726
|
-
}
|
727
847
|
|
728
|
-
|
848
|
+
rb_ary_push(callback_ary, new_hash);
|
729
849
|
|
730
|
-
|
850
|
+
return 0;
|
731
851
|
}
|
732
852
|
|
733
|
-
static int
|
853
|
+
static int
|
854
|
+
regular_callback_function(VALUE callback_ary, int count, char **data, char **columns)
|
734
855
|
{
|
735
|
-
|
736
|
-
|
856
|
+
VALUE new_ary = rb_ary_new();
|
857
|
+
int i;
|
737
858
|
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
859
|
+
for (i = 0; i < count; i++) {
|
860
|
+
if (data[i] == NULL) {
|
861
|
+
rb_ary_push(new_ary, Qnil);
|
862
|
+
} else {
|
863
|
+
rb_ary_push(new_ary, rb_str_new_cstr(data[i]));
|
864
|
+
}
|
743
865
|
}
|
744
|
-
}
|
745
866
|
|
746
|
-
|
867
|
+
rb_ary_push(callback_ary, new_ary);
|
747
868
|
|
748
|
-
|
869
|
+
return 0;
|
749
870
|
}
|
750
871
|
|
751
872
|
|
@@ -757,31 +878,30 @@ static int regular_callback_function(VALUE callback_ary, int count, char **data,
|
|
757
878
|
* so the user may parse values with a block.
|
758
879
|
* If no query is made, an empty array will be returned.
|
759
880
|
*/
|
760
|
-
static VALUE
|
881
|
+
static VALUE
|
882
|
+
exec_batch(VALUE self, VALUE sql, VALUE results_as_hash)
|
761
883
|
{
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
VALUE errexp;
|
884
|
+
sqlite3RubyPtr ctx;
|
885
|
+
int status;
|
886
|
+
VALUE callback_ary = rb_ary_new();
|
887
|
+
char *errMsg;
|
767
888
|
|
768
|
-
|
769
|
-
|
889
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
890
|
+
REQUIRE_OPEN_DB(ctx);
|
770
891
|
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
892
|
+
if (results_as_hash == Qtrue) {
|
893
|
+
status = sqlite3_exec(ctx->db, StringValuePtr(sql), (sqlite3_callback)hash_callback_function,
|
894
|
+
(void *)callback_ary,
|
895
|
+
&errMsg);
|
896
|
+
} else {
|
897
|
+
status = sqlite3_exec(ctx->db, StringValuePtr(sql), (sqlite3_callback)regular_callback_function,
|
898
|
+
(void *)callback_ary,
|
899
|
+
&errMsg);
|
900
|
+
}
|
776
901
|
|
777
|
-
|
778
|
-
{
|
779
|
-
errexp = rb_exc_new2(rb_eRuntimeError, errMsg);
|
780
|
-
sqlite3_free(errMsg);
|
781
|
-
rb_exc_raise(errexp);
|
782
|
-
}
|
902
|
+
CHECK_MSG(ctx->db, status, errMsg);
|
783
903
|
|
784
|
-
|
904
|
+
return callback_ary;
|
785
905
|
}
|
786
906
|
|
787
907
|
/* call-seq: db.db_filename(database_name)
|
@@ -789,88 +909,96 @@ static VALUE exec_batch(VALUE self, VALUE sql, VALUE results_as_hash)
|
|
789
909
|
* Returns the file associated with +database_name+. Can return nil or an
|
790
910
|
* empty string if the database is temporary, or in-memory.
|
791
911
|
*/
|
792
|
-
static VALUE
|
912
|
+
static VALUE
|
913
|
+
db_filename(VALUE self, VALUE db_name)
|
793
914
|
{
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
915
|
+
sqlite3RubyPtr ctx;
|
916
|
+
const char *fname;
|
917
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
918
|
+
REQUIRE_OPEN_DB(ctx);
|
798
919
|
|
799
|
-
|
920
|
+
fname = sqlite3_db_filename(ctx->db, StringValueCStr(db_name));
|
800
921
|
|
801
|
-
|
802
|
-
|
922
|
+
if (fname) { return SQLITE3_UTF8_STR_NEW2(fname); }
|
923
|
+
return Qnil;
|
803
924
|
}
|
804
925
|
|
805
|
-
static VALUE
|
926
|
+
static VALUE
|
927
|
+
rb_sqlite3_open16(VALUE self, VALUE file)
|
806
928
|
{
|
807
|
-
|
808
|
-
|
929
|
+
int status;
|
930
|
+
sqlite3RubyPtr ctx;
|
809
931
|
|
810
|
-
|
932
|
+
TypedData_Get_Struct(self, sqlite3Ruby, &database_type, ctx);
|
811
933
|
|
812
934
|
#if defined TAINTING_SUPPORT
|
813
935
|
#if defined StringValueCStr
|
814
|
-
|
815
|
-
|
936
|
+
StringValuePtr(file);
|
937
|
+
rb_check_safe_obj(file);
|
816
938
|
#else
|
817
|
-
|
939
|
+
Check_SafeStr(file);
|
818
940
|
#endif
|
819
941
|
#endif
|
820
942
|
|
821
|
-
|
943
|
+
// sqlite3_open16 implicitly uses flags (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
|
944
|
+
// see https://www.sqlite.org/capi3ref.html#sqlite3_open
|
945
|
+
// so we do not ever set SQLITE3_RB_DATABASE_READONLY in ctx->flags
|
946
|
+
status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
|
822
947
|
|
823
|
-
|
948
|
+
CHECK(ctx->db, status)
|
824
949
|
|
825
|
-
|
950
|
+
return INT2NUM(status);
|
826
951
|
}
|
827
952
|
|
828
|
-
void
|
953
|
+
void
|
954
|
+
init_sqlite3_database(void)
|
829
955
|
{
|
830
956
|
#if 0
|
831
|
-
|
957
|
+
VALUE mSqlite3 = rb_define_module("SQLite3");
|
832
958
|
#endif
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
959
|
+
cSqlite3Database = rb_define_class_under(mSqlite3, "Database", rb_cObject);
|
960
|
+
|
961
|
+
rb_define_alloc_func(cSqlite3Database, allocate);
|
962
|
+
rb_define_private_method(cSqlite3Database, "open_v2", rb_sqlite3_open_v2, 3);
|
963
|
+
rb_define_private_method(cSqlite3Database, "open16", rb_sqlite3_open16, 1);
|
964
|
+
rb_define_method(cSqlite3Database, "collation", collation, 2);
|
965
|
+
rb_define_method(cSqlite3Database, "close", sqlite3_rb_close, 0);
|
966
|
+
rb_define_private_method(cSqlite3Database, "discard", sqlite3_rb_discard, 0);
|
967
|
+
rb_define_method(cSqlite3Database, "closed?", closed_p, 0);
|
968
|
+
rb_define_method(cSqlite3Database, "total_changes", total_changes, 0);
|
969
|
+
rb_define_method(cSqlite3Database, "trace", trace, -1);
|
970
|
+
rb_define_method(cSqlite3Database, "last_insert_row_id", last_insert_row_id, 0);
|
971
|
+
rb_define_method(cSqlite3Database, "define_function", define_function, 1);
|
972
|
+
rb_define_method(cSqlite3Database, "define_function_with_flags", define_function_with_flags, 2);
|
973
|
+
/* public "define_aggregator" is now a shim around define_aggregator2
|
974
|
+
* implemented in Ruby */
|
975
|
+
rb_define_private_method(cSqlite3Database, "define_aggregator2", rb_sqlite3_define_aggregator2, 2);
|
976
|
+
rb_define_private_method(cSqlite3Database, "disable_quirk_mode", rb_sqlite3_disable_quirk_mode, 0);
|
977
|
+
rb_define_method(cSqlite3Database, "interrupt", interrupt, 0);
|
978
|
+
rb_define_method(cSqlite3Database, "errmsg", errmsg, 0);
|
979
|
+
rb_define_method(cSqlite3Database, "errcode", errcode_, 0);
|
980
|
+
rb_define_method(cSqlite3Database, "complete?", complete_p, 1);
|
981
|
+
rb_define_method(cSqlite3Database, "changes", changes, 0);
|
982
|
+
rb_define_method(cSqlite3Database, "authorizer=", set_authorizer, 1);
|
983
|
+
rb_define_method(cSqlite3Database, "busy_handler", busy_handler, -1);
|
984
|
+
rb_define_method(cSqlite3Database, "busy_timeout=", set_busy_timeout, 1);
|
985
|
+
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
|
986
|
+
rb_define_method(cSqlite3Database, "statement_timeout=", set_statement_timeout, 1);
|
987
|
+
#endif
|
988
|
+
rb_define_method(cSqlite3Database, "extended_result_codes=", set_extended_result_codes, 1);
|
989
|
+
rb_define_method(cSqlite3Database, "transaction_active?", transaction_active_p, 0);
|
990
|
+
rb_define_private_method(cSqlite3Database, "exec_batch", exec_batch, 2);
|
991
|
+
rb_define_private_method(cSqlite3Database, "db_filename", db_filename, 1);
|
862
992
|
|
863
993
|
#ifdef HAVE_SQLITE3_LOAD_EXTENSION
|
864
|
-
|
994
|
+
rb_define_private_method(cSqlite3Database, "load_extension_internal", load_extension_internal, 1);
|
865
995
|
#endif
|
866
996
|
|
867
997
|
#ifdef HAVE_SQLITE3_ENABLE_LOAD_EXTENSION
|
868
|
-
|
998
|
+
rb_define_method(cSqlite3Database, "enable_load_extension", enable_load_extension, 1);
|
869
999
|
#endif
|
870
1000
|
|
871
|
-
|
872
|
-
|
873
|
-
rb_sqlite3_aggregator_init();
|
1001
|
+
rb_sqlite3_aggregator_init();
|
874
1002
|
}
|
875
1003
|
|
876
1004
|
#ifdef _MSC_VER
|