extralite 1.7 → 1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +10 -33
- data/README.md +29 -19
- data/Rakefile +16 -7
- data/ext/extralite/extralite.c +137 -4
- data/extralite.gemspec +5 -5
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +20 -0
- data/lib/sequel/adapters/extralite.rb +4 -0
- data/test/perf_ary.rb +51 -0
- data/test/{perf.rb → perf_hash.rb} +0 -0
- data/test/test_database.rb +25 -0
- metadata +13 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1dbf79f19c8e1a1b31da57db9e1b20eb32efed2a510dfd1a19df048ecdf6532
|
4
|
+
data.tar.gz: 633afbfd7042dedb38a5275d525bf9876a8cd194e6b6f95db183d78019c53816
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9987a2e1b14e9213a289d04d79a0914e6d3a843c5af94e4d57d6839f193b8294b57b280141dd4784419d9f920d8b1ee32f9a21efee56a85ff524128bc4262aed
|
7
|
+
data.tar.gz: 2b53f9dd796f37e7c901f88b5bfe2b28463c0465ff5aaf0ade160b1ecf95f0beb7b0cd56496e3c76bd4f754ef7649bf8a704ff7eb94053a7b7f1b1099cf31a5b
|
data/.github/FUNDING.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
github: ciconia
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,60 +1,37 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
extralite (1.
|
4
|
+
extralite (1.10)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
ast (2.4.2)
|
10
|
-
coderay (1.1.3)
|
11
9
|
docile (1.4.0)
|
12
10
|
json (2.5.1)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
parser (3.0.1.1)
|
17
|
-
ast (~> 2.4.1)
|
18
|
-
pry (0.13.1)
|
19
|
-
coderay (~> 1.1)
|
20
|
-
method_source (~> 1.0)
|
21
|
-
rainbow (3.0.0)
|
22
|
-
rake (13.0.3)
|
23
|
-
rake-compiler (1.1.1)
|
11
|
+
minitest (5.15.0)
|
12
|
+
rake (13.0.6)
|
13
|
+
rake-compiler (1.1.6)
|
24
14
|
rake
|
25
|
-
regexp_parser (2.1.1)
|
26
|
-
rexml (3.2.5)
|
27
|
-
rubocop (0.85.1)
|
28
|
-
parallel (~> 1.10)
|
29
|
-
parser (>= 2.7.0.1)
|
30
|
-
rainbow (>= 2.2.2, < 4.0)
|
31
|
-
regexp_parser (>= 1.7)
|
32
|
-
rexml
|
33
|
-
rubocop-ast (>= 0.0.3)
|
34
|
-
ruby-progressbar (~> 1.7)
|
35
|
-
unicode-display_width (>= 1.4.0, < 2.0)
|
36
|
-
rubocop-ast (1.5.0)
|
37
|
-
parser (>= 3.0.1.1)
|
38
|
-
ruby-progressbar (1.11.0)
|
39
15
|
sequel (5.51.0)
|
40
16
|
simplecov (0.17.1)
|
41
17
|
docile (~> 1.1)
|
42
18
|
json (>= 1.8, < 3)
|
43
19
|
simplecov-html (~> 0.10.0)
|
44
20
|
simplecov-html (0.10.2)
|
45
|
-
|
21
|
+
webrick (1.7.0)
|
22
|
+
yard (0.9.27)
|
23
|
+
webrick (~> 1.7.0)
|
46
24
|
|
47
25
|
PLATFORMS
|
48
26
|
ruby
|
49
27
|
|
50
28
|
DEPENDENCIES
|
51
29
|
extralite!
|
52
|
-
minitest (= 5.
|
53
|
-
|
54
|
-
rake-compiler (= 1.1.1)
|
55
|
-
rubocop (= 0.85.1)
|
30
|
+
minitest (= 5.15.0)
|
31
|
+
rake-compiler (= 1.1.6)
|
56
32
|
sequel (= 5.51.0)
|
57
33
|
simplecov (= 0.17.1)
|
34
|
+
yard (= 0.9.27)
|
58
35
|
|
59
36
|
BUNDLED WITH
|
60
37
|
2.1.4
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Extralite - a Ruby gem for working with SQLite3 databases
|
1
|
+
# Extralite - a fast Ruby gem for working with SQLite3 databases
|
2
2
|
|
3
3
|
[](http://rubygems.org/gems/extralite)
|
4
4
|
[](https://github.com/digital-fabric/extralite/actions?query=workflow%3ATests)
|
@@ -6,9 +6,9 @@
|
|
6
6
|
|
7
7
|
## What is Extralite?
|
8
8
|
|
9
|
-
Extralite is
|
10
|
-
wrapper for Ruby. It provides a single class with a minimal set of methods
|
11
|
-
|
9
|
+
Extralite is a fast, extra-lightweight (less than 460 lines of C-code) SQLite3
|
10
|
+
wrapper for Ruby. It provides a single class with a minimal set of methods for
|
11
|
+
interacting with an SQLite3 database.
|
12
12
|
|
13
13
|
## Features
|
14
14
|
|
@@ -73,7 +73,7 @@ db.query('select * from foo where bar = :bar', 'bar' => 42)
|
|
73
73
|
db.query('select * from foo where bar = :bar', ':bar' => 42)
|
74
74
|
|
75
75
|
# get last insert rowid
|
76
|
-
rowid = db.
|
76
|
+
rowid = db.last_insert_rowid
|
77
77
|
|
78
78
|
# get number of rows changed in last query
|
79
79
|
number_of_rows_affected = db.changes
|
@@ -127,7 +127,7 @@ Here's a table summarizing the differences between the two gems:
|
|
127
127
|
|custom collations|yes|no|
|
128
128
|
|custom aggregate functions|yes|no|
|
129
129
|
|Multithread friendly|no|[yes](#what-about-concurrency)|
|
130
|
-
|Code size|~2650LoC|~
|
130
|
+
|Code size|~2650LoC|~530LoC|
|
131
131
|
|Performance|1x|1.5x to 12.5x (see [below](#performance))|
|
132
132
|
|
133
133
|
## What about concurrency?
|
@@ -140,23 +140,33 @@ performance:
|
|
140
140
|
|
141
141
|
## Performance
|
142
142
|
|
143
|
-
A benchmark script is
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
times faster than `sqlite3` when fetching a large number of rows. Here are the
|
148
|
-
results (using the `sqlite3` gem performance as baseline):
|
143
|
+
A benchmark script is included, creating a table of various row counts, then
|
144
|
+
fetching the entire table using either `sqlite3` or `extralite`. This benchmark
|
145
|
+
shows Extralite to be up to 12.5 times faster than `sqlite3` when fetching a
|
146
|
+
large number of rows. Here are the [results for fetching rows as hashes](https://github.com/digital-fabric/extralite/blob/main/test/perf_hash.rb):
|
149
147
|
|
150
|
-
|Row count|sqlite3-ruby
|
151
|
-
|
152
|
-
|10|
|
153
|
-
|1K|
|
154
|
-
|100K|
|
148
|
+
|Row count|sqlite3-ruby|Extralite|Advantage|
|
149
|
+
|-:|-:|-:|-:|
|
150
|
+
|10|57620 rows/s|95340 rows/s|__1.65x__|
|
151
|
+
|1K|286.8K rows/s|2106.4 rows/s|__7.35x__|
|
152
|
+
|100K|181K rows/s|2275.3K rows/s|__12.53x__|
|
153
|
+
|
154
|
+
When [fetching rows as arrays](https://github.com/digital-fabric/extralite/blob/main/test/perf_ary.rb) Extralite also significantly outperforms sqlite3-ruby:
|
155
|
+
|
156
|
+
|Row count|sqlite3-ruby|Extralite|Advantage|
|
157
|
+
|-:|-:|-:|-:|
|
158
|
+
|10|64365 rows/s|94031 rows/s|__1.46x__|
|
159
|
+
|1K|498.9K rows/s|2478.2K rows/s|__4.97x__|
|
160
|
+
|100K|441.1K rows/s|3023.4K rows/s|__6.85x__|
|
155
161
|
|
156
162
|
(If you're interested in checking this yourself, just run the script and let me
|
157
|
-
know if your results are
|
163
|
+
know if your results are better/worse.)
|
164
|
+
|
165
|
+
As those benchmarks show, Extralite is capabale of reading more than 3M
|
166
|
+
rows/second (when fetching rows as arrays), and more than 2.2M rows/second (when
|
167
|
+
fetching rows as hashes.)
|
158
168
|
|
159
169
|
## Contributing
|
160
170
|
|
161
171
|
Contributions in the form of issues, PRs or comments will be greatly
|
162
|
-
appreciated!
|
172
|
+
appreciated!
|
data/Rakefile
CHANGED
@@ -1,18 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rake/clean'
|
5
5
|
|
6
|
-
require
|
7
|
-
Rake::ExtensionTask.new(
|
8
|
-
ext.ext_dir =
|
6
|
+
require 'rake/extensiontask'
|
7
|
+
Rake::ExtensionTask.new('extralite_ext') do |ext|
|
8
|
+
ext.ext_dir = 'ext/extralite'
|
9
9
|
end
|
10
10
|
|
11
11
|
task :recompile => [:clean, :compile]
|
12
12
|
|
13
|
-
task :default => [:compile, :test]
|
13
|
+
task :default => [:compile, :doc, :test]
|
14
|
+
task :doc => :yard
|
14
15
|
task :test do
|
15
16
|
exec 'ruby test/run.rb'
|
16
17
|
end
|
17
18
|
|
18
|
-
CLEAN.include
|
19
|
+
CLEAN.include '**/*.o', '**/*.so', '**/*.so.*', '**/*.a', '**/*.bundle', '**/*.jar', 'pkg', 'tmp'
|
20
|
+
|
21
|
+
require 'yard'
|
22
|
+
YARD_FILES = FileList['ext/extralite/extralite.c', 'lib/extralite.rb', 'lib/sequel/adapters/extralite.rb']
|
23
|
+
|
24
|
+
YARD::Rake::YardocTask.new do |t|
|
25
|
+
t.files = YARD_FILES
|
26
|
+
t.options = %w(-o doc --readme README.md)
|
27
|
+
end
|
data/ext/extralite/extralite.c
CHANGED
@@ -51,6 +51,10 @@ static VALUE Database_allocate(VALUE klass) {
|
|
51
51
|
} \
|
52
52
|
}
|
53
53
|
|
54
|
+
/* call-seq: initialize(path)
|
55
|
+
*
|
56
|
+
* Initializes a new SQLite database with the given path.
|
57
|
+
*/
|
54
58
|
|
55
59
|
VALUE Database_initialize(VALUE self, VALUE path) {
|
56
60
|
int rc;
|
@@ -72,6 +76,10 @@ VALUE Database_initialize(VALUE self, VALUE path) {
|
|
72
76
|
return Qnil;
|
73
77
|
}
|
74
78
|
|
79
|
+
/* call-seq: close
|
80
|
+
*
|
81
|
+
* Closes the database.
|
82
|
+
*/
|
75
83
|
VALUE Database_close(VALUE self) {
|
76
84
|
int rc;
|
77
85
|
Database_t *db;
|
@@ -86,6 +94,10 @@ VALUE Database_close(VALUE self) {
|
|
86
94
|
return self;
|
87
95
|
}
|
88
96
|
|
97
|
+
/* call-seq: closed?
|
98
|
+
*
|
99
|
+
* Returns true if the database is closed.
|
100
|
+
*/
|
89
101
|
VALUE Database_closed_p(VALUE self) {
|
90
102
|
Database_t *db;
|
91
103
|
GetDatabase(self, db);
|
@@ -104,7 +116,7 @@ inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
|
|
104
116
|
case SQLITE_TEXT:
|
105
117
|
return rb_str_new_cstr((char *)sqlite3_column_text(stmt, col));
|
106
118
|
case SQLITE_BLOB:
|
107
|
-
|
119
|
+
return rb_str_new((const char *)sqlite3_column_blob(stmt, col), (long)sqlite3_column_bytes(stmt, col));
|
108
120
|
default:
|
109
121
|
rb_raise(cError, "Unknown column type: %d", type);
|
110
122
|
}
|
@@ -124,16 +136,16 @@ static inline void bind_hash_parameter_values(sqlite3_stmt *stmt, VALUE hash) {
|
|
124
136
|
switch (TYPE(k)) {
|
125
137
|
case T_FIXNUM:
|
126
138
|
bind_parameter_value(stmt, NUM2INT(k), v);
|
127
|
-
|
139
|
+
break;
|
128
140
|
case T_SYMBOL:
|
129
141
|
k = rb_funcall(k, ID_TO_S, 0);
|
130
142
|
case T_STRING:
|
131
143
|
if(RSTRING_PTR(k)[0] != ':') k = rb_str_plus(rb_str_new2(":"), k);
|
132
144
|
int pos = sqlite3_bind_parameter_index(stmt, StringValuePtr(k));
|
133
145
|
bind_parameter_value(stmt, pos, v);
|
134
|
-
|
146
|
+
break;
|
135
147
|
default:
|
136
|
-
|
148
|
+
rb_raise(cError, "Cannot bind hash key value idx %d", i);
|
137
149
|
}
|
138
150
|
}
|
139
151
|
RB_GC_GUARD(keys);
|
@@ -342,6 +354,29 @@ VALUE safe_query_hash(VALUE arg) {
|
|
342
354
|
return result;
|
343
355
|
}
|
344
356
|
|
357
|
+
/* call-seq:
|
358
|
+
* query(sql, *parameters, &block)
|
359
|
+
* query_hash(sql, *parameters, &block)
|
360
|
+
*
|
361
|
+
* Runs a query returning rows as hashes (with symbol keys). If a block is
|
362
|
+
* given, it will be called for each row. Otherwise, an array containing all
|
363
|
+
* rows is returned.
|
364
|
+
*
|
365
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
366
|
+
* a list of values or as a hash mapping parameter names to values. When
|
367
|
+
* parameters are given as a least, the query should specify parameters using
|
368
|
+
* `?`:
|
369
|
+
*
|
370
|
+
* db.query('select * from foo where x = ?', 42)
|
371
|
+
*
|
372
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
373
|
+
* specified using a hash, where keys are either strings are symbols. String
|
374
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
375
|
+
*
|
376
|
+
* db.query('select * from foo where x = :bar', bar: 42)
|
377
|
+
* db.query('select * from foo where x = :bar', 'bar' => 42)
|
378
|
+
* db.query('select * from foo where x = :bar', ':bar' => 42)
|
379
|
+
*/
|
345
380
|
VALUE Database_query_hash(int argc, VALUE *argv, VALUE self) {
|
346
381
|
query_ctx ctx = { self, argc, argv, 0 };
|
347
382
|
return rb_ensure(safe_query_hash, (VALUE)&ctx, cleanup_stmt, (VALUE)&ctx);
|
@@ -376,6 +411,26 @@ VALUE safe_query_ary(VALUE arg) {
|
|
376
411
|
return result;
|
377
412
|
}
|
378
413
|
|
414
|
+
/* call-seq: query_ary(sql, *parameters, &block)
|
415
|
+
*
|
416
|
+
* Runs a query returning rows as arrays. If a block is given, it will be called
|
417
|
+
* for each row. Otherwise, an array containing all rows is returned.
|
418
|
+
*
|
419
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
420
|
+
* a list of values or as a hash mapping parameter names to values. When
|
421
|
+
* parameters are given as a least, the query should specify parameters using
|
422
|
+
* `?`:
|
423
|
+
*
|
424
|
+
* db.query_ary('select * from foo where x = ?', 42)
|
425
|
+
*
|
426
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
427
|
+
* specified using a hash, where keys are either strings are symbols. String
|
428
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
429
|
+
*
|
430
|
+
* db.query_ary('select * from foo where x = :bar', bar: 42)
|
431
|
+
* db.query_ary('select * from foo where x = :bar', 'bar' => 42)
|
432
|
+
* db.query_ary('select * from foo where x = :bar', ':bar' => 42)
|
433
|
+
*/
|
379
434
|
VALUE Database_query_ary(int argc, VALUE *argv, VALUE self) {
|
380
435
|
query_ctx ctx = { self, argc, argv, 0 };
|
381
436
|
return rb_ensure(safe_query_ary, (VALUE)&ctx, cleanup_stmt, (VALUE)&ctx);
|
@@ -405,6 +460,25 @@ VALUE safe_query_single_row(VALUE arg) {
|
|
405
460
|
return row;
|
406
461
|
}
|
407
462
|
|
463
|
+
/* call-seq: query_single_row(sql, *parameters)
|
464
|
+
*
|
465
|
+
* Runs a query returning a single row as a hash.
|
466
|
+
*
|
467
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
468
|
+
* a list of values or as a hash mapping parameter names to values. When
|
469
|
+
* parameters are given as a least, the query should specify parameters using
|
470
|
+
* `?`:
|
471
|
+
*
|
472
|
+
* db.query_single_row('select * from foo where x = ?', 42)
|
473
|
+
*
|
474
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
475
|
+
* specified using a hash, where keys are either strings are symbols. String
|
476
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
477
|
+
*
|
478
|
+
* db.query_single_row('select * from foo where x = :bar', bar: 42)
|
479
|
+
* db.query_single_row('select * from foo where x = :bar', 'bar' => 42)
|
480
|
+
* db.query_single_row('select * from foo where x = :bar', ':bar' => 42)
|
481
|
+
*/
|
408
482
|
VALUE Database_query_single_row(int argc, VALUE *argv, VALUE self) {
|
409
483
|
query_ctx ctx = { self, argc, argv, 0 };
|
410
484
|
return rb_ensure(safe_query_single_row, (VALUE)&ctx, cleanup_stmt, (VALUE)&ctx);
|
@@ -442,6 +516,26 @@ VALUE safe_query_single_column(VALUE arg) {
|
|
442
516
|
return result;
|
443
517
|
}
|
444
518
|
|
519
|
+
/* call-seq: query_single_column(sql, *parameters, &block)
|
520
|
+
*
|
521
|
+
* Runs a query returning single column values. If a block is given, it will be called
|
522
|
+
* for each value. Otherwise, an array containing all values is returned.
|
523
|
+
*
|
524
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
525
|
+
* a list of values or as a hash mapping parameter names to values. When
|
526
|
+
* parameters are given as a least, the query should specify parameters using
|
527
|
+
* `?`:
|
528
|
+
*
|
529
|
+
* db.query_single_column('select x from foo where x = ?', 42)
|
530
|
+
*
|
531
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
532
|
+
* specified using a hash, where keys are either strings are symbols. String
|
533
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
534
|
+
*
|
535
|
+
* db.query_single_column('select x from foo where x = :bar', bar: 42)
|
536
|
+
* db.query_single_column('select x from foo where x = :bar', 'bar' => 42)
|
537
|
+
* db.query_single_column('select x from foo where x = :bar', ':bar' => 42)
|
538
|
+
*/
|
445
539
|
VALUE Database_query_single_column(int argc, VALUE *argv, VALUE self) {
|
446
540
|
query_ctx ctx = { self, argc, argv, 0 };
|
447
541
|
return rb_ensure(safe_query_single_column, (VALUE)&ctx, cleanup_stmt, (VALUE)&ctx);
|
@@ -470,11 +564,34 @@ VALUE safe_query_single_value(VALUE arg) {
|
|
470
564
|
return value;
|
471
565
|
}
|
472
566
|
|
567
|
+
/* call-seq: query_single_value(sql, *parameters)
|
568
|
+
*
|
569
|
+
* Runs a query returning a single value from the first row.
|
570
|
+
*
|
571
|
+
* Query parameters to be bound to placeholders in the query can be specified as
|
572
|
+
* a list of values or as a hash mapping parameter names to values. When
|
573
|
+
* parameters are given as a least, the query should specify parameters using
|
574
|
+
* `?`:
|
575
|
+
*
|
576
|
+
* db.query_single_value('select x from foo where x = ?', 42)
|
577
|
+
*
|
578
|
+
* Named placeholders are specified using `:`. The placeholder values are
|
579
|
+
* specified using a hash, where keys are either strings are symbols. String
|
580
|
+
* keys can include or omit the `:` prefix. The following are equivalent:
|
581
|
+
*
|
582
|
+
* db.query_single_value('select x from foo where x = :bar', bar: 42)
|
583
|
+
* db.query_single_value('select x from foo where x = :bar', 'bar' => 42)
|
584
|
+
* db.query_single_value('select x from foo where x = :bar', ':bar' => 42)
|
585
|
+
*/
|
473
586
|
VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
|
474
587
|
query_ctx ctx = { self, argc, argv, 0 };
|
475
588
|
return rb_ensure(safe_query_single_value, (VALUE)&ctx, cleanup_stmt, (VALUE)&ctx);
|
476
589
|
}
|
477
590
|
|
591
|
+
/* call-seq: last_insert_rowid
|
592
|
+
*
|
593
|
+
* Returns the rowid of the last inserted row.
|
594
|
+
*/
|
478
595
|
VALUE Database_last_insert_rowid(VALUE self) {
|
479
596
|
Database_t *db;
|
480
597
|
GetOpenDatabase(self, db);
|
@@ -482,6 +599,10 @@ VALUE Database_last_insert_rowid(VALUE self) {
|
|
482
599
|
return INT2NUM(sqlite3_last_insert_rowid(db->sqlite3_db));
|
483
600
|
}
|
484
601
|
|
602
|
+
/* call-seq: changes
|
603
|
+
*
|
604
|
+
* Returns the number of changes made to the database by the last operation.
|
605
|
+
*/
|
485
606
|
VALUE Database_changes(VALUE self) {
|
486
607
|
Database_t *db;
|
487
608
|
GetOpenDatabase(self, db);
|
@@ -489,6 +610,10 @@ VALUE Database_changes(VALUE self) {
|
|
489
610
|
return INT2NUM(sqlite3_changes(db->sqlite3_db));
|
490
611
|
}
|
491
612
|
|
613
|
+
/* call-seq: filename
|
614
|
+
*
|
615
|
+
* Returns the database filename.
|
616
|
+
*/
|
492
617
|
VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
|
493
618
|
const char *db_name;
|
494
619
|
const char *filename;
|
@@ -501,6 +626,10 @@ VALUE Database_filename(int argc, VALUE *argv, VALUE self) {
|
|
501
626
|
return filename ? rb_str_new_cstr(filename) : Qnil;
|
502
627
|
}
|
503
628
|
|
629
|
+
/* call-seq: transaction_active?
|
630
|
+
*
|
631
|
+
* Returns true if a transaction is currently in progress.
|
632
|
+
*/
|
504
633
|
VALUE Database_transaction_active_p(VALUE self) {
|
505
634
|
Database_t *db;
|
506
635
|
GetOpenDatabase(self, db);
|
@@ -508,6 +637,10 @@ VALUE Database_transaction_active_p(VALUE self) {
|
|
508
637
|
return sqlite3_get_autocommit(db->sqlite3_db) ? Qfalse : Qtrue;
|
509
638
|
}
|
510
639
|
|
640
|
+
/* call-seq: load_extension(path)
|
641
|
+
*
|
642
|
+
* Loads an extension with the given path.
|
643
|
+
*/
|
511
644
|
VALUE Database_load_extension(VALUE self, VALUE path) {
|
512
645
|
Database_t *db;
|
513
646
|
GetOpenDatabase(self, db);
|
data/extralite.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.homepage = 'https://github.com/digital-fabric/extralite'
|
12
12
|
s.metadata = {
|
13
13
|
"source_code_uri" => "https://github.com/digital-fabric/extralite",
|
14
|
-
"documentation_uri" => "https://
|
14
|
+
"documentation_uri" => "https://www.rubydoc.info/gems/extralite",
|
15
15
|
"homepage_uri" => "https://github.com/digital-fabric/extralite",
|
16
16
|
"changelog_uri" => "https://github.com/digital-fabric/extralite/blob/master/CHANGELOG.md"
|
17
17
|
}
|
@@ -21,10 +21,10 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
s.required_ruby_version = '>= 2.6'
|
23
23
|
|
24
|
-
s.add_development_dependency 'rake-compiler', '1.1.
|
25
|
-
s.add_development_dependency 'minitest', '5.
|
24
|
+
s.add_development_dependency 'rake-compiler', '1.1.6'
|
25
|
+
s.add_development_dependency 'minitest', '5.15.0'
|
26
26
|
s.add_development_dependency 'simplecov', '0.17.1'
|
27
|
-
s.add_development_dependency '
|
28
|
-
|
27
|
+
s.add_development_dependency 'yard', '0.9.27'
|
28
|
+
|
29
29
|
s.add_development_dependency 'sequel', '5.51.0'
|
30
30
|
end
|
data/lib/extralite/version.rb
CHANGED
data/lib/extralite.rb
CHANGED
@@ -1 +1,21 @@
|
|
1
1
|
require_relative './extralite_ext'
|
2
|
+
|
3
|
+
# Extralite is a Ruby gem for working with SQLite databases
|
4
|
+
module Extralite
|
5
|
+
# A base class for Extralite exceptions
|
6
|
+
class Error < RuntimeError
|
7
|
+
end
|
8
|
+
|
9
|
+
# An exception representing an SQL error emitted by SQLite
|
10
|
+
class SQLError < Error
|
11
|
+
end
|
12
|
+
|
13
|
+
# An exception raised when an SQLite database is busy (locked by another
|
14
|
+
# thread or process)
|
15
|
+
class BusyError < Error
|
16
|
+
end
|
17
|
+
|
18
|
+
# An SQLite database
|
19
|
+
class Database
|
20
|
+
end
|
21
|
+
end
|
data/test/perf_ary.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/inline'
|
4
|
+
|
5
|
+
gemfile do
|
6
|
+
source 'https://rubygems.org'
|
7
|
+
gem 'sqlite3'
|
8
|
+
gem 'extralite', path: '..'
|
9
|
+
gem 'benchmark-ips'
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'benchmark/ips'
|
13
|
+
require 'fileutils'
|
14
|
+
|
15
|
+
DB_PATH = '/tmp/extralite_sqlite3_perf.db'
|
16
|
+
|
17
|
+
def prepare_database(count)
|
18
|
+
FileUtils.rm(DB_PATH) rescue nil
|
19
|
+
db = Extralite::Database.new(DB_PATH)
|
20
|
+
db.query('create table foo ( a integer primary key, b text )')
|
21
|
+
db.query('begin')
|
22
|
+
count.times { db.query('insert into foo (b) values (?)', "hello#{rand(1000)}" )}
|
23
|
+
db.query('commit')
|
24
|
+
end
|
25
|
+
|
26
|
+
def sqlite3_run(count)
|
27
|
+
db = SQLite3::Database.new(DB_PATH)
|
28
|
+
results = db.execute('select * from foo')
|
29
|
+
raise unless results.size == count
|
30
|
+
end
|
31
|
+
|
32
|
+
def extralite_run(count)
|
33
|
+
db = Extralite::Database.new(DB_PATH)
|
34
|
+
results = db.query_ary('select * from foo')
|
35
|
+
raise unless results.size == count
|
36
|
+
end
|
37
|
+
|
38
|
+
[10, 1000, 100000].each do |c|
|
39
|
+
puts; puts; puts "Record count: #{c}"
|
40
|
+
|
41
|
+
prepare_database(c)
|
42
|
+
|
43
|
+
Benchmark.ips do |x|
|
44
|
+
x.config(:time => 3, :warmup => 1)
|
45
|
+
|
46
|
+
x.report("sqlite3") { sqlite3_run(c) }
|
47
|
+
x.report("extralite") { extralite_run(c) }
|
48
|
+
|
49
|
+
x.compare!
|
50
|
+
end
|
51
|
+
end
|
File without changes
|
data/test/test_database.rb
CHANGED
@@ -138,6 +138,31 @@ end
|
|
138
138
|
r = @db.query('select x, y, z from t where z = :bazzz', ':bazzz' => 6)
|
139
139
|
assert_equal [{ x: 4, y: 5, z: 6 }], r
|
140
140
|
end
|
141
|
+
|
142
|
+
def test_parameter_binding_with_index_key
|
143
|
+
r = @db.query('select x, y, z from t where z = ?', 1 => 3)
|
144
|
+
assert_equal [{ x: 1, y: 2, z: 3 }], r
|
145
|
+
|
146
|
+
r = @db.query('select x, y, z from t where x = ?2', 1 => 42, 2 => 4)
|
147
|
+
assert_equal [{ x: 4, y: 5, z: 6 }], r
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_value_casting
|
151
|
+
r = @db.query_single_value("select 'abc'")
|
152
|
+
assert_equal 'abc', r
|
153
|
+
|
154
|
+
r = @db.query_single_value('select 123')
|
155
|
+
assert_equal 123, r
|
156
|
+
|
157
|
+
r = @db.query_single_value('select 12.34')
|
158
|
+
assert_equal 12.34, r
|
159
|
+
|
160
|
+
r = @db.query_single_value('select zeroblob(4)')
|
161
|
+
assert_equal "\x00\x00\x00\x00", r
|
162
|
+
|
163
|
+
r = @db.query_single_value('select null')
|
164
|
+
assert_nil r
|
165
|
+
end
|
141
166
|
end
|
142
167
|
|
143
168
|
class ScenarioTest < MiniTest::Test
|
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.
|
4
|
+
version: '1.10'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.1.
|
19
|
+
version: 1.1.6
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.1.
|
26
|
+
version: 1.1.6
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 5.
|
33
|
+
version: 5.15.0
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 5.
|
40
|
+
version: 5.15.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: simplecov
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,33 +53,19 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 0.17.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: yard
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
61
|
+
version: 0.9.27
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: pry
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - '='
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 0.13.1
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - '='
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 0.13.1
|
68
|
+
version: 0.9.27
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: sequel
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -102,6 +88,7 @@ extensions:
|
|
102
88
|
extra_rdoc_files:
|
103
89
|
- README.md
|
104
90
|
files:
|
91
|
+
- ".github/FUNDING.yml"
|
105
92
|
- ".github/workflows/test.yml"
|
106
93
|
- ".gitignore"
|
107
94
|
- CHANGELOG.md
|
@@ -119,7 +106,8 @@ files:
|
|
119
106
|
- lib/extralite/version.rb
|
120
107
|
- lib/sequel/adapters/extralite.rb
|
121
108
|
- test/helper.rb
|
122
|
-
- test/
|
109
|
+
- test/perf_ary.rb
|
110
|
+
- test/perf_hash.rb
|
123
111
|
- test/run.rb
|
124
112
|
- test/test_database.rb
|
125
113
|
- test/test_sequel.rb
|
@@ -128,7 +116,7 @@ licenses:
|
|
128
116
|
- MIT
|
129
117
|
metadata:
|
130
118
|
source_code_uri: https://github.com/digital-fabric/extralite
|
131
|
-
documentation_uri: https://
|
119
|
+
documentation_uri: https://www.rubydoc.info/gems/extralite
|
132
120
|
homepage_uri: https://github.com/digital-fabric/extralite
|
133
121
|
changelog_uri: https://github.com/digital-fabric/extralite/blob/master/CHANGELOG.md
|
134
122
|
post_install_message:
|