extralite 1.18 → 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 +5 -0
- data/Gemfile.lock +1 -1
- data/README.md +5 -0
- data/ext/extralite/common.c +31 -3
- data/ext/extralite/database.c +29 -0
- data/ext/extralite/extralite.h +4 -0
- data/ext/extralite/prepared_statement.c +27 -0
- data/lib/extralite/version.rb +1 -1
- data/test/test_database.rb +36 -0
- data/test/test_prepared_statement.rb +19 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f2fbd9028327105949e5f4fcc225ccb4ec89cdb2f324d949c17c4690f8b4653f
|
|
4
|
+
data.tar.gz: 226c07bfa8e8848dc322399ee386e8f15e6bd9e41c6912850709b1cc8fd24ad3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca3c24e4be2cedee6d20101ce56018ed9f037cbe29d4e5c6fb63116a77c988390992b770586a1973acc0c42a521389369ed79cf1553464e5f775fe420c9bf421
|
|
7
|
+
data.tar.gz: f9ffb7dbe2a5d00ea2c15cca1407462897590f299acecf88de6a040f463f4f28715ae98ae34a6a0539f97b1cf2f12902803217517014661208b6238618013287
|
data/CHANGELOG.md
CHANGED
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
|
@@ -265,6 +265,34 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
|
|
|
265
265
|
return Database_perform_query(argc, argv, self, safe_query_single_value);
|
|
266
266
|
}
|
|
267
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
|
+
|
|
268
296
|
/* call-seq:
|
|
269
297
|
* db.columns(sql) -> columns
|
|
270
298
|
*
|
|
@@ -375,6 +403,7 @@ void Init_ExtraliteDatabase() {
|
|
|
375
403
|
rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
|
|
376
404
|
rb_define_method(cDatabase, "query_single_column", Database_query_single_column, -1);
|
|
377
405
|
rb_define_method(cDatabase, "query_single_value", Database_query_single_value, -1);
|
|
406
|
+
rb_define_method(cDatabase, "execute_multi", Database_execute_multi, 2);
|
|
378
407
|
rb_define_method(cDatabase, "columns", Database_columns, 1);
|
|
379
408
|
|
|
380
409
|
rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
|
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);
|
|
@@ -197,6 +197,32 @@ VALUE PreparedStatement_query_single_value(int argc, VALUE *argv, VALUE self) {
|
|
|
197
197
|
return PreparedStatement_perform_query(argc, argv, self, safe_query_single_value);
|
|
198
198
|
}
|
|
199
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
|
+
|
|
200
226
|
/* call-seq:
|
|
201
227
|
* stmt.database -> database
|
|
202
228
|
* stmt.db -> database
|
|
@@ -274,6 +300,7 @@ void Init_ExtralitePreparedStatement() {
|
|
|
274
300
|
rb_define_method(cPreparedStatement, "query_single_row", PreparedStatement_query_single_row, -1);
|
|
275
301
|
rb_define_method(cPreparedStatement, "query_single_column", PreparedStatement_query_single_column, -1);
|
|
276
302
|
rb_define_method(cPreparedStatement, "query_single_value", PreparedStatement_query_single_value, -1);
|
|
303
|
+
rb_define_method(cPreparedStatement, "execute_multi", PreparedStatement_execute_multi, 1);
|
|
277
304
|
|
|
278
305
|
rb_define_method(cPreparedStatement, "columns", PreparedStatement_columns, 0);
|
|
279
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
|