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