extralite 1.8.2 → 1.12

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: dd26d2937628e408ba8ee2f69e2e170755a43d5e1ffcdcbb82ca184cd5734576
4
- data.tar.gz: c762b88c6e1671de5daab90f6f6fb574bb4dfb43fb0a866e8154fb8ac02f755f
3
+ metadata.gz: ed6cb05e12c36bbbc92701af98639a9a92b91d9ba0fca0362b35fb12f3af676e
4
+ data.tar.gz: 6d7976fc5b39aa414a5a3431a9c5a1a6c517166d4e82de621b22da3be50fe38b
5
5
  SHA512:
6
- metadata.gz: 9ea096a28f15e8415b8e450a45035ffbe0c2186ebc7bba2ddd76c34e2328baaff307cee3c5079dda235992011497ef72b924bf3916628842e100ba8953ee7454
7
- data.tar.gz: 0efd84d3a7fa1fd1157df43f65bd4fa6999a0943efc8c0b36030a8550bbb2bc1c3a96509d4bf6c7a8a2bb9c4bd071dc0a650cb45e54e75c7ec722ee29c8bb72e
6
+ metadata.gz: 9baa147f13fa4498264e6e942e9c154bcdcd458f503944be9385f5ea61b82a8e6d381f77a75ad7a4c7643aa124cc2a4d5aa8ffb58400efa641dcb62fad810437
7
+ data.tar.gz: 789d203582c86f54a8f234b67d7b54b03150b900d25a5c76e81a57d779d1735fa7a8dc9822d121df7d08af4513481bf4c0a1a6d2fb1d6dfc54d80022b2b6e559
@@ -0,0 +1 @@
1
+ github: ciconia
@@ -7,8 +7,8 @@ jobs:
7
7
  strategy:
8
8
  fail-fast: false
9
9
  matrix:
10
- os: [ubuntu-latest]
11
- ruby: [2.6, 2.7, '3.0']
10
+ os: [ubuntu-latest, macos-10.15]
11
+ ruby: [2.7, 3.0, 3.1, truffleruby]
12
12
 
13
13
  name: >-
