extralite 1.8.1 → 1.11

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: 149ddf21bc7feaf4d7c3a19011ac9a515fe83d5f57b88c507783bee8c6d5e096
4
- data.tar.gz: 55507a0acbd8efeb89d5dfcf652441081285a670a5968a96f2696d3073844975
3
+ metadata.gz: d88bd81eca07e148be1f8d5b07d24c0aab49b32a9dc9aeb4a9ea5a74cf769491
4
+ data.tar.gz: 9f5d55a8cd4a58e2026ca3e850d48c28d86878e191e91fb92e59c76335a64484
5
5
  SHA512:
6
- metadata.gz: c7cdc5d0179c26c3deddc8439e4aaa5f91429ed3839cffc2224fefd8e90c78f0db9c4768f9534b693638572ec5e99ab0317a948f037c654262eca6d801a438f9
7
- data.tar.gz: 4ae156e6850e5485b31b1f701597c0692608b59facd1473ca064d0b23ca8b5d2d84995f4fe4c99800b3e01815fb32e21481290e369506e5d2c4e9794af8fe853
6
+ metadata.gz: cca614a4e3ffde8ff56ef3e8394f85f4a57d37f8615f2f144a9a39f84d50e51dd015348da7f93340326b40ff4b16202ec21bc03f23edacf678c03f57d0955974
7
+ data.tar.gz: 2e9f9c9091ac99fa96c3562fdb1e49642b2961ee682c30d98f8babd95b8366d9c96019df7185c5ef493b330f6c32066be6868398dcdb123c4607e9ab4875462b
@@ -0,0 +1 @@
1
+ github: ciconia
@@ -7,7 +7,7 @@ jobs:
7
7
  strategy:
8
8
  fail-fast: false
9
9
  matrix:
10
- os: [ubuntu-latest]
10
+ os: [ubuntu-latest, macos-10.15]
11
11
  ruby: [2.6, 2.7, '3.0']
12
12
 
13
13
  name: >-
