extralite 1.27 → 2.0

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: a027328ec4af0d01bc81706f4f8e1ea08770a1341d8a36953882244b2d20ed88
4
- data.tar.gz: 916fabb4a5328e93bcdcb8121b54aa3e958b87915e9b1c9027cccb44941837b9
3
+ metadata.gz: d399062a5652d11c0e969dfb74f6c998015980e8831e221d2aece7ddb6cfc83d
4
+ data.tar.gz: '0759cd16b15124934c52d5e2a3f36620b165f5a205aaea707c3d957503038173'
5
5
  SHA512:
6
- metadata.gz: fc4cd60e38d6e229d5d28ab6e28db3a16f7ddf1ffde53e3f5a97cacebe607158604421016bab34fa5bf96659ab4d0074fffb0f51712e4429d5529f59bbcdec0d
7
- data.tar.gz: db9905c1c839a4135fe4c1156b3991f8cad5ac438b5c70091be34e8afee7563371e19afe45cd835cec2f83dd26fc92e5855bf7c53c6ee473728b80caabb3e5e1
6
+ metadata.gz: 62ad641bd7661d326c3ba83eff0e7ecf5befdd12d7267608e748d6a7cf586d96e9f870cd179989cabb230b822e9d245ff9d1ed8877632ca33c7577858a639ded
7
+ data.tar.gz: 2984632a7bb790b354713396ac00856f0646c79aa22bb3a9bcd26995a127eb8cb86a5ca64efcbd91b1fcc8dd5a41f95e0bacefed7b5bacd477e9b689d79186a6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 2.0 2023-07-08
2
+
3
+ - Fix Sequel migrations (#8)
4
+ - Redesign prepared statement functionality (#24)
5
+ - Rewrite `Extralite::PreparedStatement` into `Extralite::Query` with breaking API changes
6
+ - Add `Extralite::Iterator` class for external iteration
7
+ - Add `Query#each_xxx`, `Query#to_a_xxx` method
8
+ - Add `Query#eof?` method
9
+
1
10
  # 1.27 2023-06-12
2
11
 
3
12
  - Fix execution of prepared statements in Sequel adapter (#23 @gschlager)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- extralite (1.27)
4
+ extralite (2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 Sharon Rosner
3
+ Copyright (c) 2023 Sharon Rosner
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  Extralite is a super fast, extra-lightweight (about 1300 lines of C-code)
11
11
  SQLite3 wrapper for Ruby. It provides a minimal set of methods for interacting
12
- with an SQLite3 database, as well as prepared statements.
12
+ with an SQLite3 database, as well as prepared queries (prepared statements).
13
13
 
14
14
  Extralite comes in two flavors: the `extralite` gem which uses the
15
15
  system-installed sqlite3 library, and the `extralite-bundle` gem which bundles
@@ -26,11 +26,11 @@ latest features and enhancements.
26
26
  as arrays, single row, single column, single value.
27
27
  - Prepared statements.
28
28
  - Parameter binding.
29
+ - External iteration - get single records or batches of records.
29
30
  - Use system-installed sqlite3, or the [bundled latest version of
30
31
  SQLite3](#installing-the-extralite-sqlite3-bundle).
31
32
  - Improved [concurrency](#concurrency) for multithreaded apps: the Ruby GVL is
32
33
  released while preparing SQL statements and while iterating over results.
33
- - Iterate over records with a block, or collect records into an array.
34
34
  - Automatically execute SQL strings containing multiple semicolon-separated
35
35
  queries (handy for creating/modifying schemas).
36
36
  - Execute the same query with multiple parameter lists (useful for inserting records).
@@ -111,11 +111,37 @@ db.query('select * from foo where bar = :bar', ':bar' => 42)
111
111
  db.execute_multi('insert into foo values (?)', ['bar', 'baz'])
112
112
  db.execute_multi('insert into foo values (?, ?)', [[1, 2], [3, 4]])
113
113
 
114
- # prepared statements
115
- stmt = db.prepare('select ? as foo, ? as bar') #=> Extralite::PreparedStatement
116
- stmt.query(1, 2) #=> [{ :foo => 1, :bar => 2 }]
117
- # PreparedStatement offers the same data access methods as the Database class,
118
- # but without the sql parameter.
114
+ # prepared queries
115
+ query = db.prepare('select ? as foo, ? as bar') #=> Extralite::Query
116
+ query.bind(1, 2) #=> [{ :foo => 1, :bar => 2 }]
117
+
118
+ query.next #=> next row in result_set (as hash)
119
+ query.next_hash #=> next row in result_set (as hash)
120
+ query.next_ary #=> next row in result_set (as array)
121
+ query.next_single_column #=> next row in result_set (as single value)
122
+
123
+ query.next(10) #=> next 10 rows in result_set (as hash)
124
+ query.next_hash(10) #=> next 10 rows in result_set (as hash)
125
+ query.next_ary(10) #=> next 10 rows in result_set (as array)
126
+ query.next_single_column(10) #=> next 10 rows in result_set (as single value)
127
+
128
+ query.to_a #=> all rows as array of hashes
129
+ query.to_a_hash #=> all rows as array of hashes
130
+ query.to_a_ary #=> all rows as array of arrays
131
+ query.to_a_single_column #=> all rows as array of single values
132
+
133
+ query.each { |r| ... } #=> iterate over all rows as hashes
134
+ query.each_hash { |r| ... } #=> iterate over all rows as hashes
135
+ query.each_ary { |r| ... } #=> iterate over all rows as arrays
136
+ query.each_single_column { |r| ... } #=> iterate over all rows as single columns
137
+
138
+ iterator = query.each #=> create enumerable iterator
139
+ iterator.next #=> next row
140
+ iterator.each { |r| ... } #=> iterate over all rows
141
+ values = iterator.map { |r| r[:foo] * 10 } #=> map all rows
142
+
143
+ iterator = query.each_ary #=> create enumerable iterator with rows as arrays
144
+ iterator = query.each_single_column #=> create enumerable iterator with single values
119
145
 
120
146
  # get last insert rowid
121
147
  rowid = db.last_insert_rowid
@@ -195,10 +221,10 @@ end
195
221
 
196
222
  Extralite provides methods for retrieving status information about the sqlite
197
223
  runtime, database-specific status and prepared statement-specific status,
198
- `Extralite.runtime_status`, `Database#status` and `PreparedStatement#status`
199
- respectively. You can also reset the high water mark for the specific status
200
- code by providing true as the reset argument. The status codes mirror those
201
- defined by the SQLite API. Some examples:
224
+ `Extralite.runtime_status`, `Database#status` and `Query#status` respectively.
225
+ You can also reset the high water mark for the specific status code by providing
226
+ true as the reset argument. The status codes mirror those defined by the SQLite
227
+ API. Some examples:
202
228
 
203
229
  ```ruby
204
230
  # The Extralite.runtime_status returns a tuple consisting of the current value
@@ -212,9 +238,9 @@ current, high_watermark = Extralite.runtime_status(Extralite::SQLITE_STATUS_MEMO
212
238
  # argument in order to reset the high watermark):
213
239
  current, high_watermark = db.status(Extralite::SQLITE_DBSTATUS_CACHE_USED)
214
240
 
215
- # The PreparedStatement#status method returns a single value (pass true as a
241
+ # The Query#status method returns a single value (pass true as a
216
242
  # second argument in order to reset the high watermark):
217
- value = stmt.status(Extralite::SQLITE_STMTSTATUS_RUN)
243
+ value = query.status(Extralite::SQLITE_STMTSTATUS_RUN)
218
244
  ```
219
245
 
220
246
  ### Working with Database Limits
@@ -302,7 +328,7 @@ large number of rows.
302
328
  |1K|502.1K rows/s|2.065M rows/s|__4.11x__|
303
329
  |100K|455.7K rows/s|2.511M rows/s|__5.51x__|
304
330
 
305
- ### Prepared Statements
331
+ ### Prepared Queries (Prepared Statements)
306
332
 
307
333
  [Benchmark source code](https://github.com/digital-fabric/extralite/blob/main/test/perf_prepared.rb)
308
334
 
data/TODO.md CHANGED
@@ -0,0 +1,5 @@
1
+ - Add option to open database in readonly mode, use sqlite3_open_v2
2
+ https://sqlite.org/c3ref/open.html
3
+
4
+ - Improve tracing
5
+ - Add `#inspect` method for `Database`, `Query`, `Iterator` classes
@@ -75,13 +75,11 @@ inline void bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value) {
75
75
  }
76
76
  }
77
77
 
78
- void bind_all_parameters(sqlite3_stmt *stmt, int argc, VALUE *argv) {
79
- for (int i = 0; i < argc; i++) {
80
- bind_parameter_value(stmt, i + 1, argv[i]);
81
- }
78
+ inline void bind_all_parameters(sqlite3_stmt *stmt, int argc, VALUE *argv) {
79
+ for (int i = 0; i < argc; i++) bind_parameter_value(stmt, i + 1, argv[i]);
82
80
  }
83
81
 
84
- void bind_all_parameters_from_object(sqlite3_stmt *stmt, VALUE obj) {
82
+ inline void bind_all_parameters_from_object(sqlite3_stmt *stmt, VALUE obj) {
85
83
  if (TYPE(obj) == T_ARRAY) {
86
84
  int count = RARRAY_LEN(obj);
87
85
  for (int i = 0; i < count; i++)
@@ -233,22 +231,23 @@ void *stmt_iterate_without_gvl(void *ptr) {
233
231
  return NULL;
234
232
  }
235
233
 
236
- int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db) {
237
- struct step_ctx ctx = {stmt, 0};
238
- rb_thread_call_without_gvl(stmt_iterate_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
239
- switch (ctx.rc) {
234
+ inline int stmt_iterate(query_ctx *ctx) {
235
+ struct step_ctx step_ctx = {ctx->stmt, 0};
236
+ rb_thread_call_without_gvl(stmt_iterate_without_gvl, (void *)&step_ctx, RUBY_UBF_IO, 0);
237
+ switch (step_ctx.rc) {
240
238
  case SQLITE_ROW:
241
239
  return 1;
242
240
  case SQLITE_DONE:
241
+ ctx->eof = 1;
243
242
  return 0;
244
243
  case SQLITE_BUSY:
245
244
  rb_raise(cBusyError, "Database is busy");
246
245
  case SQLITE_INTERRUPT:
247
246
  rb_raise(cInterruptError, "Query was interrupted");
248
247
  case SQLITE_ERROR:
249
- rb_raise(cSQLError, "%s", sqlite3_errmsg(db));
248
+ rb_raise(cSQLError, "%s", sqlite3_errmsg(ctx->sqlite3_db));
250
249
  default:
251
- rb_raise(cError, "%s", sqlite3_errmsg(db));
250
+ rb_raise(cError, "%s", sqlite3_errmsg(ctx->sqlite3_db));
252
251
  }
253
252
 
254
253
  return 0;
@@ -260,50 +259,61 @@ VALUE cleanup_stmt(query_ctx *ctx) {
260
259
  }
261
260
 
262
261
  VALUE safe_query_hash(query_ctx *ctx) {
263
- VALUE result = ctx->self;
264
- int yield_to_block = rb_block_given_p();
265
- VALUE row;
266
- int column_count;
267
- VALUE column_names;
268
-
269
- column_count = sqlite3_column_count(ctx->stmt);
270
- column_names = get_column_names(ctx->stmt, column_count);
271
-
272
- // block not given, so prepare the array of records to be returned
273
- if (!yield_to_block) result = rb_ary_new();
262
+ VALUE array = MULTI_ROW_P(ctx->mode) ? rb_ary_new() : Qnil;
263
+ VALUE row = Qnil;
264
+ int column_count = sqlite3_column_count(ctx->stmt);
265
+ VALUE column_names = get_column_names(ctx->stmt, column_count);
266
+ int row_count = 0;
274
267
 
275
- while (stmt_iterate(ctx->stmt, ctx->sqlite3_db)) {
268
+ while (stmt_iterate(ctx)) {
276
269
  row = row_to_hash(ctx->stmt, column_count, column_names);
277
- if (yield_to_block) rb_yield(row);
278
- else rb_ary_push(result, row);
270
+ row_count++;
271
+ switch (ctx->mode) {
272
+ case QUERY_YIELD:
273
+ rb_yield(row);
274
+ break;
275
+ case QUERY_MULTI_ROW:
276
+ rb_ary_push(array, row);
277
+ break;
278
+ case QUERY_SINGLE_ROW:
279
+ return row;
280
+ }
281
+ if (ctx->max_rows != ALL_ROWS && row_count >= ctx->max_rows)
282
+ return MULTI_ROW_P(ctx->mode) ? array : ctx->self;
279
283
  }
280
284
 
281
285
  RB_GC_GUARD(column_names);
282
286
  RB_GC_GUARD(row);
283
- RB_GC_GUARD(result);
284
- return result;
287
+ RB_GC_GUARD(array);
288
+ return MULTI_ROW_P(ctx->mode) ? array : Qnil;
285
289
  }
286
290
 
287
291
  VALUE safe_query_ary(query_ctx *ctx) {
288
- int column_count;
289
- VALUE result = ctx->self;
290
- int yield_to_block = rb_block_given_p();
291
- VALUE row;
292
-
293
- column_count = sqlite3_column_count(ctx->stmt);
294
-
295
- // block not given, so prepare the array of records to be returned
296
- if (!yield_to_block) result = rb_ary_new();
292
+ VALUE array = MULTI_ROW_P(ctx->mode) ? rb_ary_new() : Qnil;
293
+ VALUE row = Qnil;
294
+ int column_count = sqlite3_column_count(ctx->stmt);
295
+ int row_count = 0;
297
296
 
298
- while (stmt_iterate(ctx->stmt, ctx->sqlite3_db)) {
297
+ while (stmt_iterate(ctx)) {
299
298
  row = row_to_ary(ctx->stmt, column_count);
300
- if (yield_to_block) rb_yield(row);
301
- else rb_ary_push(result, row);
299
+ row_count++;
300
+ switch (ctx->mode) {
301
+ case QUERY_YIELD:
302
+ rb_yield(row);
303
+ break;
304
+ case QUERY_MULTI_ROW:
305
+ rb_ary_push(array, row);
306
+ break;
307
+ case QUERY_SINGLE_ROW:
308
+ return row;
309
+ }
310
+ if (ctx->max_rows != ALL_ROWS && row_count >= ctx->max_rows)
311
+ return MULTI_ROW_P(ctx->mode) ? array : ctx->self;
302
312
  }
303
313
 
304
314
  RB_GC_GUARD(row);
305
- RB_GC_GUARD(result);
306
- return result;
315
+ RB_GC_GUARD(array);
316
+ return MULTI_ROW_P(ctx->mode) ? array : Qnil;
307
317
  }
308
318
 
309
319
  VALUE safe_query_single_row(query_ctx *ctx) {
@@ -314,7 +324,7 @@ VALUE safe_query_single_row(query_ctx *ctx) {
314
324
  column_count = sqlite3_column_count(ctx->stmt);
315
325
  column_names = get_column_names(ctx->stmt, column_count);
316
326
 
317
- if (stmt_iterate(ctx->stmt, ctx->sqlite3_db))
327
+ if (stmt_iterate(ctx))
318
328
  row = row_to_hash(ctx->stmt, column_count, column_names);
319
329
 
320
330
  RB_GC_GUARD(row);
@@ -323,26 +333,33 @@ VALUE safe_query_single_row(query_ctx *ctx) {
323
333
  }
324
334
 
325
335
  VALUE safe_query_single_column(query_ctx *ctx) {
326
- int column_count;
327
- VALUE result = ctx->self;
328
- int yield_to_block = rb_block_given_p();
329
- VALUE value;
330
-
331
- column_count = sqlite3_column_count(ctx->stmt);
332
- if (column_count != 1)
333
- rb_raise(cError, "Expected query result to have 1 column");
336
+ VALUE array = MULTI_ROW_P(ctx->mode) ? rb_ary_new() : Qnil;
337
+ VALUE value = Qnil;
338
+ int column_count = sqlite3_column_count(ctx->stmt);
339
+ int row_count = 0;
334
340
 
335
- // block not given, so prepare the array of records to be returned
336
- if (!yield_to_block) result = rb_ary_new();
341
+ if (column_count != 1) rb_raise(cError, "Expected query result to have 1 column");
337
342
 
338
- while (stmt_iterate(ctx->stmt, ctx->sqlite3_db)) {
343
+ while (stmt_iterate(ctx)) {
339
344
  value = get_column_value(ctx->stmt, 0, sqlite3_column_type(ctx->stmt, 0));
340
- if (yield_to_block) rb_yield(value); else rb_ary_push(result, value);
345
+ row_count++;
346
+ switch (ctx->mode) {
347
+ case QUERY_YIELD:
348
+ rb_yield(value);
349
+ break;
350
+ case QUERY_MULTI_ROW:
351
+ rb_ary_push(array, value);
352
+ break;
353
+ case QUERY_SINGLE_ROW:
354
+ return value;
355
+ }
356
+ if (ctx->max_rows != ALL_ROWS && row_count >= ctx->max_rows)
357
+ return MULTI_ROW_P(ctx->mode) ? array : ctx->self;
341
358
  }
342
359
 
343
360
  RB_GC_GUARD(value);
344
- RB_GC_GUARD(result);
345
- return result;
361
+ RB_GC_GUARD(array);
362
+ return MULTI_ROW_P(ctx->mode) ? array : Qnil;
346
363
  }
347
364
 
348
365
  VALUE safe_query_single_value(query_ctx *ctx) {
@@ -353,7 +370,7 @@ VALUE safe_query_single_value(query_ctx *ctx) {
353
370
  if (column_count != 1)
354
371
  rb_raise(cError, "Expected query result to have 1 column");
355
372
 
356
- if (stmt_iterate(ctx->stmt, ctx->sqlite3_db))
373
+ if (stmt_iterate(ctx))
357
374
  value = get_column_value(ctx->stmt, 0, sqlite3_column_type(ctx->stmt, 0));
358
375
 
359
376
  RB_GC_GUARD(value);
@@ -369,7 +386,7 @@ VALUE safe_execute_multi(query_ctx *ctx) {
369
386
  sqlite3_clear_bindings(ctx->stmt);
370
387
  bind_all_parameters_from_object(ctx->stmt, RARRAY_AREF(ctx->params, i));
371
388
 
372
- while (stmt_iterate(ctx->stmt, ctx->sqlite3_db));
389
+ while (stmt_iterate(ctx));
373
390
  changes += sqlite3_changes(ctx->sqlite3_db);
374
391
  }
375
392
 
@@ -7,6 +7,7 @@ VALUE cSQLError;
7
7
  VALUE cBusyError;
8
8
  VALUE cInterruptError;
9
9
 
10
+ ID ID_bind;
10
11
  ID ID_call;
11
12
  ID ID_keys;
12
13
  ID ID_new;
@@ -155,7 +156,7 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
155
156
  RB_GC_GUARD(sql);
156
157
 
157
158
  bind_all_parameters(stmt, argc - 1, argv + 1);
158
- query_ctx ctx = { self, db->sqlite3_db, stmt };
159
+ query_ctx ctx = { self, db->sqlite3_db, stmt, Qnil, QUERY_MODE(QUERY_MULTI_ROW), ALL_ROWS };
159
160
 
160
161
  return rb_ensure(SAFE(call), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
161
162
  }
@@ -170,7 +171,7 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
170
171
  *
171
172
  * Query parameters to be bound to placeholders in the query can be specified as
172
173
  * a list of values or as a hash mapping parameter names to values. When
173
- * parameters are given as a least, the query should specify parameters using
174
+ * parameters are given as an array, the query should specify parameters using
174
175
  * `?`:
175
176
  *
176
177
  * db.query('select * from foo where x = ?', 42)
@@ -195,7 +196,7 @@ VALUE Database_query_hash(int argc, VALUE *argv, VALUE self) {
195
196
  *
196
197
  * Query parameters to be bound to placeholders in the query can be specified as
197
198
  * a list of values or as a hash mapping parameter names to values. When
198
- * parameters are given as a least, the query should specify parameters using
199
+ * parameters are given as an array, the query should specify parameters using
199
200
  * `?`:
200
201
  *
201
202
  * db.query_ary('select * from foo where x = ?', 42)
@@ -219,7 +220,7 @@ VALUE Database_query_ary(int argc, VALUE *argv, VALUE self) {
219
220
  *
220
221
  * Query parameters to be bound to placeholders in the query can be specified as
221
222
  * a list of values or as a hash mapping parameter names to values. When
222
- * parameters are given as a least, the query should specify parameters using
223
+ * parameters are given as an array, the query should specify parameters using
223
224
  * `?`:
224
225
  *
225
226
  * db.query_single_row('select * from foo where x = ?', 42)
@@ -244,7 +245,7 @@ VALUE Database_query_single_row(int argc, VALUE *argv, VALUE self) {
244
245
  *
245
246
  * Query parameters to be bound to placeholders in the query can be specified as
246
247
  * a list of values or as a hash mapping parameter names to values. When
247
- * parameters are given as a least, the query should specify parameters using
248
+ * parameters are given as an array, the query should specify parameters using
248
249
  * `?`:
249
250
  *
250
251
  * db.query_single_column('select x from foo where x = ?', 42)
@@ -268,7 +269,7 @@ VALUE Database_query_single_column(int argc, VALUE *argv, VALUE self) {
268
269
  *
269
270
  * Query parameters to be bound to placeholders in the query can be specified as
270
271
  * a list of values or as a hash mapping parameter names to values. When
271
- * parameters are given as a least, the query should specify parameters using
272
+ * parameters are given as an array, the query should specify parameters using
272
273
  * `?`:
273
274
  *
274
275
  * db.query_single_value('select x from foo where x = ?', 42)
@@ -296,7 +297,7 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
296
297
  * [1, 2, 3],
297
298
  * [4, 5, 6]
298
299
  * ]
299
- * db.execute_multi_query('insert into foo values (?, ?, ?)', records)
300
+ * db.execute_multi('insert into foo values (?, ?, ?)', records)
300
301
  *
301
302
  */
302
303
  VALUE Database_execute_multi(VALUE self, VALUE sql, VALUE params_array) {
@@ -308,7 +309,7 @@ VALUE Database_execute_multi(VALUE self, VALUE sql, VALUE params_array) {
308
309
  // prepare query ctx
309
310
  GetOpenDatabase(self, db);
310
311
  prepare_single_stmt(db->sqlite3_db, &stmt, sql);
311
- query_ctx ctx = { self, db->sqlite3_db, stmt, params_array };
312
+ query_ctx ctx = { self, db->sqlite3_db, stmt, params_array, QUERY_MODE(QUERY_MULTI_ROW), ALL_ROWS };
312
313
 
313
314
  return rb_ensure(SAFE(safe_execute_multi), (VALUE)&ctx, SAFE(cleanup_stmt), (VALUE)&ctx);
314
315
  }
@@ -398,12 +399,16 @@ VALUE Database_load_extension(VALUE self, VALUE path) {
398
399
  #endif
399
400
 
400
401
  /* call-seq:
401
- * db.prepare(sql) -> Extralite::PreparedStatement
402
+ * db.prepare(sql) -> Extralite::Query
402
403
  *
403
404
  * Creates a prepared statement with the given SQL query.
404
405
  */
405
- VALUE Database_prepare(VALUE self, VALUE sql) {
406
- return rb_funcall(cPreparedStatement, ID_new, 2, self, sql);
406
+ VALUE Database_prepare(int argc, VALUE *argv, VALUE self) {
407
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
408
+ VALUE query = rb_funcall(cQuery, ID_new, 2, self, argv[0]);
409
+ if (argc > 1) rb_funcallv(query, ID_bind, argc - 1, argv + 1);
410
+ RB_GC_GUARD(query);
411
+ return query;
407
412
  }
408
413
 
409
414
  /* call-seq:
@@ -540,6 +545,9 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
540
545
  backup_ctx ctx = { dst_db, dst_is_fn, backup, rb_block_given_p(), 0 };
541
546
  rb_ensure(SAFE(backup_safe_iterate), (VALUE)&ctx, SAFE(backup_cleanup), (VALUE)&ctx);
542
547
 
548
+ RB_GC_GUARD(src_name);
549
+ RB_GC_GUARD(dst_name);
550
+
543
551
  return self;
544
552
  }
545
553
 
@@ -717,7 +725,7 @@ void Init_ExtraliteDatabase(void) {
717
725
  rb_define_method(cDatabase, "interrupt", Database_interrupt, 0);
718
726
  rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
719
727
  rb_define_method(cDatabase, "limit", Database_limit, -1);
720
- rb_define_method(cDatabase, "prepare", Database_prepare, 1);
728
+ rb_define_method(cDatabase, "prepare", Database_prepare, -1);
721
729
  rb_define_method(cDatabase, "query", Database_query_hash, -1);
722
730
  rb_define_method(cDatabase, "query_ary", Database_query_ary, -1);
723
731
  rb_define_method(cDatabase, "query_hash", Database_query_hash, -1);
@@ -742,6 +750,7 @@ void Init_ExtraliteDatabase(void) {
742
750
  rb_gc_register_mark_object(cBusyError);
743
751
  rb_gc_register_mark_object(cInterruptError);
744
752
 
753
+ ID_bind = rb_intern("bind");
745
754
  ID_call = rb_intern("call");
746
755
  ID_keys = rb_intern("keys");
747
756
  ID_new = rb_intern("new");
@@ -4,13 +4,13 @@ if ENV['EXTRALITE_BUNDLE']
4
4
  require_relative('extconf-bundle')
5
5
  else
6
6
  ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
7
-
7
+
8
8
  require 'mkmf'
9
-
9
+
10
10
  # :stopdoc:
11
-
11
+
12
12
  RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
13
-
13
+
14
14
  ldflags = cppflags = nil
15
15
  if RbConfig::CONFIG["host_os"] =~ /darwin/
16
16
  begin
@@ -25,7 +25,7 @@ else
25
25
  cppflags = "#{brew_prefix}/include"
26
26
  pkg_conf = "#{brew_prefix}/lib/pkgconfig"
27
27
  end
28
-
28
+
29
29
  # pkg_config should be less error prone than parsing compiler
30
30
  # commandline options, but we need to set default ldflags and cpp flags
31
31
  # in case the user doesn't have pkg-config installed
@@ -33,13 +33,13 @@ else
33
33
  rescue
34
34
  end
35
35
  end
36
-
36
+
37
37
  if with_config('sqlcipher')
38
38
  pkg_config("sqlcipher")
39
39
  else
40
40
  pkg_config("sqlite3")
41
41
  end
42
-
42
+
43
43
  # --with-sqlite3-{dir,include,lib}
44
44
  if with_config('sqlcipher')
45
45
  $CFLAGS << ' -DUSING_SQLCIPHER'
@@ -47,15 +47,15 @@ else
47
47
  else
48
48
  dir_config("sqlite3", cppflags, ldflags)
49
49
  end
50
-
50
+
51
51
  if RbConfig::CONFIG["host_os"] =~ /mswin/
52
52
  $CFLAGS << ' -W3'
53
53
  end
54
-
54
+
55
55
  if RUBY_VERSION < '2.7'
56
56
  $CFLAGS << ' -DTAINTING_SUPPORT'
57
57
  end
58
-
58
+
59
59
  # @!visibility private
60
60
  def asplode missing
61
61
  if RUBY_PLATFORM =~ /mingw|mswin/
@@ -70,25 +70,25 @@ else
70
70
  error
71
71
  end
72
72
  end
73
-
73
+
74
74
  asplode('sqlite3.h') unless find_header 'sqlite3.h'
75
75
  find_library 'pthread', 'pthread_create' # 1.8 support. *shrug*
76
-
76
+
77
77
  have_library 'dl' # for static builds
78
-
78
+
79
79
  if with_config('sqlcipher')
80
80
  asplode('sqlcipher') unless find_library 'sqlcipher', 'sqlite3_libversion_number'
81
81
  else
82
82
  asplode('sqlite3') unless find_library 'sqlite3', 'sqlite3_libversion_number'
83
83
  end
84
-
84
+
85
85
  have_func('sqlite3_enable_load_extension')
86
86
  have_func('sqlite3_load_extension')
87
87
  have_func('sqlite3_prepare_v2')
88
88
  have_func('sqlite3_error_offset')
89
-
89
+
90
90
  $defs << "-DEXTRALITE_NO_BUNDLE"
91
-
91
+
92
92
  dir_config('extralite_ext')
93
93
  create_makefile('extralite_ext')
94
94
  end