extralite 1.18 → 1.19

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a40752d79cd5d28d8c9367c973f86b7e6a80d05e3878b5c70a6a98e0a70f3838
4
- data.tar.gz: d72e040c85a2e34a07ea513fd7b28cc558a0b8c71a8b4f211b21aa3a9f492387
3
+ metadata.gz: f2fbd9028327105949e5f4fcc225ccb4ec89cdb2f324d949c17c4690f8b4653f
4
+ data.tar.gz: 226c07bfa8e8848dc322399ee386e8f15e6bd9e41c6912850709b1cc8fd24ad3
5
5
  SHA512:
6
- metadata.gz: 4bc536fb4a5590fbae92abb1f99189f57812f5440addb49964908fcb6c45f1bfea575fc2421bfd4ddddcaaf7d00e9285ea3db4db9162afe4df71c27349dd1342
7
- data.tar.gz: a1a158d144c3f97f3bd281ba050419d42c95566408cb168f35f3a0fe8d328705f662fbc1c9ac22e788a44f1120ff90db7e237e8ad1f2505b29a643c05589b331
6
+ metadata.gz: ca3c24e4be2cedee6d20101ce56018ed9f037cbe29d4e5c6fb63116a77c988390992b770586a1973acc0c42a521389369ed79cf1553464e5f775fe420c9bf421
7
+ data.tar.gz: f9ffb7dbe2a5d00ea2c15cca1407462897590f299acecf88de6a040f463f4f28715ae98ae34a6a0539f97b1cf2f12902803217517014661208b6238618013287
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 1.19 2022-12-01
2
+
3
+ - Add `Database#execute_multi`
4
+ - Add `PreparedStatement#execute_multi`
5
+
1
6
  ## 1.18 2022-12-01
2
7
 
3
8
  - Fix usage with system sqlite3 lib where `load_extension` is disabled
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- extralite (1.18)
4
+ extralite (1.19)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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 }]
@@ -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
- static inline int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db) {
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); else rb_ary_push(result, 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); else rb_ary_push(result, 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
  }
@@ -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);
@@ -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
 
@@ -1,3 +1,3 @@
1
1
  module Extralite
2
- VERSION = '1.18'
2
+ VERSION = '1.19'
3
3
  end
@@ -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,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: extralite
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.18'
4
+ version: '1.19'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner