extralite-bundle 1.22 → 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 +8 -0
- data/Gemfile.lock +1 -1
- data/README.md +47 -40
- data/ext/extralite/common.c +4 -4
- data/ext/extralite/database.c +85 -1
- 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 +3 -38
- 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
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Extralite -
|
1
|
+
# Extralite - a Super Fast Ruby Gem for Working with SQLite3 Databases
|
2
2
|
|
3
3
|
* Source code: https://github.com/digital-fabric/extralite
|
4
4
|
* Documentation: http://www.rubydoc.info/gems/extralite
|
@@ -19,7 +19,7 @@ latest features and enhancements.
|
|
19
19
|
|
20
20
|
## Features
|
21
21
|
|
22
|
-
- Super fast - [up to
|
22
|
+
- Super fast - [up to 11x faster](#performance) than the
|
23
23
|
[sqlite3](https://github.com/sparklemotion/sqlite3-ruby) gem (see also
|
24
24
|
[comparison](#why-not-just-use-the-sqlite3-gem).)
|
25
25
|
- A variety of methods for different data access patterns: rows as hashes, rows
|
@@ -48,7 +48,7 @@ gem 'extralite'
|
|
48
48
|
|
49
49
|
You can also run `gem install extralite` if you just want to check it out.
|
50
50
|
|
51
|
-
### Installing the Extralite-SQLite3
|
51
|
+
### Installing the Extralite-SQLite3 Bundle
|
52
52
|
|
53
53
|
If you don't have sqlite3 installed on your system, do not want to use the
|
54
54
|
system-installed version of SQLite3, or would like to use the latest version of
|
@@ -145,9 +145,9 @@ db.close
|
|
145
145
|
db.closed? #=> true
|
146
146
|
```
|
147
147
|
|
148
|
-
## More
|
148
|
+
## More Features
|
149
149
|
|
150
|
-
### Interrupting
|
150
|
+
### Interrupting Long-running Queries
|
151
151
|
|
152
152
|
When running long-running queries, you can use `Database#interrupt` to interrupt
|
153
153
|
the query:
|
@@ -168,7 +168,7 @@ ensure
|
|
168
168
|
end
|
169
169
|
```
|
170
170
|
|
171
|
-
### Creating
|
171
|
+
### Creating Backups
|
172
172
|
|
173
173
|
You can use `Database#backup` to create backup copies of a database. The
|
174
174
|
`#backup` method takes either a filename or a database instance:
|
@@ -191,7 +191,7 @@ db.backup('backup.db') do |remaining, total|
|
|
191
191
|
end
|
192
192
|
```
|
193
193
|
|
194
|
-
###
|
194
|
+
### Retrieving Status Information
|
195
195
|
|
196
196
|
Extralite provides methods for retrieving status information about the sqlite
|
197
197
|
runtime, database-specific status and prepared statement-specific status,
|
@@ -217,6 +217,42 @@ current, high_watermark = db.status(Extralite::SQLITE_DBSTATUS_CACHE_USED)
|
|
217
217
|
value = stmt.status(Extralite::SQLITE_STMTSTATUS_RUN)
|
218
218
|
```
|
219
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
|
+
|
220
256
|
## Usage with Sequel
|
221
257
|
|
222
258
|
Extralite includes an adapter for
|
@@ -231,35 +267,6 @@ p articles.to_a
|
|
231
267
|
|
232
268
|
(Make sure you include `extralite` as a dependency in your `Gemfile`.)
|
233
269
|
|
234
|
-
## Why not just use the sqlite3 gem?
|
235
|
-
|
236
|
-
The [sqlite3](https://github.com/sparklemotion/sqlite3-ruby) gem is a
|
237
|
-
popular, solid, well-maintained project, used by thousands of developers. I've
|
238
|
-
been doing a lot of work with SQLite3 databases lately, and wanted to have a
|
239
|
-
simpler API that gives me query results in a variety of ways. Thus extralite was
|
240
|
-
born.
|
241
|
-
|
242
|
-
Extralite is significantly [faster](#performance) than the `sqlite3` gem and is
|
243
|
-
also [thread-friendly](#concurrency). On the other hand, Extralite does not have
|
244
|
-
support for defining custom functions, aggregates and collations. If you're
|
245
|
-
using any of those features, you'll have to stick to the `sqlite3` gem.
|
246
|
-
|
247
|
-
Here's a table summarizing the differences between the two gems:
|
248
|
-
|
249
|
-
| |sqlite3 1.6.0|Extralite 1.21|
|
250
|
-
|-|-|-|
|
251
|
-
|SQLite3 dependency|depends on OS-installed libsqlite3|Use either system sqlite3 or [bundled latest version of SQLite3](#installing-the-extralite-sqlite3-bundle)|
|
252
|
-
|API design|multiple classes|single class|
|
253
|
-
|Query results|row as hash, row as array, single row, single value|row as hash, row as array, __single column__, single row, single value|
|
254
|
-
|Execute multiple statements|separate API (#execute_batch)|integrated|
|
255
|
-
|Prepared statements|yes|yes|
|
256
|
-
|custom functions in Ruby|yes|no|
|
257
|
-
|custom collations|yes|no|
|
258
|
-
|custom aggregate functions|yes|no|
|
259
|
-
|Multithread friendly|no|[yes](#concurrency)|
|
260
|
-
|Code size|~2650LoC|~1300LoC|
|
261
|
-
|Performance|1x|1.5x to 10x (see [below](#performance))|
|
262
|
-
|
263
270
|
## Concurrency
|
264
271
|
|
265
272
|
Extralite releases the GVL while making blocking calls to the sqlite3 library,
|
@@ -272,10 +279,10 @@ performance, as you can see:
|
|
272
279
|
|
273
280
|
A benchmark script is included, creating a table of various row counts, then
|
274
281
|
fetching the entire table using either `sqlite3` or `extralite`. This benchmark
|
275
|
-
shows Extralite to be up to ~
|
282
|
+
shows Extralite to be up to ~11 times faster than `sqlite3` when fetching a
|
276
283
|
large number of rows.
|
277
284
|
|
278
|
-
### Rows as
|
285
|
+
### Rows as Hashes
|
279
286
|
|
280
287
|
[Benchmark source code](https://github.com/digital-fabric/extralite/blob/main/test/perf_hash.rb)
|
281
288
|
|
@@ -285,7 +292,7 @@ large number of rows.
|
|
285
292
|
|1K|299.2K rows/s|1.983M rows/s|__6.63x__|
|
286
293
|
|100K|185.4K rows/s|2.033M rows/s|__10.97x__|
|
287
294
|
|
288
|
-
### Rows as
|
295
|
+
### Rows as Arrays
|
289
296
|
|
290
297
|
[Benchmark source code](https://github.com/digital-fabric/extralite/blob/main/test/perf_ary.rb)
|
291
298
|
|
@@ -295,7 +302,7 @@ large number of rows.
|
|
295
302
|
|1K|502.1K rows/s|2.065M rows/s|__4.11x__|
|
296
303
|
|100K|455.7K rows/s|2.511M rows/s|__5.51x__|
|
297
304
|
|
298
|
-
### Prepared
|
305
|
+
### Prepared Statements
|
299
306
|
|
300
307
|
[Benchmark source code](https://github.com/digital-fabric/extralite/blob/main/test/perf_prepared.rb)
|
301
308
|
|
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 };
|
@@ -550,7 +560,7 @@ VALUE Extralite_runtime_status(int argc, VALUE* argv, VALUE self) {
|
|
550
560
|
* current value and the high water mark value. To reset the high water mark,
|
551
561
|
* pass true as reset.
|
552
562
|
*/
|
553
|
-
VALUE Database_status(int argc, VALUE*
|
563
|
+
VALUE Database_status(int argc, VALUE *argv, VALUE self) {
|
554
564
|
VALUE op, reset;
|
555
565
|
int cur, hwm;
|
556
566
|
|
@@ -565,6 +575,75 @@ VALUE Database_status(int argc, VALUE* argv, VALUE self) {
|
|
565
575
|
return rb_ary_new3(2, INT2NUM(cur), INT2NUM(hwm));
|
566
576
|
}
|
567
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
|
+
|
568
647
|
void Init_ExtraliteDatabase(void) {
|
569
648
|
VALUE mExtralite = rb_define_module("Extralite");
|
570
649
|
rb_define_singleton_method(mExtralite, "runtime_status", Extralite_runtime_status, -1);
|
@@ -574,6 +653,7 @@ void Init_ExtraliteDatabase(void) {
|
|
574
653
|
rb_define_alloc_func(cDatabase, Database_allocate);
|
575
654
|
|
576
655
|
rb_define_method(cDatabase, "backup", Database_backup, -1);
|
656
|
+
rb_define_method(cDatabase, "busy_timeout=", Database_busy_timeout_set, 1);
|
577
657
|
rb_define_method(cDatabase, "changes", Database_changes, 0);
|
578
658
|
rb_define_method(cDatabase, "close", Database_close, 0);
|
579
659
|
rb_define_method(cDatabase, "closed?", Database_closed_p, 0);
|
@@ -583,6 +663,7 @@ void Init_ExtraliteDatabase(void) {
|
|
583
663
|
rb_define_method(cDatabase, "initialize", Database_initialize, 1);
|
584
664
|
rb_define_method(cDatabase, "interrupt", Database_interrupt, 0);
|
585
665
|
rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
|
666
|
+
rb_define_method(cDatabase, "limit", Database_limit, -1);
|
586
667
|
rb_define_method(cDatabase, "prepare", Database_prepare, 1);
|
587
668
|
rb_define_method(cDatabase, "query", Database_query_hash, -1);
|
588
669
|
rb_define_method(cDatabase, "query_ary", Database_query_ary, -1);
|
@@ -591,6 +672,8 @@ void Init_ExtraliteDatabase(void) {
|
|
591
672
|
rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
|
592
673
|
rb_define_method(cDatabase, "query_single_value", Database_query_single_value, -1);
|
593
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);
|
594
677
|
rb_define_method(cDatabase, "transaction_active?", Database_transaction_active_p, 0);
|
595
678
|
|
596
679
|
#ifdef HAVE_SQLITE3_LOAD_EXTENSION
|
@@ -606,6 +689,7 @@ void Init_ExtraliteDatabase(void) {
|
|
606
689
|
rb_gc_register_mark_object(cBusyError);
|
607
690
|
rb_gc_register_mark_object(cInterruptError);
|
608
691
|
|
692
|
+
ID_CALL = rb_intern("call");
|
609
693
|
ID_KEYS = rb_intern("keys");
|
610
694
|
ID_NEW = rb_intern("new");
|
611
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
|
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
|