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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 962fb4dc86efd1082438fa17a2ac74248c6a23b46e943d25b8e9c04ae257acc0
4
- data.tar.gz: bc8a41fdc0b3765b5a3c9cf99761e13443b8c3dbb661059190df615189026f6d
3
+ metadata.gz: 6f614b88b731209821d211b3e54481829e60a374881d0e33bdd29e4510654719
4
+ data.tar.gz: 4c1ba63f851ac3a757e7cd3b72af4be2fc95855c3d6529d9f39f4613ae8b72f2
5
5
  SHA512:
6
- metadata.gz: 7d9646dc86984688d99b515c75ee5c42bb1eed87ef97502b82abf2cbe14a8c766d1f95ca7696d8a5a563a36109a2e6f4a85e05bb8fc4552c7b2f316ac23ce183
7
- data.tar.gz: a17dac12d74d7894a708758a4334ccf45fecb59d541745a196e62553c8f2bbafba7dc3f21f5d77c5d161e5dd4ebba69e9e65e8e55f365dce564f3a3d0fbf57ec
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- extralite (1.17)
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
  }
@@ -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
 
@@ -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 "Please use a newer version of SQLite3"
105
+ abort 'Please use a newer version of SQLite3'
106
106
  end
107
107
 
108
108
  have_func('sqlite3_prepare_v2')
@@ -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
 
@@ -1,3 +1,3 @@
1
1
  module Extralite
2
- VERSION = '1.17'
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,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: extralite-bundle
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.17'
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-10-31 00:00:00.000000000 Z
11
+ date: 2022-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler