extralite 2.3 → 2.5

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: 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'