14
14
  ${{matrix.os}}, ${{matrix.ruby}}
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## 1.12 2022-02-15
2
+
3
+ - Add `Extralite.sqlite3_version` method
4
+ - Bundle sqlite3 in gem
5
+
6
+ ## 1.11 2021-12-17
7
+
8
+ - Fix compilation on MacOS (#3)
9
+
10
+ ## 1.10 2021-12-15
11
+
12
+ - Fix mutliple parameter binding with hash
13
+
14
+ ## 1.9 2021-12-15
15
+
16
+ - Add support for reading BLOBs
17
+
1
18
  ## 1.8.2 2021-12-15
2
19
 
3
20
  - Add documentation
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- extralite (1.8.2)
4
+ extralite (1.12)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  docile (1.4.0)
10
- json (2.5.1)
10
+ json (2.6.1)
11
11
  minitest (5.15.0)
12
12
  rake (13.0.6)
13
13
  rake-compiler (1.1.6)
@@ -34,4 +34,4 @@ DEPENDENCIES
34
34
  yard (= 0.9.27)
35
35
 
36
36
  BUNDLED WITH
37
- 2.1.4
37
+ 2.3.3
data/README.md CHANGED
@@ -1,17 +1,36 @@
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
 
32
+ - Zero dependencies: Extralite bundles SQLite3 version 3.37.2 - no need to
33
+ install any `libsqlite3` packages.
15
34
  - A variety of methods for different data access patterns: rows as hashes, rows
16
35
  as arrays, single row, single column, single value.
17
36
  - Super fast - [up to 12.5x faster](#performance) than the
@@ -28,13 +47,16 @@ interact with an SQLite3 database.
28
47
  - Get number of rows changed by last query.
29
48
  - Load extensions (loading of extensions is autmatically enabled. You can find
30
49
  some useful extensions here: https://github.com/nalgeon/sqlean.)
31
- - Includes a [Sequel adapter](#usage-with-sequel) (an ActiveRecord)
50
+ - Includes a [Sequel adapter](#usage-with-sequel).
32
51
 
33
52
  ## Usage
34
53
 
35
54
  ```ruby
36
55
  require 'extralite'
37
56
 
57
+ # get sqlite3 version
58
+ Extralite.sqlite3_version #=> "3.37.2"
59
+
38
60
  # open a database
39
61
  db = Extralite::Database.new('/tmp/my.db')
40
62
 
@@ -96,7 +118,7 @@ Extralite includes an adapter for
96
118
  just use the `extralite` scheme instead of `sqlite`:
97
119
 
98
120
  ```ruby
99
- DB = Sequel.connect('extralite:blog.db')
121
+ DB = Sequel.connect('extralite://blog.db')
100
122
  articles = DB[:articles]
101
123
  p articles.to_a
102
124
  ```
@@ -120,6 +142,7 @@ Here's a table summarizing the differences between the two gems:
120
142
 
121
143
  | |sqlite3-ruby|Extralite|
122
144
  |-|-|-|
145
+ |SQLite3 dependency|depends on OS-installed libsqlite3|bundles latest version of SQLite3|
123
146
  |API design|multiple classes|single class|
124
147
  |Query results|row as hash, row as array, single row, single value|row as hash, row as array, __single column__, single row, single value|
125
148
  |execute multiple statements|separate API (#execute_batch)|integrated|
@@ -127,7 +150,7 @@ Here's a table summarizing the differences between the two gems:
127
150
  |custom collations|yes|no|
128
151
  |custom aggregate functions|yes|no|
129
152
  |Multithread friendly|no|[yes](#what-about-concurrency)|
130
- |Code size|~2650LoC|~500LoC|
153
+ |Code size|~2650LoC|~530LoC|
131
154
  |Performance|1x|1.5x to 12.5x (see [below](#performance))|
132
155
 
133
156
  ## What about concurrency?
@@ -140,23 +163,33 @@ performance:
140
163
 
141
164
  ## Performance
142
165
 
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):
166
+ A benchmark script is included, creating a table of various row counts, then
167
+ fetching the entire table using either `sqlite3` or `extralite`. This benchmark
168
+ shows Extralite to be up to 12.5 times faster than `sqlite3` when fetching a
169
+ 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):
170
+
171
+ |Row count|sqlite3-ruby|Extralite|Advantage|
172
+ |-:|-:|-:|-:|
173
+ |10|57620 rows/s|95340 rows/s|__1.65x__|
174
+ |1K|286.8K rows/s|2106.4 rows/s|__7.35x__|
175
+ |100K|181K rows/s|2275.3K rows/s|__12.53x__|
149
176
 
150
- |Row count|sqlite3-ruby (baseline)|Extralite (relative - rounded)|
151
- |-:|-:|-:|
152
- |10|1x|1.5x|
153
- |1K|1x|7x|
154
- |100K|1x|12.5x|
177
+ When [fetching rows as arrays](https://github.com/digital-fabric/extralite/blob/main/test/perf_ary.rb) Extralite also significantly outperforms sqlite3-ruby:
178
+
179
+ |Row count|sqlite3-ruby|Extralite|Advantage|
180
+ |-:|-:|-:|-:|
181
+ |10|64365 rows/s|94031 rows/s|__1.46x__|
182
+ |1K|498.9K rows/s|2478.2K rows/s|__4.97x__|
183
+ |100K|441.1K rows/s|3023.4K rows/s|__6.85x__|
155
184
 
156
185
  (If you're interested in checking this yourself, just run the script and let me
157
- know if your results are different.)
186
+ know if your results are better/worse.)
187
+
188
+ As those benchmarks show, Extralite is capabale of reading more than 3M
189
+ rows/second (when fetching rows as arrays), and more than 2.2M rows/second (when
190
+ fetching rows as hashes.)
158
191
 
159
192
  ## Contributing
160
193
 
161
194
  Contributions in the form of issues, PRs or comments will be greatly
162
- appreciated!
195
+ appreciated!
@@ -1,115 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # require 'rubygems'
4
- # require 'mkmf'
5
-
6
- # # $CFLAGS << "-Wdiscarded-qualifier"
7
- # # $CFLAGS << " -Wno-comment"
8
- # # $CFLAGS << " -Wno-unused-result"
9
- # # $CFLAGS << " -Wno-dangling-else"
10
- # # $CFLAGS << " -Wno-parentheses"
11
-
12
- # dir_config 'extralite_ext'
13
- # create_makefile 'extralite_ext'
14
-
15
- ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
16
-
17
3
  require 'mkmf'
18
4
 
19
- # :stopdoc:
20
-
21
- RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
22
-
23
- ldflags = cppflags = nil
24
- if RbConfig::CONFIG["host_os"] =~ /darwin/
25
- begin
26
- if with_config('sqlcipher')
27
- brew_prefix = `brew --prefix sqlcipher`.chomp
28
- ldflags = "#{brew_prefix}/lib"
29
- cppflags = "#{brew_prefix}/include/sqlcipher"
30
- pkg_conf = "#{brew_prefix}/lib/pkgconfig"
31
- else
32
- brew_prefix = `brew --prefix sqlite3`.chomp
33
- ldflags = "#{brew_prefix}/lib"
34
- cppflags = "#{brew_prefix}/include"
35
- pkg_conf = "#{brew_prefix}/lib/pkgconfig"
36
- end
37
-
38
- # pkg_config should be less error prone than parsing compiler
39
- # commandline options, but we need to set default ldflags and cpp flags
40
- # in case the user doesn't have pkg-config installed
41
- ENV['PKG_CONFIG_PATH'] ||= pkg_conf
42
- rescue
43
- end
44
- end
45
-
46
- if with_config('sqlcipher')
47
- pkg_config("sqlcipher")
48
- else
49
- pkg_config("sqlite3")
50
- end
51
-
52
- # --with-sqlite3-{dir,include,lib}
53
- if with_config('sqlcipher')
54
- $CFLAGS << ' -DUSING_SQLCIPHER'
55
- dir_config("sqlcipher", cppflags, ldflags)
56
- else
57
- dir_config("sqlite3", cppflags, ldflags)
58
- end
59
-
60
- if RbConfig::CONFIG["host_os"] =~ /mswin/
61
- $CFLAGS << ' -W3'
62
- end
63
-
64
- if RUBY_VERSION < '2.7'
65
- $CFLAGS << ' -DTAINTING_SUPPORT'
66
- end
67
-
68
- def asplode missing
69
- if RUBY_PLATFORM =~ /mingw|mswin/
70
- abort "#{missing} is missing. Install SQLite3 from " +
71
- "http://www.sqlite.org/ first."
72
- else
73
- abort <<-error
74
- #{missing} is missing. Try 'brew install sqlite3',
75
- 'yum install sqlite-devel' or 'apt-get install libsqlite3-dev'
76
- and check your shared library search path (the
77
- location where your sqlite3 shared library is located).
78
- error
79
- end
80
- end
81
-
82
- asplode('sqlite3.h') unless find_header 'sqlite3.h'
83
- find_library 'pthread', 'pthread_create' # 1.8 support. *shrug*
84
-
85
- have_library 'dl' # for static builds
86
-
87
- if with_config('sqlcipher')
88
- asplode('sqlcipher') unless find_library 'sqlcipher', 'sqlite3_libversion_number'
89
- else
90
- asplode('sqlite3') unless find_library 'sqlite3', 'sqlite3_libversion_number'
91
- end
92
-
93
- # Functions defined in 1.9 but not 1.8
94
- have_func('rb_proc_arity')
95
-
96
- # Functions defined in 2.1 but not 2.0
97
- have_func('rb_integer_pack')
98
-
99
- # These functions may not be defined
100
- have_func('sqlite3_initialize')
101
- have_func('sqlite3_backup_init')
102
- have_func('sqlite3_column_database_name')
103
- have_func('sqlite3_enable_load_extension')
104
- have_func('sqlite3_load_extension')
105
-
106
- unless have_func('sqlite3_open_v2')
107
- abort "Please use a newer version of SQLite3"
108
- end
109
-
110
- have_func('sqlite3_prepare_v2')
111
- have_type('sqlite3_int64', 'sqlite3.h')
112
- have_type('sqlite3_uint64', 'sqlite3.h')
5
+ $CFLAGS << " -Wno-undef"
6
+ $CFLAGS << " -Wno-discarded-qualifiers"
113
7
 
114
8
  dir_config('extralite_ext')
115
- create_makefile('extralite_ext')
9
+ create_makefile('extralite_ext')
@@ -51,6 +51,15 @@ static VALUE Database_allocate(VALUE klass) {
51
51
  } \
52
52
  }
53
53
 
54
+ /* call-seq: sqlite3_version
55
+ *
56
+ * Returns the sqlite3 version used by Extralite.
57
+ */
58
+
59
+ VALUE Extralite_sqlite3_version(VALUE self) {
60
+ return rb_str_new_cstr(sqlite3_version);
61
+ }
62
+
54
63
  /* call-seq: initialize(path)
55
64
  *
56
65
  * Initializes a new SQLite database with the given path.
@@ -105,7 +114,7 @@ VALUE Database_closed_p(VALUE self) {
105
114
  return db->sqlite3_db ? Qfalse : Qtrue;
106
115
  }
107
116
 
108
- inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
117
+ static inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
109
118
  switch (type) {
110
119
  case SQLITE_NULL:
111
120
  return Qnil;
@@ -116,7 +125,7 @@ inline VALUE get_column_value(sqlite3_stmt *stmt, int col, int type) {
116
125
  case SQLITE_TEXT:
117
126
  return rb_str_new_cstr((char *)sqlite3_column_text(stmt, col));
118
127
  case SQLITE_BLOB:
119
- rb_raise(cError, "BLOB reading not yet implemented");
128
+ return rb_str_new((const char *)sqlite3_column_blob(stmt, col), (long)sqlite3_column_bytes(stmt, col));
120
129
  default:
121
130
  rb_raise(cError, "Unknown column type: %d", type);
122
131
  }
@@ -128,24 +137,24 @@ static void bind_parameter_value(sqlite3_stmt *stmt, int pos, VALUE value);
128
137
 
129
138
  static inline void bind_hash_parameter_values(sqlite3_stmt *stmt, VALUE hash) {
130
139
  VALUE keys = rb_funcall(hash, ID_KEYS, 0);
131
- int len = RARRAY_LEN(keys);
132
- for (int i = 0; i < len; i++) {
140
+ long len = RARRAY_LEN(keys);
141
+ for (long i = 0; i < len; i++) {
133
142
  VALUE k = RARRAY_AREF(keys, i);
134
143
  VALUE v = rb_hash_aref(hash, k);
135
144
 
136
145
  switch (TYPE(k)) {
137
146
  case T_FIXNUM:
138
147
  bind_parameter_value(stmt, NUM2INT(k), v);
139
- return;
148
+ break;
140
149
  case T_SYMBOL:
141
150
  k = rb_funcall(k, ID_TO_S, 0);
142
151
  case T_STRING:
143
152
  if(RSTRING_PTR(k)[0] != ':') k = rb_str_plus(rb_str_new2(":"), k);
144
153
  int pos = sqlite3_bind_parameter_index(stmt, StringValuePtr(k));
145
154
  bind_parameter_value(stmt, pos, v);
146
- return;
155
+ break;
147
156
  default:
148
- rb_raise(cError, "Cannot bind hash key value idx %d", i);
157
+ rb_raise(cError, "Cannot bind hash key value idx %ld", i);
149
158
  }
150
159
  }
151
160
  RB_GC_GUARD(keys);
@@ -218,7 +227,7 @@ struct multi_stmt_ctx {
218
227
  sqlite3 *db;
219
228
  sqlite3_stmt **stmt;
220
229
  const char *str;
221
- int len;
230
+ long len;
222
231
  int rc;
223
232
  };
224
233
 
@@ -256,7 +265,7 @@ statements. It will release the GVL while the statements are being prepared and
256
265
  executed. All statements excluding the last one are executed. The last statement
257
266
  is not executed, but instead handed back to the caller for looping over results.
258
267
  */
259
- inline void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
268
+ static inline void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql) {
260
269
  struct multi_stmt_ctx ctx = {db, stmt, RSTRING_PTR(sql), RSTRING_LEN(sql), 0};
261
270
  rb_thread_call_without_gvl(prepare_multi_stmt_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
262
271
  RB_GC_GUARD(sql);
@@ -284,7 +293,7 @@ void *stmt_iterate_without_gvl(void *ptr) {
284
293
  return NULL;
285
294
  }
286
295
 
287
- inline int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db) {
296
+ static inline int stmt_iterate(sqlite3_stmt *stmt, sqlite3 *db) {
288
297
  struct step_ctx ctx = {stmt, 0};
289
298
  rb_thread_call_without_gvl(stmt_iterate_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
290
299
  switch (ctx.rc) {
@@ -658,6 +667,8 @@ VALUE Database_load_extension(VALUE self, VALUE path) {
658
667
 
659
668
  void Init_Extralite() {
660
669
  VALUE mExtralite = rb_define_module("Extralite");
670
+ rb_define_singleton_method(mExtralite, "sqlite3_version", Extralite_sqlite3_version, 0);
671
+
661
672
  VALUE cDatabase = rb_define_class_under(mExtralite, "Database", rb_cObject);
662
673
  rb_define_alloc_func(cDatabase, Database_allocate);
663
674