extralite 2.3 → 2.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 130809d0ec92b7ae91e31f05e24c072500ef3096bda542a293469f1e206ff8eb
4
- data.tar.gz: b7236d13577003ed2ccd806eb31b3a6703d3126196147f78220504b7fd0264b8
3
+ metadata.gz: dd2ac51ceb97cc6476f169da43fd11d6dad00ac39d5e9fd5eb0d4842ce670f0e
4
+ data.tar.gz: f58478324015a1f1827c55ca8b2170b177b270cad0ab8102be225a414da34148
5
5
  SHA512:
6
- metadata.gz: 44b5ab1374077c97418b14581947aaa2dd20010590e21beaa755be9aaa6228b1408e5593c69f9f901f7935b0ba58494772cd9c0062b7632dc8c48d4ea54845bd
7
- data.tar.gz: 218b35977b8ce307b9e738dfb280bfcfa4e67910542d03cf11aaec36acbaf61ba74db5c10f5b2d21364384c9cfbc830f91f945d5417dd00b6b090895dadfe276
6
+ metadata.gz: 3051d9b5d2a2543be9bdafd5b46aab5574acefa8ca946eaf34f5b1a8097154748f90ab6ba1b7fd76d5bb2b92d97599b35748be0d815258b4b3a499c6c5cefef7
7
+ data.tar.gz: 148d29ccfff66b0f1531000d0773b8de98b98169594c08ed032fccd7e92ffe1333357aa3da2e168d2b629d4c7b41b14af373e705601bf384b9d4a4f39af9c4f6
data/.editorconfig ADDED
@@ -0,0 +1,6 @@
1
+ [*]
2
+ charset = utf-8
3
+ trim_trailing_whitespace = true
4
+ insert_final_newline = true
5
+ indent_style = space
6
+ indent_size = 2
@@ -0,0 +1,30 @@
1
+ name: Tests (extralite-bundle)
2
+
3
+ on: [push, pull_request]
4
+
5
+ concurrency:
6
+ group: tests-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }}
7
+ cancel-in-progress: true
8
+
9
+ jobs:
10
+ build:
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ os: [ubuntu-latest, macos-latest]
15
+ ruby: ['3.0', '3.1', '3.2', '3.3']
16
+
17
+ name: >-
18
+ ${{matrix.os}}, ${{matrix.ruby}}
19
+
20
+ runs-on: ${{matrix.os}}
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: ${{matrix.ruby}}
26
+ bundler-cache: true # 'bundle install' and cache
27
+ - name: Compile C-extension
28
+ run: EXTRALITE_BUNDLE=1 bundle exec rake compile
29
+ - name: Run tests
30
+ run: bundle exec rake test
@@ -1,21 +1,25 @@
1
- name: Tests
1
+ name: Tests (extralite)
2
2
 
3
3
  on: [push, pull_request]
4
4
 
5
+ concurrency:
6
+ group: tests-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }}
7
+ cancel-in-progress: true
8
+
5
9
  jobs:
6
10
  build:
7
11
  strategy:
8
12
  fail-fast: false
9
13
  matrix:
10
14
  os: [ubuntu-latest, macos-latest]
11
- ruby: ['2.7', '3.0', '3.1', '3.2', '3.3']
15
+ ruby: ['3.0', '3.1', '3.2', '3.3']
12
16
 
13
17
  name: >-
14
18
  ${{matrix.os}}, ${{matrix.ruby}}
15
19
 
16
20
  runs-on: ${{matrix.os}}
17
21
  steps:
18
- - uses: actions/checkout@v3
22
+ - uses: actions/checkout@v4
19
23
  - uses: ruby/setup-ruby@v1
20
24
  with:
21
25
  ruby-version: ${{matrix.ruby}}
data/.gitignore CHANGED
@@ -10,6 +10,9 @@
10
10
  /test/version_tmp/
11
11
  /tmp/
12
12
  lib/extralite_ext.*
13
+ Gemfile-bundle.lock
14
+ /cmake-build-debug/
15
+ CMakeLists.txt
13
16
 
