extralite 1.17 → 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: 95bc7f2b8567ffcee4bdf29dfdde7f078b3d8f588546ca4d20d7f6f1b507ab71
4
- data.tar.gz: 8c084dbab358e67be76e040bc0272caed27ab369c64b95bff05fa89f748c845f
3
+ metadata.gz: f2fbd9028327105949e5f4fcc225ccb4ec89cdb2f324d949c17c4690f8b4653f
4
+ data.tar.gz: 226c07bfa8e8848dc322399ee386e8f15e6bd9e41c6912850709b1cc8fd24ad3
5
5
  SHA512:
6
- metadata.gz: ff1c0e65d4c7c342f67d63bf648119ee4c0113ee1023156462c8366868b4c0a3d0489f31025c909aecb02a22328009714bac9996495d9ad0c2f11ac4cd41ba2d
7
- data.tar.gz: 5ea37a2254f876e1318c1cf98e4af56c377a6baad3ecca24e9059da997df7a3409d74f929af69dc1f8633deb3c07a16daf78fcf79c4bfed56180957806d1d2b6
6
+ metadata.gz: ca3c24e4be2cedee6d20101ce56018ed9f037cbe29d4e5c6fb63116a77c988390992b770586a1973acc0c42a521389369ed79cf1553464e5f775fe420c9bf421
7
+ data.tar.gz: f9ffb7dbe2a5d00ea2c15cca1407462897590f299acecf88de6a040f463f4f28715ae98ae34a6a0539f97b1cf2f12902803217517014661208b6238618013287
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
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