data/CHANGELOG.md CHANGED
@@ -1,4 +1,16 @@
1
- ## 1.8 2021-12-15
1
+ ## 1.11 2021-12-17
2
+
3
+ - Fix compilation on MacOS (#3)
4
+
5
+ ## 1.10 2021-12-15
6
+
7
+ - Fix mutliple parameter binding with hash
8
+
9
+ ## 1.9 2021-12-15
10
+
11
+ - Add support for reading BLOBs
12
+
13
+ ## 1.8.2 2021-12-15
2
14
 
3
15
  - Add documentation
4
16
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- extralite (1.8)
4
+ extralite (1.11)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,14 +1,31 @@
1
- # Extralite - a Ruby gem for working with SQLite3 databases
2
-
3
- [![Gem Version](https://badge.fury.io/rb/extralite.svg)](http://rubygems.org/gems/extralite)
4
- [![Modulation Test](https://github.com/digital-fabric/extralite/workflows/Tests/badge.svg)](https://github.com/digital-fabric/extralite/actions?query=workflow%3ATests)
5
- [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/digital-fabric/extralite/blob/master/LICENSE)
1
+ <h1 align="center">
2
+ Extralite
3
+ </h1>
4
+
5
+ <h4 align="center">A fast Ruby gem for working with SQLite3 databases</h4>
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>
6
23
 
7
24
  ## What is Extralite?
8
25
 
9
- Extralite is an extra-lightweight (less than 430 lines of C-code) SQLite3
10
- wrapper for Ruby. It provides a single class with a minimal set of methods to
11
- interact with an SQLite3 database.
26
+ Extralite is a fast, extra-lightweight (less than 460 lines of C-code) SQLite3
27
+ wrapper for Ruby. It provides a single class with a minimal set of methods for
28
+ interacting with an SQLite3 database.
12
29
 
13
30
  ## Features
14
31
 
@@ -127,7 +144,7 @@ Here's a table summarizing the differences between the two gems:
127
144
  |custom collations|yes|no|
128
145
  |custom aggregate functions|yes|no|
129
146
  |Multithread friendly|no|[yes](#what-about-concurrency)|
130
- |Code size|~2650LoC|~500LoC|
147
+ |Code size|~2650LoC|~530LoC|
131
148
  |Performance|1x|1.5x to 12.5x (see [below](#performance))|
132
149
 
133
150
  ## What about concurrency?
@@ -140,23 +157,33 @@ performance:
140
157
 
141
158
  ## Performance
142
159
 
143
- A benchmark script is
144
- [included](https://github.com/digital-fabric/extralite/blob/main/test/perf.rb),
145
- creating a table of various row counts, then fetching the entire table using
146
- either `sqlite3` or `extralite`. This benchmark shows Extralite to be up to 12.5
147
- times faster than `sqlite3` when fetching a large number of rows. Here are the
148
- results (using the `sqlite3` gem performance as baseline):
160
+ A benchmark script is included, creating a table of various row counts, then
161
+ fetching the entire table using either `sqlite3` or `extralite`. This benchmark
162
+ shows Extralite to be up to 12.5 times faster than `sqlite3` when fetching a
163
+ 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):
164
+
165
+ |Row count|sqlite3-ruby|Extralite|Advantage|
166
+ |-:|-:|-:|-:|
167
+ |10|57620 rows/s|95340 rows/s|__1.65x__|
168
+ |1K|286.8K rows/s|2106.4 rows/s|__7.35x__|
169
+ |100K|181K rows/s|2275.3K rows/s|__12.53x__|
149
170
 
150
- |Row count|sqlite3-ruby (baseline)|Extralite (relative - rounded)|
151
- |-:|-:|-:|
152
- |10|1x|1.5x|
153
- |1K|1x|7x|
154
- |100K|1x|12.5x|
171
+ When [fetching rows as arrays](https://github.com/digital-fabric/extralite/blob/main/test/perf_ary.rb) Extralite also significantly outperforms sqlite3-ruby:
172
+
173
+ |Row count|sqlite3-ruby|Extralite|Advantage|
174
+ |-:|-:|-:|-:|
175
+ |10|64365 rows/s|94031 rows/s|__1.46x__|
176
+ |1K|498.9K rows/s|2478.2K rows/s|__4.97x__|
177
+ |100K|441.1K rows/s|3023.4K rows/s|__6.85x__|
155
178
 
156
179
  (If you're interested in checking this yourself, just run the script and let me
157
- know if your results are different.)
180
+ know if your results are better/worse.)
181
+
182
+ As those benchmarks show, Extralite is capabale of reading more than 3M
183
+ rows/second (when fetching rows as arrays), and more than 2.2M rows/second (when
184
+ fetching rows as hashes.)
158
185
 
159
186
  ## Contributing
160
187
 
161
188
  Contributions in the form of issues, PRs or comments will be greatly
162
- appreciated!
189
+ appreciated!
@@ -105,7 +105,7 @@ VALUE Database_closed_p(VALUE self) {
105
105
  return db->sqlite3_db ? Qfalse : Qtrue;
106
106
  }
107
107
 
108
- inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
108
+ static inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
109
109
  switch (type) {
110
110
  case SQLITE_NULL:
111
111
  return Qnil;
@@ -116,7 +116,7 @@ inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
116
116
  case SQLITE_TEXT:
117
117
  return rb_str_new_cstr((char *)sqlite3_column_text(stmt, col));
118
118
  case SQLITE_BLOB:
119
- rb_raise(cError, "BLOB reading not yet implemented");
119
+ return rb_str_new((const char *)sqlite3_column_blob(stmt, col), (long)sqlite3_column_bytes(stmt, col));
120
120
  default:
121
121
  rb_raise(cError, "Unknown column type: %d", type);
122
122
  }
@@ -128,24 +128,24 @@ static void bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value);
128
128
 
129
129
  static inline void bind_hash_parameter_values(sqlite3_stmt *stmt, VALUE hash) {
130
130
  VALUE keys = rb_funcall(hash, ID_KEYS, 0);
131
- int len = RARRAY_LEN(keys);
132
- for (int i = 0; i < len; i++) {
131
+ long len = RARRAY_LEN(keys);
132
+ for (long i = 0; i < len; i++) {
133
133
  VALUE k = RARRAY_AREF(keys, i);
134
134
  VALUE v = rb_hash_aref(hash, k);
135
135
 
136
136
  switch (TYPE(k)) {
137
137
  case T_FIXNUM:
138
138
  bind_parameter_value(stmt, NUM2INT(k), v);
139
- return;
139
+ break;
140
140
  case T_SYMBOL:
141
141
  k = rb_funcall(k, ID_TO_S, 0);
142
142
  case T_STRING:
143
143
  if(RSTRING_PTR(k)[0] != ':') k = rb_str_plus(rb_str_new2(":"), k);
144
144
  int pos = sqlite3_bind_parameter_index(stmt, StringValuePtr(k));
145
145
  bind_parameter_value(stmt, pos, v);
146
- return;
146
+ break;
147
147
  default:
148
- rb_raise(cError, "Cannot bind hash key value idx %d", i);
148
+ rb_raise(cError, "Cannot bind hash key value idx %ld", i);
149
149
  }
150
150
  }
151
151
  RB_GC_GUARD(keys);
@@ -218,7 +218,7 @@ struct multi_stmt_ctx {
218
218
  sqlite3 *db;
219
219
  sqlite3_stmt **stmt;
220
220
  const char *str;
221
- int len;
221
+ long len;
222
222
  int rc;
223
223
  };
224
224
 
@@ -256,7 +256,7 @@ statements. It will release the GVL while the statements are being prepared and
256
256
  executed. All statements excluding the last one are executed. The last statement
257
257
  is not executed, but instead handed back to the caller for looping over results.
258
258
  */
259
- inline void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
259
+ static inline void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
260
260
  struct multi_stmt_ctx ctx = {db, stmt, RSTRING_PTR(sql), RSTRING_LEN(sql), 0};
261
261
  rb_thread_call_without_gvl(prepare_multi_stmt_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
262
262
  RB_GC_GUARD(sql);
@@ -284,7 +284,7 @@ void *stmt_iterate_without_gvl(void *ptr) {
284
284
  return NULL;
285
285
  }
286
286
 
287
- inline int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db) {
287
+ static inline int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db) {
288
288
  struct step_ctx ctx = {stmt, 0};
289
289
  rb_thread_call_without_gvl(stmt_iterate_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
290
290
  switch (ctx.rc) {
data/extralite.gemspec CHANGED
@@ -11,6 +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://www.rubydoc.info/gems/extralite",
14
15
  "homepage_uri" => "https://github.com/digital-fabric/extralite",
15
16
  "changelog_uri" => "https://github.com/digital-fabric/extralite/blob/master/CHANGELOG.md"
16
17
  }
@@ -1,3 +1,3 @@
1
1
  module Extralite
2
- VERSION = '1.8.1'
2
+ VERSION = '1.11'
3
3
  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
@@ -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.8.1
4
+ version: '1.11'
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-15 00:00:00.000000000 Z
11
+ date: 2021-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -88,6 +88,7 @@ extensions:
88
88
  extra_rdoc_files:
89
89
  - README.md
90
90
  files:
91
+ - ".github/FUNDING.yml"
91
92
  - ".github/workflows/test.yml"
92
93
  - ".gitignore"
93
94
  - CHANGELOG.md
@@ -105,7 +106,8 @@ files:
105
106
  - lib/extralite/version.rb
106
107
  - lib/sequel/adapters/extralite.rb
107
108
  - test/helper.rb
108
- - test/perf.rb
109
+ - test/perf_ary.rb
110
+ - test/perf_hash.rb
109
111
  - test/run.rb
110
112
  - test/test_database.rb
111
113
  - test/test_sequel.rb
@@ -114,6 +116,7 @@ licenses:
114
116
  - MIT
115
117
  metadata:
116
118
  source_code_uri: https://github.com/digital-fabric/extralite
119
+ documentation_uri: https://www.rubydoc.info/gems/extralite
117
120
  homepage_uri: https://github.com/digital-fabric/extralite
118
121
  changelog_uri: https://github.com/digital-fabric/extralite/blob/master/CHANGELOG.md
119
122
  post_install_message: