extralite-bundle 1.14
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 +7 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +26 -0
- data/.gitignore +57 -0
- data/CHANGELOG.md +96 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +37 -0
- data/LICENSE +21 -0
- data/README.md +248 -0
- data/Rakefile +46 -0
- data/TODO.md +0 -0
- data/bin/update_sqlite_source +26 -0
- data/ext/extralite/common.c +347 -0
- data/ext/extralite/database.c +385 -0
- data/ext/extralite/extconf-bundle.rb +9 -0
- data/ext/extralite/extconf.rb +115 -0
- data/ext/extralite/extralite.h +64 -0
- data/ext/extralite/extralite_ext.c +7 -0
- data/ext/extralite/extralite_sqlite3.c +3 -0
- data/ext/extralite/prepared_statement.c +238 -0
- data/ext/sqlite3/sqlite3.c +239246 -0
- data/ext/sqlite3/sqlite3.h +12802 -0
- data/extralite-bundle.gemspec +8 -0
- data/extralite.gemspec +8 -0
- data/gemspec.rb +26 -0
- data/lib/extralite/version.rb +3 -0
- data/lib/extralite.rb +21 -0
- data/lib/sequel/adapters/extralite.rb +380 -0
- data/test/extensions/text.dylib +0 -0
- data/test/extensions/text.so +0 -0
- data/test/helper.rb +7 -0
- data/test/perf_ary.rb +51 -0
- data/test/perf_hash.rb +51 -0
- data/test/perf_prepared.rb +64 -0
- data/test/run.rb +5 -0
- data/test/test_database.rb +247 -0
- data/test/test_extralite.rb +9 -0
- data/test/test_prepared_statement.rb +165 -0
- data/test/test_sequel.rb +24 -0
- metadata +160 -0
@@ -0,0 +1,385 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include "extralite.h"
|
3
|
+
|
4
|
+
VALUE cDatabase;
|
5
|
+
VALUE cError;
|
6
|
+
VALUE cSQLError;
|
7
|
+
VALUE cBusyError;
|
8
|
+
|
9
|
+
ID ID_KEYS;
|
10
|
+
ID ID_NEW;
|
11
|
+
ID ID_STRIP;
|
12
|
+
ID ID_TO_S;
|
13
|
+
|
14
|
+
static size_t Database_size(const void *ptr) {
|
15
|
+
return sizeof(Database_t);
|
16
|
+
}
|
17
|
+
|
18
|
+
static void Database_free(void *ptr) {
|
19
|
+
Database_t *db = ptr;
|
20
|
+
if (db->sqlite3_db) sqlite3_close(db->sqlite3_db);
|
21
|
+
free(ptr);
|
22
|
+
}
|
23
|
+
|
24
|
+
static const rb_data_type_t Database_type = {
|
25
|
+
"Database",
|
26
|
+
{0, Database_free, Database_size,},
|
27
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
28
|
+
};
|
29
|
+
|
30
|
+
static VALUE Database_allocate(VALUE klass) {
|
31
|
+
Database_t *db = ALLOC(Database_t);
|
32
|
+
db->sqlite3_db = 0;
|
33
|
+
return TypedData_Wrap_Struct(klass, &Database_type, db);
|
34
|
+
}
|
35
|
+
|
36
|
+
#define GetDatabase(obj, database) \
|
37
|
+
TypedData_Get_Struct((obj), Database_t, &Database_type, (database))
|
38
|
+
|
39
|
+
// make sure the database is open
|
40
|
+
#define GetOpenDatabase(obj, database) { \
|
41
|
+
TypedData_Get_Struct((obj), Database_t, &Database_type, (database)); \
|
42
|
+
if (!(database)->sqlite3_db) { \
|
43
|
+
rb_raise(cError, "Database is closed"); \
|
44
|
+
} \
|
45
|
+
}
|
46
|
+
|
47
|
+
sqlite3 *Database_sqlite3_db(VALUE self) {
|
48
|
+
Database_t *db;
|
49
|
+
GetDatabase(self, db);
|
50
|
+
return db->sqlite3_db;
|
51
|
+
}
|
52
|
+
|
53
|
+
/* call-seq:
|
54
|
+
* Extralite.sqlite3_version -> version
|
55
|
+
*
|
56
|
+
* Returns the sqlite3 version used by Extralite.
|
57
|
+
*/
|
58
|
+
|
59
|
+
VALUE Extralite_sqlite3_version(VALUE self) {
|
60
|
+
return rb_str_new_cstr(sqlite3_version);
|
61
|
+
}
|
62
|
+
|
63
|
+
/* call-seq:
|
64
|
+
* db.initialize(path)
|
65
|
+
*
|
66
|
+
* Initializes a new SQLite database with the given path.
|
67
|
+
*/
|
68
|
+
|
69
|
+
VALUE Database_initialize(VALUE self, VALUE path) {
|
70
|
+
int rc;
|
71
|
+
Database_t *db;
|
72
|
+
GetDatabase(self, db);
|
73
|
+
|
74
|
+
rc = sqlite3_open(StringValueCStr(path), &db->sqlite3_db);
|
75
|
+
if (rc) {
|
76
|
+
sqlite3_close(db->sqlite3_db);
|
77
|
+
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
78
|
+
}
|
79
|
+
|
80
|
+
rc = sqlite3_enable_load_extension(db->sqlite3_db, 1);
|
81
|
+
if (rc) {
|
82
|
+
sqlite3_close(db->sqlite3_db);
|
83
|
+
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
84
|
+
}
|
85
|
+
|
86
|
+
return Qnil;
|
87
|
+
}
|
88
|
+
|
89
|
+
/* call-seq:
|
90
|
+
* db.close -> db
|
91
|
+
*
|
92
|
+
* Closes the database.
|
93
|
+
*/
|
94
|
+
VALUE Database_close(VALUE self) {
|
95
|
+
int rc;
|
96
|
+
Database_t *db;
|
97
|
+
GetDatabase(self, db);
|
98
|
+
|
99
|
+
rc = sqlite3_close(db->sqlite3_db);
|
100
|
+
if (rc) {
|
101
|
+
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
102
|
+
}
|
103
|
+
|
104
|
+
db->sqlite3_db = 0;
|
105
|
+
return self;
|
106
|
+
}
|
107
|
+
|
108
|
+
/* call-seq:
|
109
|
+
* db.closed? -> bool
|
110
|
+
*
|
111
|
+
* Returns true if the database is closed.
|
112
|
+
*
|
113
|
+
* @return [bool] is database closed
|
114
|
+
*/
|
115
|
+
VALUE Database_closed_p(VALUE self) {
|
116
|
+
Database_t *db;
|
117
|
+
GetDatabase(self, db);
|
118
|
+
|
119
|
+
return db->sqlite3_db ? Qfalse : Qtrue;
|
120
|
+
}
|
121
|
+
|
122
|
+
static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VALUE (*call)(query_ctx *)) {
|
123
|
+
Database_t *db;
|
124
|
+
sqlite3_stmt *stmt;
|
125
|
+
VALUE sql;
|
126
|
+
|
127
|
+
// extract query from args
|
128
|
+
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
|
129
|
+
sql = rb_funcall(argv[0], ID_STRIP, 0);
|
130
|
+
if (RSTRING_LEN(sql) == 0) return Qnil;
|
131
|
+
|
132
|
+
// prepare query ctx
|
133
|
+
GetOpenDatabase(self, db);
|
134
|
+
prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
|
135
|
+
bind_all_parameters(stmt, argc - 1, argv + 1);
|
136
|
+
query_ctx ctx = { self, db->sqlite3_db, stmt };
|
137
|
+
|
138
|
+
return rb_ensure(SAFE(call), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
|
139
|
+
}
|
140
|
+
|
141
|
+
/* call-seq:
|
142
|
+
* db.query(sql, *parameters, &block) -> [...]
|
143
|
+
* db.query_hash(sql, *parameters, &block) -> [...]
|
144
|
+
*
|
145
|
+
* Runs a query returning rows as hashes (with symbol keys). If a block is
|
146
|
+
* given, it will be called for each row. Otherwise, an array containing all
|
147
|
+
* rows is returned.
|
148
|
+
*
|
149
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
150
|
+
* a list of values or as a hash mapping parameter names to values. When
|
151
|
+
* parameters are given as a least, the query should specify parameters using
|
152
|
+
* `?`:
|
153
|
+
*
|
154
|
+
* db.query('select * from foo where x = ?', 42)
|
155
|
+
*
|
156
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
157
|
+
* specified using a hash, where keys are either strings are symbols. String
|
158
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
159
|
+
*
|
160
|
+
* db.query('select * from foo where x = :bar', bar: 42)
|
161
|
+
* db.query('select * from foo where x = :bar', 'bar' => 42)
|
162
|
+
* db.query('select * from foo where x = :bar', ':bar' => 42)
|
163
|
+
*/
|
164
|
+
VALUE Database_query_hash(int argc, VALUE *argv, VALUE self) {
|
165
|
+
return Database_perform_query(argc, argv, self, safe_query_hash);
|
166
|
+
}
|
167
|
+
|
168
|
+
/* call-seq:
|
169
|
+
* db.query_ary(sql, *parameters, &block) -> [...]
|
170
|
+
*
|
171
|
+
* Runs a query returning rows as arrays. If a block is given, it will be called
|
172
|
+
* for each row. Otherwise, an array containing all rows is returned.
|
173
|
+
*
|
174
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
175
|
+
* a list of values or as a hash mapping parameter names to values. When
|
176
|
+
* parameters are given as a least, the query should specify parameters using
|
177
|
+
* `?`:
|
178
|
+
*
|
179
|
+
* db.query_ary('select * from foo where x = ?', 42)
|
180
|
+
*
|
181
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
182
|
+
* specified using a hash, where keys are either strings are symbols. String
|
183
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
184
|
+
*
|
185
|
+
* db.query_ary('select * from foo where x = :bar', bar: 42)
|
186
|
+
* db.query_ary('select * from foo where x = :bar', 'bar' => 42)
|
187
|
+
* db.query_ary('select * from foo where x = :bar', ':bar' => 42)
|
188
|
+
*/
|
189
|
+
VALUE Database_query_ary(int argc, VALUE *argv, VALUE self) {
|
190
|
+
return Database_perform_query(argc, argv, self, safe_query_ary);
|
191
|
+
}
|
192
|
+
|
193
|
+
/* call-seq:
|
194
|
+
* db.query_single_row(sql, *parameters) -> {...}
|
195
|
+
*
|
196
|
+
* Runs a query returning a single row as a hash.
|
197
|
+
*
|
198
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
199
|
+
* a list of values or as a hash mapping parameter names to values. When
|
200
|
+
* parameters are given as a least, the query should specify parameters using
|
201
|
+
* `?`:
|
202
|
+
*
|
203
|
+
* db.query_single_row('select * from foo where x = ?', 42)
|
204
|
+
*
|
205
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
206
|
+
* specified using a hash, where keys are either strings are symbols. String
|
207
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
208
|
+
*
|
209
|
+
* db.query_single_row('select * from foo where x = :bar', bar: 42)
|
210
|
+
* db.query_single_row('select * from foo where x = :bar', 'bar' => 42)
|
211
|
+
* db.query_single_row('select * from foo where x = :bar', ':bar' => 42)
|
212
|
+
*/
|
213
|
+
VALUE Database_query_single_row(int argc, VALUE *argv, VALUE self) {
|
214
|
+
return Database_perform_query(argc, argv, self, safe_query_single_row);
|
215
|
+
}
|
216
|
+
|
217
|
+
/* call-seq:
|
218
|
+
* db.query_single_column(sql, *parameters, &block) -> [...]
|
219
|
+
*
|
220
|
+
* Runs a query returning single column values. If a block is given, it will be called
|
221
|
+
* for each value. Otherwise, an array containing all values is returned.
|
222
|
+
*
|
223
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
224
|
+
* a list of values or as a hash mapping parameter names to values. When
|
225
|
+
* parameters are given as a least, the query should specify parameters using
|
226
|
+
* `?`:
|
227
|
+
*
|
228
|
+
* db.query_single_column('select x from foo where x = ?', 42)
|
229
|
+
*
|
230
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
231
|
+
* specified using a hash, where keys are either strings are symbols. String
|
232
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
233
|
+
*
|
234
|
+
* db.query_single_column('select x from foo where x = :bar', bar: 42)
|
235
|
+
* db.query_single_column('select x from foo where x = :bar', 'bar' => 42)
|
236
|
+
* db.query_single_column('select x from foo where x = :bar', ':bar' => 42)
|
237
|
+
*/
|
238
|
+
VALUE Database_query_single_column(int argc, VALUE *argv, VALUE self) {
|
239
|
+
return Database_perform_query(argc, argv, self, safe_query_single_column);
|
240
|
+
}
|
241
|
+
|
242
|
+
/* call-seq:
|
243
|
+
* db.query_single_value(sql, *parameters) -> value
|
244
|
+
*
|
245
|
+
* Runs a query returning a single value from the first row.
|
246
|
+
*
|
247
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
248
|
+
* a list of values or as a hash mapping parameter names to values. When
|
249
|
+
* parameters are given as a least, the query should specify parameters using
|
250
|
+
* `?`:
|
251
|
+
*
|
252
|
+
* db.query_single_value('select x from foo where x = ?', 42)
|
253
|
+
*
|
254
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
255
|
+
* specified using a hash, where keys are either strings are symbols. String
|
256
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
257
|
+
*
|
258
|
+
* db.query_single_value('select x from foo where x = :bar', bar: 42)
|
259
|
+
* db.query_single_value('select x from foo where x = :bar', 'bar' => 42)
|
260
|
+
* db.query_single_value('select x from foo where x = :bar', ':bar' => 42)
|
261
|
+
*/
|
262
|
+
VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
|
263
|
+
return Database_perform_query(argc, argv, self, safe_query_single_value);
|
264
|
+
}
|
265
|
+
|
266
|
+
/* call-seq:
|
267
|
+
* db.last_insert_rowid -> int
|
268
|
+
*
|
269
|
+
* Returns the rowid of the last inserted row.
|
270
|
+
*/
|
271
|
+
VALUE Database_last_insert_rowid(VALUE self) {
|
272
|
+
Database_t *db;
|
273
|
+
GetOpenDatabase(self, db);
|
274
|
+
|
275
|
+
return INT2NUM(sqlite3_last_insert_rowid(db->sqlite3_db));
|
276
|
+
}
|
277
|
+
|
278
|
+
/* call-seq:
|
279
|
+
* db.changes -> int
|
280
|
+
*
|
281
|
+
* Returns the number of changes made to the database by the last operation.
|
282
|
+
*/
|
283
|
+
VALUE Database_changes(VALUE self) {
|
284
|
+
Database_t *db;
|
285
|
+
GetOpenDatabase(self, db);
|
286
|
+
|
287
|
+
return INT2NUM(sqlite3_changes(db->sqlite3_db));
|
288
|
+
}
|
289
|
+
|
290
|
+
/* call-seq:
|
291
|
+
* db.filename -> string
|
292
|
+
*
|
293
|
+
* Returns the database filename.
|
294
|
+
*/
|
295
|
+
VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
|
296
|
+
const char *db_name;
|
297
|
+
const char *filename;
|
298
|
+
Database_t *db;
|
299
|
+
GetOpenDatabase(self, db);
|
300
|
+
|
301
|
+
rb_check_arity(argc, 0, 1);
|
302
|
+
db_name = (argc == 1) ? StringValueCStr(argv[0]) : "main";
|
303
|
+
filename = sqlite3_db_filename(db->sqlite3_db, db_name);
|
304
|
+
return filename ? rb_str_new_cstr(filename) : Qnil;
|
305
|
+
}
|
306
|
+
|
307
|
+
/* call-seq:
|
308
|
+
* db.transaction_active? -> bool
|
309
|
+
*
|
310
|
+
* Returns true if a transaction is currently in progress.
|
311
|
+
*/
|
312
|
+
VALUE Database_transaction_active_p(VALUE self) {
|
313
|
+
Database_t *db;
|
314
|
+
GetOpenDatabase(self, db);
|
315
|
+
|
316
|
+
return sqlite3_get_autocommit(db->sqlite3_db) ? Qfalse : Qtrue;
|
317
|
+
}
|
318
|
+
|
319
|
+
/* call-seq:
|
320
|
+
* db.load_extension(path) -> db
|
321
|
+
*
|
322
|
+
* Loads an extension with the given path.
|
323
|
+
*/
|
324
|
+
VALUE Database_load_extension(VALUE self, VALUE path) {
|
325
|
+
Database_t *db;
|
326
|
+
GetOpenDatabase(self, db);
|
327
|
+
char *err_msg;
|
328
|
+
|
329
|
+
int rc = sqlite3_load_extension(db->sqlite3_db, RSTRING_PTR(path), 0, &err_msg);
|
330
|
+
if (rc != SQLITE_OK) {
|
331
|
+
VALUE error = rb_exc_new2(cError, err_msg);
|
332
|
+
sqlite3_free(err_msg);
|
333
|
+
rb_exc_raise(error);
|
334
|
+
}
|
335
|
+
|
336
|
+
return self;
|
337
|
+
}
|
338
|
+
|
339
|
+
/* call-seq:
|
340
|
+
* db.prepare(sql) -> Extralite::PreparedStatement
|
341
|
+
*
|
342
|
+
* Creates a prepared statement with the given SQL query.
|
343
|
+
*/
|
344
|
+
VALUE Database_prepare(VALUE self, VALUE sql) {
|
345
|
+
return rb_funcall(cPreparedStatement, ID_NEW, 2, self, sql);
|
346
|
+
}
|
347
|
+
|
348
|
+
void Init_ExtraliteDatabase() {
|
349
|
+
VALUE mExtralite = rb_define_module("Extralite");
|
350
|
+
rb_define_singleton_method(mExtralite, "sqlite3_version", Extralite_sqlite3_version, 0);
|
351
|
+
|
352
|
+
cDatabase = rb_define_class_under(mExtralite, "Database", rb_cObject);
|
353
|
+
rb_define_alloc_func(cDatabase, Database_allocate);
|
354
|
+
|
355
|
+
rb_define_method(cDatabase, "initialize", Database_initialize, 1);
|
356
|
+
rb_define_method(cDatabase, "close", Database_close, 0);
|
357
|
+
rb_define_method(cDatabase, "closed?", Database_closed_p, 0);
|
358
|
+
|
359
|
+
rb_define_method(cDatabase, "query", Database_query_hash, -1);
|
360
|
+
rb_define_method(cDatabase, "query_hash", Database_query_hash, -1);
|
361
|
+
rb_define_method(cDatabase, "query_ary", Database_query_ary, -1);
|
362
|
+
rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
|
363
|
+
rb_define_method(cDatabase, "query_single_column", Database_query_single_column, -1);
|
364
|
+
rb_define_method(cDatabase, "query_single_value", Database_query_single_value, -1);
|
365
|
+
|
366
|
+
rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
|
367
|
+
rb_define_method(cDatabase, "changes", Database_changes, 0);
|
368
|
+
rb_define_method(cDatabase, "filename", Database_filename, -1);
|
369
|
+
rb_define_method(cDatabase, "transaction_active?", Database_transaction_active_p, 0);
|
370
|
+
rb_define_method(cDatabase, "load_extension", Database_load_extension, 1);
|
371
|
+
|
372
|
+
rb_define_method(cDatabase, "prepare", Database_prepare, 1);
|
373
|
+
|
374
|
+
cError = rb_define_class_under(mExtralite, "Error", rb_eRuntimeError);
|
375
|
+
cSQLError = rb_define_class_under(mExtralite, "SQLError", cError);
|
376
|
+
cBusyError = rb_define_class_under(mExtralite, "BusyError", cError);
|
377
|
+
rb_gc_register_mark_object(cError);
|
378
|
+
rb_gc_register_mark_object(cSQLError);
|
379
|
+
rb_gc_register_mark_object(cBusyError);
|
380
|
+
|
381
|
+
ID_KEYS = rb_intern("keys");
|
382
|
+
ID_NEW = rb_intern("new");
|
383
|
+
ID_STRIP = rb_intern("strip");
|
384
|
+
ID_TO_S = rb_intern("to_s");
|
385
|
+
}
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require 'rubygems'
|
4
|
+
# require 'mkmf'
|
5
|
+
|
6
|
+
# # $CFLAGS << "-Wdiscarded-qualifier"
|
7
|
+
# # $CFLAGS << " -Wno-comment"
|
8
|
+
# # $CFLAGS << " -Wno-unused-result"
|
9
|
+
# # $CFLAGS << " -Wno-dangling-else"
|
10
|
+
# # $CFLAGS << " -Wno-parentheses"
|
11
|
+
|
12
|
+
# dir_config 'extralite_ext'
|
13
|
+
# create_makefile 'extralite_ext'
|
14
|
+
|
15
|
+
ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
|
16
|
+
|
17
|
+
require 'mkmf'
|
18
|
+
|
19
|
+
# :stopdoc:
|
20
|
+
|
21
|
+
RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
|
22
|
+
|
23
|
+
ldflags = cppflags = nil
|
24
|
+
if RbConfig::CONFIG["host_os"] =~ /darwin/
|
25
|
+
begin
|
26
|
+
if with_config('sqlcipher')
|
27
|
+
brew_prefix = `brew --prefix sqlcipher`.chomp
|
28
|
+
ldflags = "#{brew_prefix}/lib"
|
29
|
+
cppflags = "#{brew_prefix}/include/sqlcipher"
|
30
|
+
pkg_conf = "#{brew_prefix}/lib/pkgconfig"
|
31
|
+
else
|
32
|
+
brew_prefix = `brew --prefix sqlite3`.chomp
|
33
|
+
ldflags = "#{brew_prefix}/lib"
|
34
|
+
cppflags = "#{brew_prefix}/include"
|
35
|
+
pkg_conf = "#{brew_prefix}/lib/pkgconfig"
|
36
|
+
end
|
37
|
+
|
38
|
+
# pkg_config should be less error prone than parsing compiler
|
39
|
+
# commandline options, but we need to set default ldflags and cpp flags
|
40
|
+
# in case the user doesn't have pkg-config installed
|
41
|
+
ENV['PKG_CONFIG_PATH'] ||= pkg_conf
|
42
|
+
rescue
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if with_config('sqlcipher')
|
47
|
+
pkg_config("sqlcipher")
|
48
|
+
else
|
49
|
+
pkg_config("sqlite3")
|
50
|
+
end
|
51
|
+
|
52
|
+
# --with-sqlite3-{dir,include,lib}
|
53
|
+
if with_config('sqlcipher')
|
54
|
+
$CFLAGS << ' -DUSING_SQLCIPHER'
|
55
|
+
dir_config("sqlcipher", cppflags, ldflags)
|
56
|
+
else
|
57
|
+
dir_config("sqlite3", cppflags, ldflags)
|
58
|
+
end
|
59
|
+
|
60
|
+
if RbConfig::CONFIG["host_os"] =~ /mswin/
|
61
|
+
$CFLAGS << ' -W3'
|
62
|
+
end
|
63
|
+
|
64
|
+
if RUBY_VERSION < '2.7'
|
65
|
+
$CFLAGS << ' -DTAINTING_SUPPORT'
|
66
|
+
end
|
67
|
+
|
68
|
+
def asplode missing
|
69
|
+
if RUBY_PLATFORM =~ /mingw|mswin/
|
70
|
+
abort "#{missing} is missing. Install SQLite3 from " +
|
71
|
+
"http://www.sqlite.org/ first."
|
72
|
+
else
|
73
|
+
abort <<-error
|
74
|
+
#{missing} is missing. Try 'brew install sqlite3',
|
75
|
+
'yum install sqlite-devel' or 'apt-get install libsqlite3-dev'
|
76
|
+
and check your shared library search path (the
|
77
|
+
location where your sqlite3 shared library is located).
|
78
|
+
error
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
asplode('sqlite3.h') unless find_header 'sqlite3.h'
|
83
|
+
find_library 'pthread', 'pthread_create' # 1.8 support. *shrug*
|
84
|
+
|
85
|
+
have_library 'dl' # for static builds
|
86
|
+
|
87
|
+
if with_config('sqlcipher')
|
88
|
+
asplode('sqlcipher') unless find_library 'sqlcipher', 'sqlite3_libversion_number'
|
89
|
+
else
|
90
|
+
asplode('sqlite3') unless find_library 'sqlite3', 'sqlite3_libversion_number'
|
91
|
+
end
|
92
|
+
|
93
|
+
# Functions defined in 1.9 but not 1.8
|
94
|
+
have_func('rb_proc_arity')
|
95
|
+
|
96
|
+
# Functions defined in 2.1 but not 2.0
|
97
|
+
have_func('rb_integer_pack')
|
98
|
+
|
99
|
+
# These functions may not be defined
|
100
|
+
have_func('sqlite3_initialize')
|
101
|
+
have_func('sqlite3_enable_load_extension')
|
102
|
+
have_func('sqlite3_load_extension')
|
103
|
+
|
104
|
+
unless have_func('sqlite3_open_v2')
|
105
|
+
abort "Please use a newer version of SQLite3"
|
106
|
+
end
|
107
|
+
|
108
|
+
have_func('sqlite3_prepare_v2')
|
109
|
+
have_type('sqlite3_int64', 'sqlite3.h')
|
110
|
+
have_type('sqlite3_uint64', 'sqlite3.h')
|
111
|
+
|
112
|
+
$defs << "-DEXTRALITE_NO_BUNDLE"
|
113
|
+
|
114
|
+
dir_config('extralite_ext')
|
115
|
+
create_makefile('extralite_ext')
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#ifndef EXTRALITE_H
|
2
|
+
#define EXTRALITE_H
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
#include "ruby/thread.h"
|
6
|
+
|
7
|
+
#ifdef EXTRALITE_NO_BUNDLE
|
8
|
+
#include <sqlite3.h>
|
9
|
+
#else
|
10
|
+
#include "../sqlite3/sqlite3.h"
|
11
|
+
#endif
|
12
|
+
|
13
|
+
// debug utility
|
14
|
+
#define INSPECT(str, obj) { \
|
15
|
+
printf(str); \
|
16
|
+
VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); \
|
17
|
+
printf(": %s\n", StringValueCStr(s)); \
|
18
|
+
}
|
19
|
+
|
20
|
+
#define SAFE(f) (VALUE (*)(VALUE))(f)
|
21
|
+
|
22
|
+
extern VALUE cDatabase;
|
23
|
+
extern VALUE cPreparedStatement;
|
24
|
+
|
25
|
+
extern VALUE cError;
|
26
|
+
extern VALUE cSQLError;
|
27
|
+
extern VALUE cBusyError;
|
28
|
+
|
29
|
+
extern ID ID_KEYS;
|
30
|
+
extern ID ID_NEW;
|
31
|
+
extern ID ID_STRIP;
|
32
|
+
extern ID ID_TO_S;
|
33
|
+
|
34
|
+
typedef struct {
|
35
|
+
sqlite3 *sqlite3_db;
|
36
|
+
} Database_t;
|
37
|
+
|
38
|
+
typedef struct {
|
39
|
+
VALUE db;
|
40
|
+
VALUE sql;
|
41
|
+
sqlite3 *sqlite3_db;
|
42
|
+
sqlite3_stmt *stmt;
|
43
|
+
} PreparedStatement_t;
|
44
|
+
|
45
|
+
typedef struct {
|
46
|
+
VALUE self;
|
47
|
+
sqlite3 *sqlite3_db;
|
48
|
+
sqlite3_stmt *stmt;
|
49
|
+
} query_ctx;
|
50
|
+
|
51
|
+
VALUE safe_query_ary(query_ctx *ctx);
|
52
|
+
VALUE safe_query_hash(query_ctx *ctx);
|
53
|
+
VALUE safe_query_single_column(query_ctx *ctx);
|
54
|
+
VALUE safe_query_single_row(query_ctx *ctx);
|
55
|
+
VALUE safe_query_single_value(query_ctx *ctx);
|
56
|
+
|
57
|
+
void prepare_single_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql);
|
58
|
+
void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql);
|
59
|
+
void bind_all_parameters(sqlite3_stmt *stmt, int argc, VALUE *argv);
|
60
|
+
VALUE cleanup_stmt(query_ctx *ctx);
|
61
|
+
|
62
|
+
sqlite3 *Database_sqlite3_db(VALUE self);
|
63
|
+
|
64
|
+
#endif /* EXTRALITE_H */
|