extralite-bundle 1.17 → 1.19
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 +9 -0
- data/Gemfile.lock +1 -1
- data/README.md +5 -0
- data/ext/extralite/common.c +31 -3
- data/ext/extralite/database.c +36 -0
- data/ext/extralite/extconf.rb +1 -1
- data/ext/extralite/extralite.h +4 -0
- data/ext/extralite/prepared_statement.c +27 -1
- data/lib/extralite/version.rb +1 -1
- data/test/test_database.rb +36 -0
- data/test/test_prepared_statement.rb +19 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f614b88b731209821d211b3e54481829e60a374881d0e33bdd29e4510654719
|
4
|
+
data.tar.gz: 4c1ba63f851ac3a757e7cd3b72af4be2fc95855c3d6529d9f39f4613ae8b72f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 368d16a06c4e60a6c27568bd428848e94fa12bd0eb33204d81c9f0ac7c3e9cf6387300afb9a9be758c916500ccc50a3e83564239e7e7bd88af44eafdc3158071
|
7
|
+
data.tar.gz: 81ed36fc318dcd971044054638bebb14bb506de7d26326620a9548b1f3616c31c8c97c1e77cbd20cf55c86b2773ac1cc7f9cc772b238e6af3f779a627dc070fc
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 1.19 2022-12-01
|
2
|
+
|
3
|
+
- Add `Database#execute_multi`
|
4
|
+
- Add `PreparedStatement#execute_multi`
|
5
|
+
|
6
|
+
## 1.18 2022-12-01
|
7
|
+
|
8
|
+
- Fix usage with system sqlite3 lib where `load_extension` is disabled
|
9
|
+
|
1
10
|
## 1.17 2022-10-31
|
2
11
|
|
3
12
|
- Fix `Database#tables` to work on older version of sqlite (pre 3.33.0)
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -51,6 +51,7 @@ latest features and enhancements.
|
|
51
51
|
queries (handy for creating/modifying schemas).
|
52
52
|
- Get last insert rowid.
|
53
53
|
- Get number of rows changed by last query.
|
54
|
+
- Execute the same query with multiple parameter lists (useful for inserting records).
|
54
55
|
- Load extensions (loading of extensions is autmatically enabled. You can find
|
55
56
|
some useful extensions here: https://github.com/nalgeon/sqlean.)
|
56
57
|
- Includes a [Sequel adapter](#usage-with-sequel).
|
@@ -123,6 +124,10 @@ db.query('select * from foo where bar = :bar', bar: 42)
|
|
123
124
|
db.query('select * from foo where bar = :bar', 'bar' => 42)
|
124
125
|
db.query('select * from foo where bar = :bar', ':bar' => 42)
|
125
126
|
|
127
|
+
# insert multiple rows
|
128
|
+
db.execute_multi('insert into foo values (?)', ['bar', 'baz'])
|
129
|
+
db.execute_multi('insert into foo values (?, ?)', [[1, 2], [3, 4]])
|
130
|
+
|
126
131
|
# prepared statements
|
127
132
|
stmt = db.prepare('select ? as foo, ? as bar') #=> Extralite::PreparedStatement
|
128
133
|
stmt.query(1, 2) #=> [{ :foo => 1, :bar => 2 }]
|
data/ext/extralite/common.c
CHANGED
@@ -81,6 +81,16 @@ void bind_all_parameters(sqlite3_stmt *stmt, int argc, VALUE *argv) {
|
|
81
81
|
}
|
82
82
|
}
|
83
83
|
|
84
|
+
void bind_all_parameters_from_object(sqlite3_stmt *stmt, VALUE obj) {
|
85
|
+
if (TYPE(obj) == T_ARRAY) {
|
86
|
+
int count = RARRAY_LEN(obj);
|
87
|
+
for (int i = 0; i < count; i++)
|
88
|
+
bind_parameter_value(stmt, i + 1, RARRAY_AREF(obj, i));
|
89
|
+
}
|
90
|
+
else
|
91
|
+
bind_parameter_value(stmt, 1, obj);
|
92
|
+
}
|
93
|
+
|
84
94
|
static inline VALUE get_column_names(sqlite3_stmt *stmt, int column_count) {
|
85
95
|
VALUE arr = rb_ary_new2(column_count);
|
86
96
|
for (int i = 0; i < column_count; i++) {
|
@@ -223,7 +233,7 @@ void *stmt_iterate_without_gvl(void *ptr) {
|
|
223
233
|
return NULL;
|
224
234
|
}
|
225
235
|
|
226
|
-
|
236
|
+
int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db) {
|
227
237
|
struct step_ctx ctx = {stmt, 0};
|
228
238
|
rb_thread_call_without_gvl(stmt_iterate_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
|
229
239
|
switch (ctx.rc) {
|
@@ -262,7 +272,8 @@ VALUE safe_query_hash(query_ctx *ctx) {
|
|
262
272
|
|
263
273
|
while (stmt_iterate(ctx->stmt, ctx->sqlite3_db)) {
|
264
274
|
row = row_to_hash(ctx->stmt, column_count, column_names);
|
265
|
-
if (yield_to_block) rb_yield(row);
|
275
|
+
if (yield_to_block) rb_yield(row);
|
276
|
+
else rb_ary_push(result, row);
|
266
277
|
}
|
267
278
|
|
268
279
|
RB_GC_GUARD(column_names);
|
@@ -284,7 +295,8 @@ VALUE safe_query_ary(query_ctx *ctx) {
|
|
284
295
|
|
285
296
|
while (stmt_iterate(ctx->stmt, ctx->sqlite3_db)) {
|
286
297
|
row = row_to_ary(ctx->stmt, column_count);
|
287
|
-
if (yield_to_block) rb_yield(row);
|
298
|
+
if (yield_to_block) rb_yield(row);
|
299
|
+
else rb_ary_push(result, row);
|
288
300
|
}
|
289
301
|
|
290
302
|
RB_GC_GUARD(row);
|
@@ -346,6 +358,22 @@ VALUE safe_query_single_value(query_ctx *ctx) {
|
|
346
358
|
return value;
|
347
359
|
}
|
348
360
|
|
361
|
+
VALUE safe_execute_multi(query_ctx *ctx) {
|
362
|
+
int count = RARRAY_LEN(ctx->params);
|
363
|
+
int changes = 0;
|
364
|
+
|
365
|
+
for (int i = 0; i < count; i++) {
|
366
|
+
sqlite3_reset(ctx->stmt);
|
367
|
+
sqlite3_clear_bindings(ctx->stmt);
|
368
|
+
bind_all_parameters_from_object(ctx->stmt, RARRAY_AREF(ctx->params, i));
|
369
|
+
|
370
|
+
while (stmt_iterate(ctx->stmt, ctx->sqlite3_db));
|
371
|
+
changes += sqlite3_changes(ctx->sqlite3_db);
|
372
|
+
}
|
373
|
+
|
374
|
+
return INT2FIX(changes);
|
375
|
+
}
|
376
|
+
|
349
377
|
VALUE safe_query_columns(query_ctx *ctx) {
|
350
378
|
return get_column_names(ctx->stmt, sqlite3_column_count(ctx->stmt));
|
351
379
|
}
|
data/ext/extralite/database.c
CHANGED
@@ -77,11 +77,13 @@ VALUE Database_initialize(VALUE self, VALUE path) {
|
|
77
77
|
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
78
78
|
}
|
79
79
|
|
80
|
+
#ifdef HAVE_SQLITE3_ENABLE_LOAD_EXTENSION
|
80
81
|
rc = sqlite3_enable_load_extension(db->sqlite3_db, 1);
|
81
82
|
if (rc) {
|
82
83
|
sqlite3_close(db->sqlite3_db);
|
83
84
|
rb_raise(cError, "%s", sqlite3_errmsg(db->sqlite3_db));
|
84
85
|
}
|
86
|
+
#endif
|
85
87
|
|
86
88
|
return Qnil;
|
87
89
|
}
|
@@ -263,6 +265,34 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
|
|
263
265
|
return Database_perform_query(argc, argv, self, safe_query_single_value);
|
264
266
|
}
|
265
267
|
|
268
|
+
/* call-seq:
|
269
|
+
* db.execute_multi(sql, params_array) -> changes
|
270
|
+
*
|
271
|
+
* Executes the given query for each list of parameters in params_array. Returns
|
272
|
+
* the number of changes effected. This method is designed for inserting
|
273
|
+
* multiple records.
|
274
|
+
*
|
275
|
+
* records = [
|
276
|
+
* [1, 2, 3],
|
277
|
+
* [4, 5, 6]
|
278
|
+
* ]
|
279
|
+
* db.execute_multi_query('insert into foo values (?, ?, ?)', records)
|
280
|
+
*
|
281
|
+
*/
|
282
|
+
VALUE Database_execute_multi(VALUE self, VALUE sql, VALUE params_array) {
|
283
|
+
Database_t *db;
|
284
|
+
sqlite3_stmt *stmt;
|
285
|
+
|
286
|
+
if (RSTRING_LEN(sql) == 0) return Qnil;
|
287
|
+
|
288
|
+
// prepare query ctx
|
289
|
+
GetOpenDatabase(self, db);
|
290
|
+
prepare_single_stmt(db->sqlite3_db, &stmt, sql);
|
291
|
+
query_ctx ctx = { self, db->sqlite3_db, stmt, params_array };
|
292
|
+
|
293
|
+
return rb_ensure(SAFE(safe_execute_multi), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
|
294
|
+
}
|
295
|
+
|
266
296
|
/* call-seq:
|
267
297
|
* db.columns(sql) -> columns
|
268
298
|
*
|
@@ -325,6 +355,7 @@ VALUE Database_transaction_active_p(VALUE self) {
|
|
325
355
|
return sqlite3_get_autocommit(db->sqlite3_db) ? Qfalse : Qtrue;
|
326
356
|
}
|
327
357
|
|
358
|
+
#ifdef HAVE_SQLITE3_LOAD_EXTENSION
|
328
359
|
/* call-seq:
|
329
360
|
* db.load_extension(path) -> db
|
330
361
|
*
|
@@ -344,6 +375,7 @@ VALUE Database_load_extension(VALUE self, VALUE path) {
|
|
344
375
|
|
345
376
|
return self;
|
346
377
|
}
|
378
|
+
#endif
|
347
379
|
|
348
380
|
/* call-seq:
|
349
381
|
* db.prepare(sql) -> Extralite::PreparedStatement
|
@@ -371,13 +403,17 @@ void Init_ExtraliteDatabase() {
|
|
371
403
|
rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
|
372
404
|
rb_define_method(cDatabase, "query_single_column", Database_query_single_column, -1);
|
373
405
|
rb_define_method(cDatabase, "query_single_value", Database_query_single_value, -1);
|
406
|
+
rb_define_method(cDatabase, "execute_multi", Database_execute_multi, 2);
|
374
407
|
rb_define_method(cDatabase, "columns", Database_columns, 1);
|
375
408
|
|
376
409
|
rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
|
377
410
|
rb_define_method(cDatabase, "changes", Database_changes, 0);
|
378
411
|
rb_define_method(cDatabase, "filename", Database_filename, -1);
|
379
412
|
rb_define_method(cDatabase, "transaction_active?", Database_transaction_active_p, 0);
|
413
|
+
|
414
|
+
#ifdef HAVE_SQLITE3_LOAD_EXTENSION
|
380
415
|
rb_define_method(cDatabase, "load_extension", Database_load_extension, 1);
|
416
|
+
#endif
|
381
417
|
|
382
418
|
rb_define_method(cDatabase, "prepare", Database_prepare, 1);
|
383
419
|
|
data/ext/extralite/extconf.rb
CHANGED
@@ -102,7 +102,7 @@ have_func('sqlite3_enable_load_extension')
|
|
102
102
|
have_func('sqlite3_load_extension')
|
103
103
|
|
104
104
|
unless have_func('sqlite3_open_v2')
|
105
|
-
abort
|
105
|
+
abort 'Please use a newer version of SQLite3'
|
106
106
|
end
|
107
107
|
|
108
108
|
have_func('sqlite3_prepare_v2')
|
data/ext/extralite/extralite.h
CHANGED
@@ -46,6 +46,7 @@ typedef struct {
|
|
46
46
|
VALUE self;
|
47
47
|
sqlite3 *sqlite3_db;
|
48
48
|
sqlite3_stmt *stmt;
|
49
|
+
VALUE params;
|
49
50
|
} query_ctx;
|
50
51
|
|
51
52
|
VALUE safe_query_ary(query_ctx *ctx);
|
@@ -53,11 +54,14 @@ VALUE safe_query_hash(query_ctx *ctx);
|
|
53
54
|
VALUE safe_query_single_column(query_ctx *ctx);
|
54
55
|
VALUE safe_query_single_row(query_ctx *ctx);
|
55
56
|
VALUE safe_query_single_value(query_ctx *ctx);
|
57
|
+
VALUE safe_execute_multi(query_ctx *ctx);
|
56
58
|
VALUE safe_query_columns(query_ctx *ctx);
|
57
59
|
|
58
60
|
void prepare_single_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql);
|
59
61
|
void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql);
|
60
62
|
void bind_all_parameters(sqlite3_stmt *stmt, int argc, VALUE *argv);
|
63
|
+
void bind_all_parameters_from_object(sqlite3_stmt *stmt, VALUE obj);
|
64
|
+
int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db);
|
61
65
|
VALUE cleanup_stmt(query_ctx *ctx);
|
62
66
|
|
63
67
|
sqlite3 *Database_sqlite3_db(VALUE self);
|
@@ -53,7 +53,6 @@ VALUE PreparedStatement_initialize(VALUE self, VALUE db, VALUE sql) {
|
|
53
53
|
stmt->sqlite3_db = Database_sqlite3_db(db);
|
54
54
|
stmt->sql = sql;
|
55
55
|
|
56
|
-
// TODO: setup stmt
|
57
56
|
prepare_single_stmt(stmt->sqlite3_db, &stmt->stmt, sql);
|
58
57
|
|
59
58
|
return Qnil;
|
@@ -198,6 +197,32 @@ VALUE PreparedStatement_query_single_value(int argc, VALUE *argv, VALUE self) {
|
|
198
197
|
return PreparedStatement_perform_query(argc, argv, self, safe_query_single_value);
|
199
198
|
}
|
200
199
|
|
200
|
+
/* call-seq:
|
201
|
+
* stmt.execute_multi(params_array) -> changes
|
202
|
+
*
|
203
|
+
* Executes the prepared statment for each list of parameters in params_array.
|
204
|
+
* Returns the number of changes effected. This method is designed for inserting
|
205
|
+
* multiple records.
|
206
|
+
*
|
207
|
+
* stmt = db.prepare('insert into foo values (?, ?, ?)')
|
208
|
+
* records = [
|
209
|
+
* [1, 2, 3],
|
210
|
+
* [4, 5, 6]
|
211
|
+
* ]
|
212
|
+
* stmt.execute_multi_query(records)
|
213
|
+
*
|
214
|
+
*/
|
215
|
+
VALUE PreparedStatement_execute_multi(VALUE self, VALUE params_array) {
|
216
|
+
PreparedStatement_t *stmt;
|
217
|
+
GetPreparedStatement(self, stmt);
|
218
|
+
|
219
|
+
if (!stmt->stmt)
|
220
|
+
rb_raise(cError, "Prepared statement is closed");
|
221
|
+
|
222
|
+
query_ctx ctx = { self, stmt->sqlite3_db, stmt->stmt, params_array };
|
223
|
+
return safe_execute_multi(&ctx);
|
224
|
+
}
|
225
|
+
|
201
226
|
/* call-seq:
|
202
227
|
* stmt.database -> database
|
203
228
|
* stmt.db -> database
|
@@ -275,6 +300,7 @@ void Init_ExtralitePreparedStatement() {
|
|
275
300
|
rb_define_method(cPreparedStatement, "query_single_row", PreparedStatement_query_single_row, -1);
|
276
301
|
rb_define_method(cPreparedStatement, "query_single_column", PreparedStatement_query_single_column, -1);
|
277
302
|
rb_define_method(cPreparedStatement, "query_single_value", PreparedStatement_query_single_value, -1);
|
303
|
+
rb_define_method(cPreparedStatement, "execute_multi", PreparedStatement_execute_multi, 1);
|
278
304
|
|
279
305
|
rb_define_method(cPreparedStatement, "columns", PreparedStatement_columns, 0);
|
280
306
|
|
data/lib/extralite/version.rb
CHANGED
data/test/test_database.rb
CHANGED
@@ -207,6 +207,42 @@ end
|
|
207
207
|
assert_equal [{schema_version: 33}], @db.pragma(:schema_version)
|
208
208
|
assert_equal [{recursive_triggers: 1}], @db.pragma(:recursive_triggers)
|
209
209
|
end
|
210
|
+
|
211
|
+
def test_execute_multi
|
212
|
+
@db.query('create table foo (a, b, c)')
|
213
|
+
assert_equal [], @db.query('select * from foo')
|
214
|
+
|
215
|
+
records = [
|
216
|
+
[1, '2', 3],
|
217
|
+
['4', 5, 6]
|
218
|
+
]
|
219
|
+
|
220
|
+
changes = @db.execute_multi('insert into foo values (?, ?, ?)', records)
|
221
|
+
|
222
|
+
assert_equal 2, changes
|
223
|
+
assert_equal [
|
224
|
+
{ a: 1, b: '2', c: 3 },
|
225
|
+
{ a: '4', b: 5, c: 6 }
|
226
|
+
], @db.query('select * from foo')
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_execute_multi_single_values
|
230
|
+
@db.query('create table foo (bar)')
|
231
|
+
assert_equal [], @db.query('select * from foo')
|
232
|
+
|
233
|
+
records = [
|
234
|
+
'hi',
|
235
|
+
'bye'
|
236
|
+
]
|
237
|
+
|
238
|
+
changes = @db.execute_multi('insert into foo values (?)', records)
|
239
|
+
|
240
|
+
assert_equal 2, changes
|
241
|
+
assert_equal [
|
242
|
+
{ bar: 'hi' },
|
243
|
+
{ bar: 'bye' }
|
244
|
+
], @db.query('select * from foo')
|
245
|
+
end
|
210
246
|
end
|
211
247
|
|
212
248
|
class ScenarioTest < MiniTest::Test
|
@@ -181,4 +181,23 @@ end
|
|
181
181
|
|
182
182
|
assert_raises { p.query_single_value }
|
183
183
|
end
|
184
|
+
|
185
|
+
def test_prepared_statement_execute_multi
|
186
|
+
@db.query('create table foo (a, b, c)')
|
187
|
+
assert_equal [], @db.query('select * from foo')
|
188
|
+
|
189
|
+
records = [
|
190
|
+
[1, '2', 3],
|
191
|
+
['4', 5, 6]
|
192
|
+
]
|
193
|
+
|
194
|
+
p = @db.prepare('insert into foo values (?, ?, ?)')
|
195
|
+
changes = p.execute_multi(records)
|
196
|
+
|
197
|
+
assert_equal 2, changes
|
198
|
+
assert_equal [
|
199
|
+
{ a: 1, b: '2', c: 3 },
|
200
|
+
{ a: '4', b: 5, c: 6 }
|
201
|
+
], @db.query('select * from foo')
|
202
|
+
end
|
184
203
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: extralite-bundle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.19'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|