extralite-bundle 1.21 → 1.23
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +1 -1
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +1 -1
- data/README.md +151 -90
- data/ext/extralite/common.c +4 -4
- data/ext/extralite/database.c +98 -3
- data/ext/extralite/extralite.h +4 -0
- data/ext/extralite/prepared_statement.c +3 -0
- data/lib/extralite/sqlite3_constants.rb +50 -0
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +15 -52
- data/test/test_database.rb +86 -0
- data/test/test_prepared_statement.rb +5 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6622a72a5d4e7ea9670be11b21708f2f8eedda7d94e870ea283d6e8b0d13471e
|
4
|
+
data.tar.gz: 1eed8cd7ca3fe7844dead0024463efe285f4979eef2be94741c96bf42b447a42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3300b9369bffded248aa4291db0f18da92a2c95015b0fbd4afc668c2783c223cb72d4cfb207ca1dace057a41c3e61a38ee37e0b8ffe5a1199a147143374c9f6d
|
7
|
+
data.tar.gz: 17d532b9c92238f3b94f073b2e7c13d793a872511778f7138e371a8e0a8f4e776d830a00874df203de514d2143e8340d42f479e2f267105a907226892540d74b
|
data/.github/workflows/test.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
# 1.23 2023-01-26
|
2
|
+
|
3
|
+
- Add `Database#trace` (#21)
|
4
|
+
- Add `Database#total_changes` (#20)
|
5
|
+
- Add `Database#busy_timeout=` (#19)
|
6
|
+
- Add `Database#limit` (#16)
|
7
|
+
- Improve error handling
|
8
|
+
|
9
|
+
# 1.22 2023-01-23
|
10
|
+
|
11
|
+
- Improve documentation (#17)
|
12
|
+
|
1
13
|
# 1.21 2023-01-23
|
2
14
|
|
3
15
|
- Update bundled sqlite to version 3.40.1 (#18)
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,64 +1,42 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
<p align="center">
|
8
|
-
<a href="http://rubygems.org/gems/extralite">
|
9
|
-
<img src="https://badge.fury.io/rb/extralite.svg" alt="Ruby gem">
|
10
|
-
</a>
|
11
|
-
<a href="https://github.com/digital-fabric/extralite/actions?query=workflow%3ATests">
|
12
|
-
<img src="https://github.com/digital-fabric/extralite/workflows/Tests/badge.svg" alt="Tests">
|
13
|
-
</a>
|
14
|
-
<a href="https://github.com/digital-fabric/extralite/blob/master/LICENSE">
|
15
|
-
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
|
16
|
-
</a>
|
17
|
-
</p>
|
18
|
-
|
19
|
-
<p align="center">
|
20
|
-
<a href="https://www.rubydoc.info/gems/extralite">DOCS</a> |
|
21
|
-
<a href="https://noteflakes.com/articles/2021-12-15-extralite">BLOG POST</a>
|
22
|
-
</p>
|
1
|
+
# Extralite - a Super Fast Ruby Gem for Working with SQLite3 Databases
|
2
|
+
|
3
|
+
* Source code: https://github.com/digital-fabric/extralite
|
4
|
+
* Documentation: http://www.rubydoc.info/gems/extralite
|
5
|
+
|
6
|
+
[](https://rubygems.org/gems/extralite) [](https://github.com/digital-fabric/extralite/actions?query=workflow%3ATests) [](https://github.com/digital-fabric/extralite/blob/master/LICENSE)
|
23
7
|
|
24
8
|
## What is Extralite?
|
25
9
|
|
26
|
-
Extralite is a fast, extra-lightweight (about
|
27
|
-
wrapper for Ruby. It provides a minimal set of methods for interacting
|
28
|
-
SQLite3 database, as well as prepared statements.
|
10
|
+
Extralite is a super fast, extra-lightweight (about 1300 lines of C-code)
|
11
|
+
SQLite3 wrapper for Ruby. It provides a minimal set of methods for interacting
|
12
|
+
with an SQLite3 database, as well as prepared statements.
|
29
13
|
|
30
14
|
Extralite comes in two flavors: the `extralite` gem which uses the
|
31
15
|
system-installed sqlite3 library, and the `extralite-bundle` gem which bundles
|
32
16
|
the latest version of SQLite
|
33
|
-
([3.
|
17
|
+
([3.40.1](https://sqlite.org/releaselog/3_40_1.html)), offering access to the
|
34
18
|
latest features and enhancements.
|
35
19
|
|
36
20
|
## Features
|
37
21
|
|
22
|
+
- Super fast - [up to 11x faster](#performance) than the
|
23
|
+
[sqlite3](https://github.com/sparklemotion/sqlite3-ruby) gem (see also
|
24
|
+
[comparison](#why-not-just-use-the-sqlite3-gem).)
|
38
25
|
- A variety of methods for different data access patterns: rows as hashes, rows
|
39
26
|
as arrays, single row, single column, single value.
|
40
27
|
- Prepared statements.
|
28
|
+
- Parameter binding.
|
41
29
|
- Use system-installed sqlite3, or the [bundled latest version of
|
42
30
|
SQLite3](#installing-the-extralite-sqlite3-bundle).
|
43
|
-
- Super fast - [up to 12.5x faster](#performance) than the
|
44
|
-
[sqlite3](https://github.com/sparklemotion/sqlite3-ruby) gem (see also
|
45
|
-
[comparison](#why-not-just-use-the-sqlite3-gem).)
|
46
31
|
- Improved [concurrency](#concurrency) for multithreaded apps: the Ruby GVL is
|
47
32
|
released while preparing SQL statements and while iterating over results.
|
48
33
|
- Iterate over records with a block, or collect records into an array.
|
49
|
-
- Parameter binding.
|
50
34
|
- Automatically execute SQL strings containing multiple semicolon-separated
|
51
35
|
queries (handy for creating/modifying schemas).
|
52
|
-
- Get last insert rowid.
|
53
|
-
- Get number of rows changed by last query.
|
54
36
|
- Execute the same query with multiple parameter lists (useful for inserting records).
|
55
37
|
- Load extensions (loading of extensions is autmatically enabled. You can find
|
56
38
|
some useful extensions here: https://github.com/nalgeon/sqlean.)
|
57
39
|
- Includes a [Sequel adapter](#usage-with-sequel).
|
58
|
-
- Other features:
|
59
|
-
- Backup databases
|
60
|
-
- Interrupt long-running queries (from another thread)
|
61
|
-
- Get runtime status, database status and prepared statement status values.
|
62
40
|
|
63
41
|
## Installation
|
64
42
|
|
@@ -70,20 +48,21 @@ gem 'extralite'
|
|
70
48
|
|
71
49
|
You can also run `gem install extralite` if you just want to check it out.
|
72
50
|
|
73
|
-
### Installing the Extralite-SQLite3
|
51
|
+
### Installing the Extralite-SQLite3 Bundle
|
74
52
|
|
75
53
|
If you don't have sqlite3 installed on your system, do not want to use the
|
76
54
|
system-installed version of SQLite3, or would like to use the latest version of
|
77
55
|
SQLite3, you can install the `extralite-bundle` gem, which integrates the
|
78
56
|
SQLite3 source code.
|
79
57
|
|
80
|
-
> **Important note**: The `extralite-bundle` will take a while to install
|
81
|
-
> modest machine it takes about a minute), due to the size of the sqlite3
|
58
|
+
> **Important note**: The `extralite-bundle` gem will take a while to install
|
59
|
+
> (on my modest machine it takes about a minute), due to the size of the sqlite3
|
60
|
+
> code.
|
82
61
|
|
83
62
|
Usage of the `extralite-bundle` gem is identical to the usage of the normal
|
84
|
-
`extralite` gem.
|
63
|
+
`extralite` gem, using `require 'extralite'` to load the gem.
|
85
64
|
|
86
|
-
##
|
65
|
+
## Synopsis
|
87
66
|
|
88
67
|
```ruby
|
89
68
|
require 'extralite'
|
@@ -144,6 +123,9 @@ rowid = db.last_insert_rowid
|
|
144
123
|
# get number of rows changed in last query
|
145
124
|
number_of_rows_affected = db.changes
|
146
125
|
|
126
|
+
# get column names for the given sql
|
127
|
+
db.columns('select a, b, c from foo') => [:a, :b, :c]
|
128
|
+
|
147
129
|
# get db filename
|
148
130
|
db.filename #=> "/tmp/my.db"
|
149
131
|
|
@@ -163,6 +145,114 @@ db.close
|
|
163
145
|
db.closed? #=> true
|
164
146
|
```
|
165
147
|
|
148
|
+
## More Features
|
149
|
+
|
150
|
+
### Interrupting Long-running Queries
|
151
|
+
|
152
|
+
When running long-running queries, you can use `Database#interrupt` to interrupt
|
153
|
+
the query:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
timeout_thread = Thread.new do
|
157
|
+
sleep 10
|
158
|
+
db.interrupt
|
159
|
+
end
|
160
|
+
|
161
|
+
result = begin
|
162
|
+
db.query(super_slow_sql)
|
163
|
+
rescue Extralite::InterruptError
|
164
|
+
nil
|
165
|
+
ensure
|
166
|
+
timeout_thread.kill
|
167
|
+
timeout_thread.join
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
171
|
+
### Creating Backups
|
172
|
+
|
173
|
+
You can use `Database#backup` to create backup copies of a database. The
|
174
|
+
`#backup` method takes either a filename or a database instance:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
# with a filename
|
178
|
+
db.backup('backup.db')
|
179
|
+
|
180
|
+
# with an instance
|
181
|
+
target = Extralite::Database.new('backup.db')
|
182
|
+
db.backup(target)
|
183
|
+
```
|
184
|
+
|
185
|
+
For big databases, you can also track the backup progress by providing a block
|
186
|
+
that takes two arguments - the number of remaining pages, and the total number pages:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
db.backup('backup.db') do |remaining, total|
|
190
|
+
puts "backup progress: #{(remaining.to_f/total * 100).round}%"
|
191
|
+
end
|
192
|
+
```
|
193
|
+
|
194
|
+
### Retrieving Status Information
|
195
|
+
|
196
|
+
Extralite provides methods for retrieving status information about the sqlite
|
197
|
+
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:
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
# The Extralite.runtime_status returns a tuple consisting of the current value
|
205
|
+
# and the high water mark value.
|
206
|
+
current, high_watermark = Extralite.runtime_status(Extralite::SQLITE_STATUS_MEMORY_USED)
|
207
|
+
|
208
|
+
# To reset the high water mark, pass true as a second argument:
|
209
|
+
current, high_watermark = Extralite.runtime_status(Extralite::SQLITE_STATUS_MEMORY_USED, true)
|
210
|
+
|
211
|
+
# Similarly, you can interrogate a database's status (pass true as a second
|
212
|
+
# argument in order to reset the high watermark):
|
213
|
+
current, high_watermark = db.status(Extralite::SQLITE_DBSTATUS_CACHE_USED)
|
214
|
+
|
215
|
+
# The PreparedStatement#status method returns a single value (pass true as a
|
216
|
+
# second argument in order to reset the high watermark):
|
217
|
+
value = stmt.status(Extralite::SQLITE_STMTSTATUS_RUN)
|
218
|
+
```
|
219
|
+
|
220
|
+
### Working with Database Limits
|
221
|
+
|
222
|
+
The `Database#limit` can be used to get and set various database limits, as
|
223
|
+
[discussed in the SQLite docs](https://www.sqlite.org/limits.html):
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
# get limit
|
227
|
+
value = db.limit(Extralite::SQLITE_LIMIT_ATTACHED)
|
228
|
+
|
229
|
+
# set limit
|
230
|
+
db.limit(Extralite::SQLITE_LIMIT_ATTACHED, new_value)
|
231
|
+
```
|
232
|
+
|
233
|
+
### Setting the Busy Timeout
|
234
|
+
|
235
|
+
When accessing a database concurrently it can be handy to set a busy timeout, in
|
236
|
+
order to not have to deal with rescuing `Extralite::BusyError` exceptions. The
|
237
|
+
timeout is given in seconds:
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
db.busy_timeout = 5
|
241
|
+
```
|
242
|
+
|
243
|
+
### Tracing SQL Statements
|
244
|
+
|
245
|
+
To trace all SQL statements executed on the database, pass a block to
|
246
|
+
`Database#trace`. To disable tracing, call `Database#trace` without a block:
|
247
|
+
|
248
|
+
```ruby
|
249
|
+
# enable tracing
|
250
|
+
db.trace { |sql| puts sql: sql }
|
251
|
+
|
252
|
+
# disable tracing
|
253
|
+
db.trace
|
254
|
+
```
|
255
|
+
|
166
256
|
## Usage with Sequel
|
167
257
|
|
168
258
|
Extralite includes an adapter for
|
@@ -177,82 +267,53 @@ p articles.to_a
|
|
177
267
|
|
178
268
|
(Make sure you include `extralite` as a dependency in your `Gemfile`.)
|
179
269
|
|
180
|
-
## Why not just use the sqlite3 gem?
|
181
|
-
|
182
|
-
The [sqlite3-ruby](https://github.com/sparklemotion/sqlite3-ruby) gem is a
|
183
|
-
popular, solid, well-maintained project, used by thousands of developers. I've
|
184
|
-
been doing a lot of work with SQLite3 databases lately, and wanted to have a
|
185
|
-
simpler API that gives me query results in a variety of ways. Thus extralite was
|
186
|
-
born.
|
187
|
-
|
188
|
-
Extralite is quite a bit [faster](#performance) than sqlite3-ruby and is also
|
189
|
-
[thread-friendly](#concurrency). On the other hand, Extralite does not have
|
190
|
-
support for defining custom functions, aggregates and collations. If you're
|
191
|
-
using any of those features, you'll have to stick to sqlite3-ruby.
|
192
|
-
|
193
|
-
Here's a table summarizing the differences between the two gems:
|
194
|
-
|
195
|
-
| |sqlite3-ruby|Extralite|
|
196
|
-
|-|-|-|
|
197
|
-
|SQLite3 dependency|depends on OS-installed libsqlite3|Use either system sqlite3 or [bundled latest version of SQLite3](#installing-the-extralite-sqlite3-bundle)|
|
198
|
-
|API design|multiple classes|single class|
|
199
|
-
|Query results|row as hash, row as array, single row, single value|row as hash, row as array, __single column__, single row, single value|
|
200
|
-
|Execute multiple statements|separate API (#execute_batch)|integrated|
|
201
|
-
|Prepared statements|yes|yes|
|
202
|
-
|custom functions in Ruby|yes|no|
|
203
|
-
|custom collations|yes|no|
|
204
|
-
|custom aggregate functions|yes|no|
|
205
|
-
|Multithread friendly|no|[yes](#concurrency)|
|
206
|
-
|Code size|~2650LoC|~600LoC|
|
207
|
-
|Performance|1x|1.5x to 12.5x (see [below](#performance))|
|
208
|
-
|
209
270
|
## Concurrency
|
210
271
|
|
211
272
|
Extralite releases the GVL while making blocking calls to the sqlite3 library,
|
212
273
|
that is while preparing SQL statements and fetching rows. Releasing the GVL
|
213
274
|
allows other threads to run while the sqlite3 library is busy compiling SQL into
|
214
|
-
bytecode, or fetching the next row. This does not
|
215
|
-
performance:
|
275
|
+
bytecode, or fetching the next row. This *does not* hurt Extralite's
|
276
|
+
performance, as you can see:
|
216
277
|
|
217
278
|
## Performance
|
218
279
|
|
219
280
|
A benchmark script is included, creating a table of various row counts, then
|
220
281
|
fetching the entire table using either `sqlite3` or `extralite`. This benchmark
|
221
|
-
shows Extralite to be up to ~
|
282
|
+
shows Extralite to be up to ~11 times faster than `sqlite3` when fetching a
|
222
283
|
large number of rows.
|
223
284
|
|
224
|
-
### Rows as
|
285
|
+
### Rows as Hashes
|
225
286
|
|
226
287
|
[Benchmark source code](https://github.com/digital-fabric/extralite/blob/main/test/perf_hash.rb)
|
227
288
|
|
228
|
-
|Row count|sqlite3
|
289
|
+
|Row count|sqlite3 1.6.0|Extralite 1.21|Advantage|
|
229
290
|
|-:|-:|-:|-:|
|
230
|
-
|10|
|
231
|
-
|1K|
|
232
|
-
|100K|
|
291
|
+
|10|63.7K rows/s|94.0K rows/s|__1.48x__|
|
292
|
+
|1K|299.2K rows/s|1.983M rows/s|__6.63x__|
|
293
|
+
|100K|185.4K rows/s|2.033M rows/s|__10.97x__|
|
233
294
|
|
234
|
-
### Rows as
|
295
|
+
### Rows as Arrays
|
235
296
|
|
236
297
|
[Benchmark source code](https://github.com/digital-fabric/extralite/blob/main/test/perf_ary.rb)
|
237
298
|
|
238
|
-
|Row count|sqlite3
|
299
|
+
|Row count|sqlite3 1.6.0|Extralite 1.21|Advantage|
|
239
300
|
|-:|-:|-:|-:|
|
240
|
-
|10|
|
241
|
-
|1K|
|
242
|
-
|100K|
|
301
|
+
|10|71.2K rows/s|92.1K rows/s|__1.29x__|
|
302
|
+
|1K|502.1K rows/s|2.065M rows/s|__4.11x__|
|
303
|
+
|100K|455.7K rows/s|2.511M rows/s|__5.51x__|
|
243
304
|
|
244
|
-
### Prepared
|
305
|
+
### Prepared Statements
|
245
306
|
|
246
307
|
[Benchmark source code](https://github.com/digital-fabric/extralite/blob/main/test/perf_prepared.rb)
|
247
308
|
|
248
|
-
|Row count|sqlite3
|
309
|
+
|Row count|sqlite3 1.6.0|Extralite 1.21|Advantage|
|
249
310
|
|-:|-:|-:|-:|
|
250
|
-
|10|
|
251
|
-
|1K|
|
252
|
-
|100K|
|
311
|
+
|10|232.2K rows/s|741.6K rows/s|__3.19x__|
|
312
|
+
|1K|299.8K rows/s|2386.0M rows/s|__7.96x__|
|
313
|
+
|100K|183.1K rows/s|1.893M rows/s|__10.34x__|
|
253
314
|
|
254
|
-
As those benchmarks show, Extralite is capabale of reading up to
|
255
|
-
when fetching rows as arrays, and up to
|
315
|
+
As those benchmarks show, Extralite is capabale of reading up to 2.5M
|
316
|
+
rows/second when fetching rows as arrays, and up to 2M rows/second when fetching
|
256
317
|
rows as hashes.
|
257
318
|
|
258
319
|
## License
|
data/ext/extralite/common.c
CHANGED
@@ -174,7 +174,7 @@ void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
|
|
174
174
|
case SQLITE_ERROR:
|
175
175
|
rb_raise(cSQLError, "%s", sqlite3_errmsg(db));
|
176
176
|
default:
|
177
|
-
rb_raise(cError, "
|
177
|
+
rb_raise(cError, "%s", sqlite3_errmsg(db));
|
178
178
|
}
|
179
179
|
}
|
180
180
|
|
@@ -216,9 +216,9 @@ void prepare_single_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
|
|
216
216
|
case SQLITE_ERROR:
|
217
217
|
rb_raise(cSQLError, "%s", sqlite3_errmsg(db));
|
218
218
|
case SQLITE_MULTI_STMT:
|
219
|
-
rb_raise(
|
219
|
+
rb_raise(cError, "A prepared statement does not accept SQL strings with multiple queries");
|
220
220
|
default:
|
221
|
-
rb_raise(cError, "
|
221
|
+
rb_raise(cError, "%s", sqlite3_errmsg(db));
|
222
222
|
}
|
223
223
|
}
|
224
224
|
|
@@ -248,7 +248,7 @@ int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db) {
|
|
248
248
|
case SQLITE_ERROR:
|
249
249
|
rb_raise(cSQLError, "%s", sqlite3_errmsg(db));
|
250
250
|
default:
|
251
|
-
rb_raise(cError, "
|
251
|
+
rb_raise(cError, "%s", sqlite3_errmsg(db));
|
252
252
|
}
|
253
253
|
|
254
254
|
return 0;
|
data/ext/extralite/database.c
CHANGED
@@ -7,6 +7,7 @@ VALUE cSQLError;
|
|
7
7
|
VALUE cBusyError;
|
8
8
|
VALUE cInterruptError;
|
9
9
|
|
10
|
+
ID ID_CALL;
|
10
11
|
ID ID_KEYS;
|
11
12
|
ID ID_NEW;
|
12
13
|
ID ID_STRIP;
|
@@ -45,6 +46,12 @@ static VALUE Database_allocate(VALUE klass) {
|
|
45
46
|
} \
|
46
47
|
}
|
47
48
|
|
49
|
+
Database_t *Database_struct(VALUE self) {
|
50
|
+
Database_t *db;
|
51
|
+
GetDatabase(self, db);
|
52
|
+
return db;
|
53
|
+
}
|
54
|
+
|
48
55
|
sqlite3 *Database_sqlite3_db(VALUE self) {
|
49
56
|
Database_t *db;
|
50
57
|
GetDatabase(self, db);
|
@@ -86,6 +93,8 @@ VALUE Database_initialize(VALUE self, VALUE path) {
|
|
86
93
|
}
|
87
94
|
#endif
|
88
95
|
|
96
|
+
db->trace_block = Qnil;
|
97
|
+
|
89
98
|
return Qnil;
|
90
99
|
}
|
91
100
|
|
@@ -134,6 +143,7 @@ static inline VALUE Database_perform_query(int argc, VALUE *argv, VALUE self, VA
|
|
134
143
|
|
135
144
|
// prepare query ctx
|
136
145
|
GetOpenDatabase(self, db);
|
146
|
+
if (db->trace_block != Qnil) rb_funcall(db->trace_block, ID_CALL, 1, sql);
|
137
147
|
prepare_multi_stmt(db->sqlite3_db, &stmt, sql);
|
138
148
|
bind_all_parameters(stmt, argc - 1, argv + 1);
|
139
149
|
query_ctx ctx = { self, db->sqlite3_db, stmt };
|
@@ -470,6 +480,17 @@ VALUE backup_cleanup(VALUE ptr) {
|
|
470
480
|
return Qnil;
|
471
481
|
}
|
472
482
|
|
483
|
+
/* call-seq:
|
484
|
+
* db.backup(dest) -> db
|
485
|
+
* db.backup(dest) { |remaining, total| } -> db
|
486
|
+
*
|
487
|
+
* Creates a backup of the database to the given destination, which can be
|
488
|
+
* either a filename or a database instance. In order to monitor the backup
|
489
|
+
* progress you can pass a block that will be called periodically by the backup
|
490
|
+
* method with two arguments: the remaining page count, and the total page
|
491
|
+
* count, which can be used to display the progress to the user or to collect
|
492
|
+
* statistics.
|
493
|
+
*/
|
473
494
|
VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
|
474
495
|
VALUE dst;
|
475
496
|
VALUE src_name;
|
@@ -513,8 +534,8 @@ VALUE Database_backup(int argc, VALUE *argv, VALUE self) {
|
|
513
534
|
return self;
|
514
535
|
}
|
515
536
|
|
516
|
-
/*
|
517
|
-
*
|
537
|
+
/* call-seq:
|
538
|
+
* Extralite.runtime_status(op[, reset]) -> [value, highwatermark]
|
518
539
|
*
|
519
540
|
* Returns runtime status values for the given op as an array containing the
|
520
541
|
* current value and the high water mark value. To reset the high water mark,
|
@@ -539,7 +560,7 @@ VALUE Extralite_runtime_status(int argc, VALUE* argv, VALUE self) {
|
|
539
560
|
* current value and the high water mark value. To reset the high water mark,
|
540
561
|
* pass true as reset.
|
541
562
|
*/
|
542
|
-
VALUE Database_status(int argc, VALUE*
|
563
|
+
VALUE Database_status(int argc, VALUE *argv, VALUE self) {
|
543
564
|
VALUE op, reset;
|
544
565
|
int cur, hwm;
|
545
566
|
|
@@ -554,6 +575,75 @@ VALUE Database_status(int argc, VALUE* argv, VALUE self) {
|
|
554
575
|
return rb_ary_new3(2, INT2NUM(cur), INT2NUM(hwm));
|
555
576
|
}
|
556
577
|
|
578
|
+
/* call-seq:
|
579
|
+
* db.limit(category) -> value
|
580
|
+
* db.limit(category, new_value) -> prev_value
|
581
|
+
*
|
582
|
+
* Returns the current limit for the given category. If a new value is given,
|
583
|
+
* sets the limit to the new value and returns the previous value.
|
584
|
+
*/
|
585
|
+
VALUE Database_limit(int argc, VALUE *argv, VALUE self) {
|
586
|
+
VALUE category, new_value;
|
587
|
+
|
588
|
+
rb_scan_args(argc, argv, "11", &category, &new_value);
|
589
|
+
|
590
|
+
Database_t *db;
|
591
|
+
GetOpenDatabase(self, db);
|
592
|
+
|
593
|
+
int value = sqlite3_limit(db->sqlite3_db, NUM2INT(category), RTEST(new_value) ? NUM2INT(new_value) : -1);
|
594
|
+
|
595
|
+
if (value == -1) rb_raise(cError, "Invalid limit category");
|
596
|
+
|
597
|
+
return INT2NUM(value);
|
598
|
+
}
|
599
|
+
|
600
|
+
/* call-seq:
|
601
|
+
* db.busy_timeout=(sec) -> db
|
602
|
+
* db.busy_timeout=nil -> db
|
603
|
+
*
|
604
|
+
* Sets the busy timeout for the database, in seconds or fractions thereof. To
|
605
|
+
* disable the busy timeout, set it to 0 or nil.
|
606
|
+
*/
|
607
|
+
VALUE Database_busy_timeout_set(VALUE self, VALUE sec) {
|
608
|
+
Database_t *db;
|
609
|
+
GetOpenDatabase(self, db);
|
610
|
+
|
611
|
+
int ms = (sec == Qnil) ? 0 : (int)(NUM2DBL(sec) * 1000);
|
612
|
+
|
613
|
+
int rc = sqlite3_busy_timeout(db->sqlite3_db, ms);
|
614
|
+
if (rc != SQLITE_OK) rb_raise(cError, "Failed to set busy timeout");
|
615
|
+
|
616
|
+
return self;
|
617
|
+
}
|
618
|
+
|
619
|
+
/* call-seq:
|
620
|
+
* db.total_changes -> value
|
621
|
+
*
|
622
|
+
* Returns the total number of changes made to the database since opening it.
|
623
|
+
*/
|
624
|
+
VALUE Database_total_changes(VALUE self) {
|
625
|
+
Database_t *db;
|
626
|
+
GetOpenDatabase(self, db);
|
627
|
+
|
628
|
+
int value = sqlite3_total_changes(db->sqlite3_db);
|
629
|
+
return INT2NUM(value);
|
630
|
+
}
|
631
|
+
|
632
|
+
/* call-seq:
|
633
|
+
* db.trace { |sql| } -> db
|
634
|
+
* db.trace -> db
|
635
|
+
*
|
636
|
+
* Installs or removes a block that will be invoked for every SQL statement
|
637
|
+
* executed.
|
638
|
+
*/
|
639
|
+
VALUE Database_trace(VALUE self) {
|
640
|
+
Database_t *db;
|
641
|
+
GetOpenDatabase(self, db);
|
642
|
+
|
643
|
+
db->trace_block = rb_block_given_p() ? rb_block_proc() : Qnil;
|
644
|
+
return self;
|
645
|
+
}
|
646
|
+
|
557
647
|
void Init_ExtraliteDatabase(void) {
|
558
648
|
VALUE mExtralite = rb_define_module("Extralite");
|
559
649
|
rb_define_singleton_method(mExtralite, "runtime_status", Extralite_runtime_status, -1);
|
@@ -563,6 +653,7 @@ void Init_ExtraliteDatabase(void) {
|
|
563
653
|
rb_define_alloc_func(cDatabase, Database_allocate);
|
564
654
|
|
565
655
|
rb_define_method(cDatabase, "backup", Database_backup, -1);
|
656
|
+
rb_define_method(cDatabase, "busy_timeout=", Database_busy_timeout_set, 1);
|
566
657
|
rb_define_method(cDatabase, "changes", Database_changes, 0);
|
567
658
|
rb_define_method(cDatabase, "close", Database_close, 0);
|
568
659
|
rb_define_method(cDatabase, "closed?", Database_closed_p, 0);
|
@@ -572,6 +663,7 @@ void Init_ExtraliteDatabase(void) {
|
|
572
663
|
rb_define_method(cDatabase, "initialize", Database_initialize, 1);
|
573
664
|
rb_define_method(cDatabase, "interrupt", Database_interrupt, 0);
|
574
665
|
rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
|
666
|
+
rb_define_method(cDatabase, "limit", Database_limit, -1);
|
575
667
|
rb_define_method(cDatabase, "prepare", Database_prepare, 1);
|
576
668
|
rb_define_method(cDatabase, "query", Database_query_hash, -1);
|
577
669
|
rb_define_method(cDatabase, "query_ary", Database_query_ary, -1);
|
@@ -580,6 +672,8 @@ void Init_ExtraliteDatabase(void) {
|
|
580
672
|
rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
|
581
673
|
rb_define_method(cDatabase, "query_single_value", Database_query_single_value, -1);
|
582
674
|
rb_define_method(cDatabase, "status", Database_status, -1);
|
675
|
+
rb_define_method(cDatabase, "total_changes", Database_total_changes, 0);
|
676
|
+
rb_define_method(cDatabase, "trace", Database_trace, 0);
|
583
677
|
rb_define_method(cDatabase, "transaction_active?", Database_transaction_active_p, 0);
|
584
678
|
|
585
679
|
#ifdef HAVE_SQLITE3_LOAD_EXTENSION
|
@@ -595,6 +689,7 @@ void Init_ExtraliteDatabase(void) {
|
|
595
689
|
rb_gc_register_mark_object(cBusyError);
|
596
690
|
rb_gc_register_mark_object(cInterruptError);
|
597
691
|
|
692
|
+
ID_CALL = rb_intern("call");
|
598
693
|
ID_KEYS = rb_intern("keys");
|
599
694
|
ID_NEW = rb_intern("new");
|
600
695
|
ID_STRIP = rb_intern("strip");
|
data/ext/extralite/extralite.h
CHANGED
@@ -27,6 +27,7 @@ extern VALUE cSQLError;
|
|
27
27
|
extern VALUE cBusyError;
|
28
28
|
extern VALUE cInterruptError;
|
29
29
|
|
30
|
+
extern ID ID_CALL;
|
30
31
|
extern ID ID_KEYS;
|
31
32
|
extern ID ID_NEW;
|
32
33
|
extern ID ID_STRIP;
|
@@ -34,11 +35,13 @@ extern ID ID_TO_S;
|
|
34
35
|
|
35
36
|
typedef struct {
|
36
37
|
sqlite3 *sqlite3_db;
|
38
|
+
VALUE trace_block;
|
37
39
|
} Database_t;
|
38
40
|
|
39
41
|
typedef struct {
|
40
42
|
VALUE db;
|
41
43
|
VALUE sql;
|
44
|
+
Database_t *db_struct;
|
42
45
|
sqlite3 *sqlite3_db;
|
43
46
|
sqlite3_stmt *stmt;
|
44
47
|
} PreparedStatement_t;
|
@@ -72,5 +75,6 @@ int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db);
|
|
72
75
|
VALUE cleanup_stmt(query_ctx *ctx);
|
73
76
|
|
74
77
|
sqlite3 *Database_sqlite3_db(VALUE self);
|
78
|
+
Database_t *Database_struct(VALUE self);
|
75
79
|
|
76
80
|
#endif /* EXTRALITE_H */
|
@@ -50,6 +50,7 @@ VALUE PreparedStatement_initialize(VALUE self, VALUE db, VALUE sql) {
|
|
50
50
|
rb_raise(cError, "Cannot prepare an empty SQL query");
|
51
51
|
|
52
52
|
stmt->db = db;
|
53
|
+
stmt->db_struct = Database_struct(db);
|
53
54
|
stmt->sqlite3_db = Database_sqlite3_db(db);
|
54
55
|
stmt->sql = sql;
|
55
56
|
|
@@ -65,6 +66,8 @@ static inline VALUE PreparedStatement_perform_query(int argc, VALUE *argv, VALUE
|
|
65
66
|
if (!stmt->stmt)
|
66
67
|
rb_raise(cError, "Prepared statement is closed");
|
67
68
|
|
69
|
+
if (stmt->db_struct->trace_block != Qnil) rb_funcall(stmt->db_struct->trace_block, ID_CALL, 1, stmt->sql);
|
70
|
+
|
68
71
|
sqlite3_reset(stmt->stmt);
|
69
72
|
sqlite3_clear_bindings(stmt->stmt);
|
70
73
|
bind_all_parameters(stmt->stmt, argc, argv);
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Extralite
|
2
|
+
|
3
|
+
SQLITE_STATUS_MEMORY_USED = 0
|
4
|
+
SQLITE_STATUS_PAGECACHE_USED = 1
|
5
|
+
SQLITE_STATUS_PAGECACHE_OVERFLOW = 2
|
6
|
+
SQLITE_STATUS_SCRATCH_USED = 3 # NOT USED
|
7
|
+
SQLITE_STATUS_SCRATCH_OVERFLOW = 4 # NOT USED
|
8
|
+
SQLITE_STATUS_MALLOC_SIZE = 5
|
9
|
+
SQLITE_STATUS_PARSER_STACK = 6
|
10
|
+
SQLITE_STATUS_PAGECACHE_SIZE = 7
|
11
|
+
SQLITE_STATUS_SCRATCH_SIZE = 8 # NOT USED
|
12
|
+
SQLITE_STATUS_MALLOC_COUNT = 9
|
13
|
+
|
14
|
+
SQLITE_DBSTATUS_LOOKASIDE_USED = 0
|
15
|
+
SQLITE_DBSTATUS_CACHE_USED = 1
|
16
|
+
SQLITE_DBSTATUS_SCHEMA_USED = 2
|
17
|
+
SQLITE_DBSTATUS_STMT_USED = 3
|
18
|
+
SQLITE_DBSTATUS_LOOKASIDE_HIT = 4
|
19
|
+
SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE = 5
|
20
|
+
SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL = 6
|
21
|
+
SQLITE_DBSTATUS_CACHE_HIT = 7
|
22
|
+
SQLITE_DBSTATUS_CACHE_MISS = 8
|
23
|
+
SQLITE_DBSTATUS_CACHE_WRITE = 9
|
24
|
+
SQLITE_DBSTATUS_DEFERRED_FKS = 10
|
25
|
+
SQLITE_DBSTATUS_CACHE_USED_SHARED = 11
|
26
|
+
SQLITE_DBSTATUS_CACHE_SPILL = 12
|
27
|
+
|
28
|
+
SQLITE_STMTSTATUS_FULLSCAN_STEP = 1
|
29
|
+
SQLITE_STMTSTATUS_SORT = 2
|
30
|
+
SQLITE_STMTSTATUS_AUTOINDEX = 3
|
31
|
+
SQLITE_STMTSTATUS_VM_STEP = 4
|
32
|
+
SQLITE_STMTSTATUS_REPREPARE = 5
|
33
|
+
SQLITE_STMTSTATUS_RUN = 6
|
34
|
+
SQLITE_STMTSTATUS_FILTER_MISS = 7
|
35
|
+
SQLITE_STMTSTATUS_FILTER_HIT = 8
|
36
|
+
SQLITE_STMTSTATUS_MEMUSED = 99
|
37
|
+
|
38
|
+
SQLITE_LIMIT_LENGTH = 0
|
39
|
+
SQLITE_LIMIT_SQL_LENGTH = 1
|
40
|
+
SQLITE_LIMIT_COLUMN = 2
|
41
|
+
SQLITE_LIMIT_EXPR_DEPTH = 3
|
42
|
+
SQLITE_LIMIT_COMPOUND_SELECT = 4
|
43
|
+
SQLITE_LIMIT_VDBE_OP = 5
|
44
|
+
SQLITE_LIMIT_FUNCTION_ARG = 6
|
45
|
+
SQLITE_LIMIT_ATTACHED = 7
|
46
|
+
SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8
|
47
|
+
SQLITE_LIMIT_VARIABLE_NUMBER = 9
|
48
|
+
SQLITE_LIMIT_TRIGGER_DEPTH = 10
|
49
|
+
SQLITE_LIMIT_WORKER_THREADS = 11
|
50
|
+
end
|
data/lib/extralite/version.rb
CHANGED
data/lib/extralite.rb
CHANGED
@@ -1,46 +1,11 @@
|
|
1
1
|
require_relative './extralite_ext'
|
2
|
+
require_relative './extralite/sqlite3_constants'
|
2
3
|
|
3
4
|
# Extralite is a Ruby gem for working with SQLite databases
|
4
5
|
module Extralite
|
5
|
-
|
6
|
-
SQLITE_STATUS_MEMORY_USED = 0
|
7
|
-
SQLITE_STATUS_PAGECACHE_USED = 1
|
8
|
-
SQLITE_STATUS_PAGECACHE_OVERFLOW = 2
|
9
|
-
SQLITE_STATUS_SCRATCH_USED = 3 # NOT USED
|
10
|
-
SQLITE_STATUS_SCRATCH_OVERFLOW = 4 # NOT USED
|
11
|
-
SQLITE_STATUS_MALLOC_SIZE = 5
|
12
|
-
SQLITE_STATUS_PARSER_STACK = 6
|
13
|
-
SQLITE_STATUS_PAGECACHE_SIZE = 7
|
14
|
-
SQLITE_STATUS_SCRATCH_SIZE = 8 # NOT USED
|
15
|
-
SQLITE_STATUS_MALLOC_COUNT = 9
|
16
6
|
|
17
|
-
|
18
|
-
|
19
|
-
SQLITE_DBSTATUS_SCHEMA_USED = 2
|
20
|
-
SQLITE_DBSTATUS_STMT_USED = 3
|
21
|
-
SQLITE_DBSTATUS_LOOKASIDE_HIT = 4
|
22
|
-
SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE = 5
|
23
|
-
SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL = 6
|
24
|
-
SQLITE_DBSTATUS_CACHE_HIT = 7
|
25
|
-
SQLITE_DBSTATUS_CACHE_MISS = 8
|
26
|
-
SQLITE_DBSTATUS_CACHE_WRITE = 9
|
27
|
-
SQLITE_DBSTATUS_DEFERRED_FKS = 10
|
28
|
-
SQLITE_DBSTATUS_CACHE_USED_SHARED = 11
|
29
|
-
SQLITE_DBSTATUS_CACHE_SPILL = 12
|
30
|
-
|
31
|
-
SQLITE_STMTSTATUS_FULLSCAN_STEP = 1
|
32
|
-
SQLITE_STMTSTATUS_SORT = 2
|
33
|
-
SQLITE_STMTSTATUS_AUTOINDEX = 3
|
34
|
-
SQLITE_STMTSTATUS_VM_STEP = 4
|
35
|
-
SQLITE_STMTSTATUS_REPREPARE = 5
|
36
|
-
SQLITE_STMTSTATUS_RUN = 6
|
37
|
-
SQLITE_STMTSTATUS_FILTER_MISS = 7
|
38
|
-
SQLITE_STMTSTATUS_FILTER_HIT = 8
|
39
|
-
SQLITE_STMTSTATUS_MEMUSED = 99
|
40
|
-
|
41
|
-
# The following class definitions are not really needed, as they're already
|
42
|
-
# defined in the C extension. We put them here for the sake of generating
|
43
|
-
# docs.
|
7
|
+
# The following error classes are already defined in the C extension. We put
|
8
|
+
# them here for the sake of generating docs.
|
44
9
|
|
45
10
|
# A base class for Extralite exceptions
|
46
11
|
class Error < ::StandardError
|
@@ -70,14 +35,26 @@ module Extralite
|
|
70
35
|
AND name NOT LIKE 'sqlite_%';
|
71
36
|
SQL
|
72
37
|
|
38
|
+
# Returns the list of currently defined tables.
|
39
|
+
#
|
40
|
+
# @return [Array] list of tables
|
73
41
|
def tables
|
74
42
|
query_single_column(TABLES_SQL)
|
75
43
|
end
|
76
44
|
|
45
|
+
# Gets or sets one or more pragmas:
|
46
|
+
#
|
47
|
+
# db.pragma(:cache_size) # get
|
48
|
+
# db.pragma(cache_size: -2000) # set
|
49
|
+
#
|
50
|
+
# @param value [Symbol, String, Hash] pragma name or hash mapping names to values
|
51
|
+
# @return [Hash] query result
|
77
52
|
def pragma(value)
|
78
53
|
value.is_a?(Hash) ? pragma_set(value) : pragma_get(value)
|
79
54
|
end
|
80
55
|
|
56
|
+
private
|
57
|
+
|
81
58
|
def pragma_set(values)
|
82
59
|
sql = values.inject(+'') { |s, (k, v)| s += "pragma #{k}=#{v}; " }
|
83
60
|
query(sql)
|
@@ -87,18 +64,4 @@ module Extralite
|
|
87
64
|
query("pragma #{key}")
|
88
65
|
end
|
89
66
|
end
|
90
|
-
|
91
|
-
# An SQLite backup
|
92
|
-
class Backup
|
93
|
-
# def initialize(dst, dst_name, src, src_name); end
|
94
|
-
|
95
|
-
# def dst; end
|
96
|
-
# def src; end
|
97
|
-
|
98
|
-
# def step(pages); end
|
99
|
-
# def finish; end
|
100
|
-
|
101
|
-
# def pagecount; end
|
102
|
-
# def remaining; end
|
103
|
-
end
|
104
67
|
end
|
data/test/test_database.rb
CHANGED
@@ -269,6 +269,68 @@ end
|
|
269
269
|
def test_database_status
|
270
270
|
assert_operator 0, :<, @db.status(Extralite::SQLITE_DBSTATUS_SCHEMA_USED).first
|
271
271
|
end
|
272
|
+
|
273
|
+
def test_database_limit
|
274
|
+
result = @db.limit(Extralite::SQLITE_LIMIT_ATTACHED)
|
275
|
+
assert_equal 10, result
|
276
|
+
|
277
|
+
result = @db.limit(Extralite::SQLITE_LIMIT_ATTACHED, 5)
|
278
|
+
assert_equal 10, result
|
279
|
+
|
280
|
+
result = @db.limit(Extralite::SQLITE_LIMIT_ATTACHED)
|
281
|
+
assert_equal 5, result
|
282
|
+
|
283
|
+
assert_raises(Extralite::Error) { @db.limit(-999) }
|
284
|
+
end
|
285
|
+
|
286
|
+
def test_database_busy_timeout
|
287
|
+
fn = "/tmp/extralite-#{rand(10000)}.db"
|
288
|
+
db1 = Extralite::Database.new(fn)
|
289
|
+
db2 = Extralite::Database.new(fn)
|
290
|
+
|
291
|
+
db1.query('begin exclusive')
|
292
|
+
assert_raises(Extralite::BusyError) { db2.query('begin exclusive') }
|
293
|
+
|
294
|
+
db2.busy_timeout = 0.3
|
295
|
+
t0 = Time.now
|
296
|
+
t = Thread.new { sleep 0.1; db1.query('rollback') }
|
297
|
+
result = db2.query('begin exclusive')
|
298
|
+
t1 = Time.now
|
299
|
+
|
300
|
+
assert_equal [], result
|
301
|
+
assert t1 - t0 >= 0.1
|
302
|
+
db2.query('rollback')
|
303
|
+
|
304
|
+
# try to provoke a timeout
|
305
|
+
db1.query('begin exclusive')
|
306
|
+
db2.busy_timeout = 0.1
|
307
|
+
t0 = Time.now
|
308
|
+
t = Thread.new do
|
309
|
+
sleep 0.5
|
310
|
+
ensure
|
311
|
+
db1.query('rollback')
|
312
|
+
end
|
313
|
+
assert_raises(Extralite::BusyError) { db2.query('begin exclusive') }
|
314
|
+
t1 = Time.now
|
315
|
+
assert t1 - t0 >= 0.1
|
316
|
+
t.kill
|
317
|
+
t.join
|
318
|
+
|
319
|
+
db1.query('begin exclusive')
|
320
|
+
db2.busy_timeout = 0
|
321
|
+
assert_raises(Extralite::BusyError) { db2.query('begin exclusive') }
|
322
|
+
|
323
|
+
db2.busy_timeout = nil
|
324
|
+
assert_raises(Extralite::BusyError) { db2.query('begin exclusive') }
|
325
|
+
end
|
326
|
+
|
327
|
+
def test_database_total_changes
|
328
|
+
assert_equal 2, @db.total_changes
|
329
|
+
|
330
|
+
@db.query('insert into t values (7, 8, 9)')
|
331
|
+
|
332
|
+
assert_equal 3, @db.total_changes
|
333
|
+
end
|
272
334
|
end
|
273
335
|
|
274
336
|
class ScenarioTest < MiniTest::Test
|
@@ -336,6 +398,30 @@ class ScenarioTest < MiniTest::Test
|
|
336
398
|
result = @db.query_single_column('select x from t')
|
337
399
|
assert_equal [1, 4, 7], result
|
338
400
|
end
|
401
|
+
|
402
|
+
def test_database_trace
|
403
|
+
sqls = []
|
404
|
+
@db.trace { |sql| sqls << sql }
|
405
|
+
|
406
|
+
@db.query('select 1')
|
407
|
+
assert_equal ['select 1'], sqls
|
408
|
+
|
409
|
+
@db.query('select 2')
|
410
|
+
assert_equal ['select 1', 'select 2'], sqls
|
411
|
+
|
412
|
+
stmt = @db.prepare('select 3')
|
413
|
+
|
414
|
+
stmt.query
|
415
|
+
assert_equal ['select 1', 'select 2', 'select 3'], sqls
|
416
|
+
|
417
|
+
# turn off
|
418
|
+
@db.trace
|
419
|
+
|
420
|
+
stmt.query
|
421
|
+
|
422
|
+
@db.query('select 4')
|
423
|
+
assert_equal ['select 1', 'select 2', 'select 3'], sqls
|
424
|
+
end
|
339
425
|
end
|
340
426
|
|
341
427
|
class BackupTest < MiniTest::Test
|
@@ -37,6 +37,11 @@ class PreparedStatementTest < MiniTest::Test
|
|
37
37
|
assert_raises(Extralite::SQLError) { @db.prepare('blah') }
|
38
38
|
end
|
39
39
|
|
40
|
+
def test_prepared_statement_with_multiple_queries
|
41
|
+
error = begin; @db.prepare('select 1; select 2'); rescue => e; error = e; end
|
42
|
+
assert_equal Extralite::Error, error.class
|
43
|
+
end
|
44
|
+
|
40
45
|
def test_prepared_statement_query_hash
|
41
46
|
r = @stmt.query_hash(4)
|
42
47
|
assert_equal [{x: 4, y: 5, z: 6}], r
|
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.
|
4
|
+
version: '1.23'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-01-
|
11
|
+
date: 2023-01-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -113,6 +113,7 @@ files:
|
|
113
113
|
- extralite.gemspec
|
114
114
|
- gemspec.rb
|
115
115
|
- lib/extralite.rb
|
116
|
+
- lib/extralite/sqlite3_constants.rb
|
116
117
|
- lib/extralite/version.rb
|
117
118
|
- lib/sequel/adapters/extralite.rb
|
118
119
|
- test/extensions/text.dylib
|