14
17
  # Used by dotenv library to load environment variables.
15
18
  # .env
data/CHANGELOG.md CHANGED
@@ -1,3 +1,43 @@
1
+ # 2.5 2024-01-16
2
+
3
+ - Update bundled sqlite to version 3.45.0
4
+ - Implement `Database#batch_query` and related methods
5
+ [53](https://github.com/digital-fabric/extralite/issues/53)
6
+ - Accept more options in `Database#initialize`
7
+ [48](https://github.com/digital-fabric/extralite/issues/48)
8
+ - Fix `Database#pragma` to return single value when reading pragma value
9
+ - Accept database name in `Database#tables` method
10
+ - Improve `Database#batch_execute` - now accepts Enumerable and Callable
11
+ parameters [52](https://github.com/digital-fabric/extralite/issues/52)
12
+ - Rename `Database#execute_multi` to `Database#batch_execute`
13
+ - Implement Query#clone
14
+ [51](https://github.com/digital-fabric/extralite/issues/51)
15
+ - Add support for GC compaction
16
+ - Remove support for Ruby 2.7
17
+ - Implement Query#<< [49](https://github.com/digital-fabric/extralite/issues/49)
18
+ - Allow passing parameters in array
19
+ - Add support for ractors
20
+ [#50](https://github.com/digital-fabric/extralite/issues/50)
21
+
22
+ # 2.4 2023-12-24
23
+
24
+ - Implement GVL release threshold.
25
+ [#46](https://github.com/digital-fabric/extralite/pull/46)
26
+ - Implement write barriers for better GC performance.
27
+ - Add support for binding large numbers and symbols.
28
+ [#43](https://github.com/digital-fabric/extralite/pull/43)
29
+ - Implement Database#transaction.
30
+ [#42](https://github.com/digital-fabric/extralite/pull/42)
31
+ - Add support for binding BLOBs.
32
+ [#40](https://github.com/digital-fabric/extralite/pull/40)
33
+ - Minor fixes and cleanup of C-code.
34
+ - Fix `Database#inspect` for a closed database instance
35
+ [#37](https://github.com/digital-fabric/extralite/issues/37)
36
+ - Add support for binding named parameters from Struct and Data classes
37
+ [#30](https://github.com/digital-fabric/extralite/pull/30)
38
+ - Update bundled SQLite code to version 3.44.2
39
+ [#32](https://github.com/digital-fabric/extralite/pull/32)
40
+
1
41
  # 2.3 2023-11-12
2
42
 
3
43
  - Update bundled SQLite to version 3.44.0 (#29)
@@ -16,7 +56,8 @@
16
56
 
17
57
  - Fix Sequel migrations (#8)
18
58
  - Redesign prepared statement functionality (#24)
19
- - Rewrite `Extralite::PreparedStatement` into `Extralite::Query` with breaking API changes
59
+ - Rewrite `Extralite::PreparedStatement` into `Extralite::Query` with breaking
60
+ API changes
20
61
  - Add `Extralite::Iterator` class for external iteration
21
62
  - Add `Query#each_xxx`, `Query#to_a_xxx` method
22
63
  - Add `Query#eof?` method
@@ -65,7 +106,8 @@
65
106
  # 1.20 2023-01-21
66
107
 
67
108
  - Fix compilation error (#15 @sitano)
68
- - Add status methods `Extralite.runtime_status`, `Database#status`, `PreparedStatement#status` (#14 @sitano)
109
+ - Add status methods `Extralite.runtime_status`, `Database#status`,
110
+ `PreparedStatement#status` (#14 @sitano)
69
111
  - Add `Database#interrupt` (#13 @sitano)
70
112
  - Add `Database#backup` (#11 @sitano)
71
113
  - Derive `Extralite::Error` from `StandardError` (#10 @sitano)
@@ -187,4 +229,3 @@
187
229
  ## 0.1 2021-05-21
188
230
 
189
231
  - First release
190
-
data/Gemfile-bundle ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec name: 'extralite'
4
+
5
+ gem "extralite-bundle", git: "file://#{File.expand_path(__dir__)}", ref: `git rev-parse --abbrev-ref HEAD`.strip
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- extralite (2.3)
4
+ extralite (2.5)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  docile (1.4.0)
10
- json (2.6.3)
10
+ json (2.7.1)
11
11
  minitest (5.15.0)
12
12
  rake (13.1.0)
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.3.3
37
+ 2.4.22
data/README.md CHANGED
@@ -14,14 +14,13 @@ with an SQLite3 database, as well as prepared queries (prepared statements).
14
14
  Extralite comes in two flavors: the `extralite` gem which uses the
15
15
  system-installed sqlite3 library, and the `extralite-bundle` gem which bundles
16
16
  the latest version of SQLite
17
- ([3.44.0](https://sqlite.org/releaselog/3_44_0.html)), offering access to the
17
+ ([3.44.2](https://sqlite.org/releaselog/3_44_2.html)), offering access to the
18
18
  latest features and enhancements.
19
19
 
20
20
  ## Features
21
21
 
22
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).)
23
+ [sqlite3](https://github.com/sparklemotion/sqlite3-ruby) gem.
25
24
  - A variety of methods for different data access patterns: rows as hashes, rows
26
25
  as arrays, single row, single column, single value.
27
26
  - Prepared statements.
@@ -30,10 +29,12 @@ latest features and enhancements.
30
29
  - Use system-installed sqlite3, or the [bundled latest version of
31
30
  SQLite3](#installing-the-extralite-sqlite3-bundle).
32
31
  - Improved [concurrency](#concurrency) for multithreaded apps: the Ruby GVL is
33
- released while preparing SQL statements and while iterating over results.
32
+ released peridically while preparing SQL statements and while iterating over
33
+ results.
34
34
  - Automatically execute SQL strings containing multiple semicolon-separated
35
35
  queries (handy for creating/modifying schemas).
36
- - Execute the same query with multiple parameter lists (useful for inserting records).
36
+ - Execute the same query with multiple parameter lists (useful for inserting
37
+ records).
37
38
  - Load extensions (loading of extensions is autmatically enabled. You can find
38
39
  some useful extensions here: https://github.com/nalgeon/sqlean.)
39
40
  - Includes a [Sequel adapter](#usage-with-sequel).
@@ -48,6 +49,8 @@ gem 'extralite'
48
49
 
49
50
  You can also run `gem install extralite` if you just want to check it out.
50
51
 
52
+ __Note__: Extralite supports Ruby 3.0 and higher.
53
+
51
54
  ### Installing the Extralite-SQLite3 Bundle
52
55
 
53
56
  If you don't have sqlite3 installed on your system, do not want to use the
@@ -101,15 +104,37 @@ db.query_single_value("select 'foo'") #=> "foo"
101
104
 
102
105
  # parameter binding (works for all query_xxx methods)
103
106
  db.query_hash('select ? as foo, ? as bar', 1, 2) #=> [{ :foo => 1, :bar => 2 }]
107
+ db.query_hash('select ? as foo, ? as bar', [1, 2]) #=> [{ :foo => 1, :bar => 2 }]
108
+
109
+ # explicit parameter indexes
110
+ db.query_hash('select ?2 as foo, ?1 as bar, ?1 * ?2 as baz', 6, 7) #=> [{ :foo => 7, :bar => 6, :baz => 42 }
104
111
 
105
- # parameter binding of named parameters
112
+ # named parameters
106
113
  db.query('select * from foo where bar = :bar', bar: 42)
107
114
  db.query('select * from foo where bar = :bar', 'bar' => 42)
108
115
  db.query('select * from foo where bar = :bar', ':bar' => 42)
109
116
 
117
+ # parameter binding of named parameters from Struct and Data
118
+ SomeStruct = Struct.new(:foo, :bar)
119
+ db.query_single_column('select :bar', SomeStruct.new(41, 42)) #=> [42]
120
+ SomeData = Data.define(:foo, :bar)
121
+ db.query_single_column('select :bar', SomeData.new(foo: 41, bar: 42)) #=> [42]
122
+
123
+ # parameter binding for binary data (BLOBs)
124
+ db.execute('insert into foo values (?)', File.binread('/path/to/file'))
125
+ db.execute('insert into foo values (?)', Extralite::Blob.new('Hello, 世界!'))
126
+ db.execute('insert into foo values (?)', 'Hello, 世界!'.force_encoding(Encoding::ASCII_8BIT))
127
+
110
128
  # insert multiple rows
111
- db.execute_multi('insert into foo values (?)', ['bar', 'baz'])
112
- db.execute_multi('insert into foo values (?, ?)', [[1, 2], [3, 4]])
129
+ db.batch_execute('insert into foo values (?)', ['bar', 'baz'])
130
+ db.batch_execute('insert into foo values (?, ?)', [[1, 2], [3, 4]])
131
+
132
+ # batch execute from enumerable
133
+ db.batch_execute('insert into foo values (?)', 1..10)
134
+
135
+ # batch execute from block
136
+ source = [[1, 2], [2, 3], [3, 4]]
137
+ db.batch_execute('insert into foo values (?, ?)') { source.shift }
113
138
 
114
139
  # prepared queries
115
140
  query = db.prepare('select ? as foo, ? as bar') #=> Extralite::Query
@@ -166,6 +191,13 @@ db.pragma(:journal_mode) #=> 'wal'
166
191
  # load an extension
167
192
  db.load_extension('/path/to/extension.so')
168
193
 
194
+ # run queries in a transaction
195
+ db.transaction do
196
+ db.execute('insert into foo values (?)', 1)
197
+ db.execute('insert into foo values (?)', 2)
198
+ db.execute('insert into foo values (?)', 3)
199
+ end
200
+
169
201
  # close database
170
202
  db.close
171
203
  db.closed? #=> true
@@ -194,6 +226,23 @@ ensure
194
226
  end
195
227
  ```
196
228
 
229
+ ### Running transactions
230
+
231
+ In order to run multiple queries in a single transaction, use
232
+ `Database#transaction`, passing a block that runs the queries. You can specify
233
+ the [transaction
234
+ mode](https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions).
235
+ The default mode is `:immediate`:
236
+
237
+ ```ruby
238
+ db.transaction { ... } # Run an immediate transaction
239
+ db.transaction(:deferred) { ... } # Run a deferred transaction
240
+ db.transaction(:exclusive) { ... } # Run an exclusive transaction
241
+ ```
242
+
243
+ If an exception is raised in the given block, the transaction will be rolled
244
+ back. Otherwise, it is committed.
245
+
197
246
  ### Creating Backups
198
247
 
199
248
  You can use `Database#backup` to create backup copies of a database. The
@@ -295,11 +344,55 @@ p articles.to_a
295
344
 
296
345
  ## Concurrency
297
346
 
298
- Extralite releases the GVL while making blocking calls to the sqlite3 library,
299
- that is while preparing SQL statements and fetching rows. Releasing the GVL
300
- allows other threads to run while the sqlite3 library is busy compiling SQL into
301
- bytecode, or fetching the next row. This *does not* hurt Extralite's
302
- performance, as you can see:
347
+ ### The Ruby GVL
348
+
349
+ Extralite releases the [Ruby
350
+ GVL](https://www.speedshop.co/2020/05/11/the-ruby-gvl-and-scaling.html) while
351
+ making calls to the sqlite3 library that might block, such as when backing up a
352
+ database, or when preparing a query. This allows other threads to run while the
353
+ underlying sqlite3 library is busy preparing queries, fetching records and
354
+ backing up databases.
355
+
356
+ Extralite also releases the GVL periodically when iterating over records. By
357
+ default, the GVL is released every 1000 records iterated. The GVL release
358
+ threshold can be set separately for each database:
359
+
360
+ ```ruby
361
+ db.gvl_release_threshold = 10 # release GVL every 10 records
362
+
363
+ db.gvl_release_threshold = nil # use default value (currently 1000)
364
+ ```
365
+
366
+ For most applications, there's no need to tune the GVL threshold value, as it
367
+ provides [excellent](#performance) performance characteristics for both
368
+ single-threaded and multi-threaded applications.
369
+
370
+ In a heavily multi-threaded application, releasing the GVL more often (lower
371
+ threshold value) will lead to less latency (for threads not running a query),
372
+ but will also hurt the throughput (for the thread running the query). Releasing
373
+ the GVL less often (higher threshold value) will lead to better throughput for
374
+ queries, while increasing latency for threads not running a query. The following
375
+ diagram demonstrates the relationship between the GVL release threshold value,
376
+ latency and throughput:
377
+
378
+ ```
379
+ less latency & throughput <<< GVL release threshold >>> more latency & throughput
380
+ ```
381
+
382
+ ### Thread Safety
383
+
384
+ A single database instance can be safely used in multiple threads simultaneously
385
+ as long as the following conditions are met:
386
+
387
+ - No explicit transactions are used.
388
+ - Each thread issues queries by calling `Database#query_xxx`, or uses a separate
389
+ `Query` instance.
390
+ - The GVL release threshold is not `0` (i.e. the GVL is released periodically
391
+ while running queries.)
392
+
393
+ ### Use with Ractors
394
+
395
+ Extralite databases can be used inside ractors
303
396
 
304
397
  ## Performance
305
398
 
data/TODO.md CHANGED
@@ -1,7 +1,5 @@
1
- - Improve tracing
2
1
  - Transactions and savepoints:
3
2
 
4
- - `DB#transaction {}` - does a `BEGIN..COMMIT` - non-reentrant!
5
3
  - `DB#savepoint(name)` - creates a savepoint
6
4
  - `DB#release(name)` - releases a savepoint
7
5
  - `DB#rollback` - raises `Extralite::Rollback`, which is rescued by `DB#transaction`
@@ -10,7 +8,6 @@
10
8
  - More database methods:
11
9
 
12
10
  - `Database#quote`
13
- - `Database#busy_timeout=` https://sqlite.org/c3ref/busy_timeout.html
14
11
  - `Database#cache_flush` https://sqlite.org/c3ref/db_cacheflush.html
15
12
  - `Database#release_memory` https://sqlite.org/c3ref/db_release_memory.html
16
13
 
@@ -9,10 +9,10 @@ require 'date'
9
9
 
10
10
  FileUtils.cd '/tmp'
11
11
 
12
- version_id = version.gsub('.', '')
12
+ version_id = version.split('.').each_with_index.map { |v, i| i == 0 ? v : v.rjust(2, '0') }.join
13
13
  version_id += '0' * (7 - version_id.length)
14
14
  url = "https://sqlite.org/#{Date.today.year}/sqlite-amalgamation-#{version_id}.zip"
15
- dest = File.expand_path('../ext/extralite', __dir__)
15
+ dest = File.expand_path('../ext/sqlite3', __dir__)
16
16
 
17
17
  puts "Downloading from #{url}..."
18
18
  `curl #{url} > #{version_id}.zip`
@@ -23,4 +23,11 @@ puts "Unzipping zip file..."
23
23
  puts "Copying source files"
24
24
  `cp sqlite-amalgamation-#{version_id}/sqlite3.* #{dest}/`
25
25
 
26
- puts 'Done updating source files'
26
+ puts "Updating README"
27
+ readme_path = File.expand_path('../README.md', __dir__)
28
+ readme = File.read(readme_path)
29
+ readme.gsub!(/\[\d+\.\d+\.\d+\]/, "[#{version}]")
30
+ readme.gsub!(/\d+_\d+_\d+\.html/, "#{version.gsub('.', '_')}.html")
31
+ File.write(readme_path, readme)
32
+
33
+ puts 'Done updating source files'