libsql-sequel 0.3.0 → 0.3.1

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: 37c836e635f6104c6972774e8cb3dba6397cfbf5b6c958c19f15eaaca4fc1367
4
- data.tar.gz: adce3fe4850eab4d30b4b303f404520f969390060dded1b89def96675d2f7249
3
+ metadata.gz: 0b85ae761e49c7fc3ed4146cdf94f244d1cae27a1642b57fe7ebc9e00265211e
4
+ data.tar.gz: e7a634b05315f2795010cfbb8719ae8edbf70c4fd942950a33b56c77a5d59adf
5
5
  SHA512:
6
- metadata.gz: d02c3ee895aa85b2e4bae54743d0fc8b8b0028a8170b9b87212caeab594798b080c52dba582b2fd882a19e23a3aea5989ba5f0b53dc33ba68f7c924cb544b606
7
- data.tar.gz: 8c777ebc4996b0d96a5e4fd437337347568820f5a8a3adc2569ce69ad603f6b133acb386e6f8c2e59a641789b62f7c8aef780e66234908ba05e28e394276df37
6
+ metadata.gz: ca3aa0de34530d183c699699380c984a1bf84c52e2f509ac90c433da17abb334db72083fd1d820932b8adef32be47d4a9a3d80f7765a722368d9da417454e2eb
7
+ data.tar.gz: 34d675b2abcaf4435812d98b38ccf3a0c6931ca52e4652bf49a1d77df38538f14b2f5bb1c78ab1a2fc4ca7663d3882ee3d18be7d9e096aa7b0da3774e1e44496
data/README.md CHANGED
@@ -30,13 +30,13 @@ Install from [RubyGems](https://rubygems.org/gems/libsql-sequel):
30
30
 
31
31
  ```ruby
32
32
  # Gemfile
33
- gem 'libsql-sequel', '~> 0.3.0'
33
+ gem 'libsql-sequel', '~> 0.3.1'
34
34
  ```
35
35
 
36
36
  Then run `bundle install`. To install without Bundler:
37
37
 
38
38
  ```sh
39
- gem install libsql-sequel -v 0.3.0
39
+ gem install libsql-sequel -v 0.3.1
40
40
  ```
41
41
 
42
42
  Ruby 3.3+ and Sequel 5.80–5.x are required. The native dependency is **pinned to `turso_libsql` 0.2.0**: 0.2.1 contains native debug output of connection credentials and SQL. Do not widen that dependency without reviewing the replacement library. The adapter uses the bundled native library with scoped corrections for the driver's Ruby wrapper; it does not patch global `Libsql` classes or require `sqlite3` at runtime.
@@ -135,6 +135,7 @@ bundle exec rake lint # syntax and method visibility only
135
135
  bundle exec rake test # real native local/file databases
136
136
  bundle exec rake upstream # upstream SQLite + integration specs
137
137
  bundle exec rake remote_local # real native HTTP client, local Hrana server
138
+ bundle exec rake performance # local end-to-end timing and allocation benchmarks
138
139
  ```
139
140
 
140
141
  `upstream` and `remote_local` need the ignored reference checkouts; setup instructions are in [development.md](docs/development.md). `rake` defaults to `check`. Checks follow Sequel's own syntax/warnings/visibility approach; Sequel does not configure RuboCop or a complexity analyzer. See [the spec and code-quality review](docs/testing-review.md).
@@ -142,3 +143,8 @@ bundle exec rake remote_local # real native HTTP client, local Hrana server
142
143
  For an explicit test against a **disposable** hosted database, set `LIBSQL_TEST_URL` and `LIBSQL_TEST_AUTH_TOKEN` in your environment, then run `bundle exec rake remote`. It creates randomly named test tables and drops those tables afterward. Hosted authentication and HTTPS remote operations have been validated with a `turso://` URL; embedded replication remains unverified. See [compatibility and verification](docs/compatibility.md) for the tested scope.
143
144
 
144
145
  See [architecture](docs/architecture.md), [compatibility](docs/compatibility.md), and [the local example](examples/local.rb).
146
+
147
+ The [performance suite](test/performance/README.md) covers queries/chaining, bound
148
+ and prepared operations, result decoding, CRUD, models, transactions, bulk writes,
149
+ schema operations, and file-backed connection concurrency. It includes correctness
150
+ checks, JSON before/after comparisons, and a Sequel SQLite reference mode.
data/docs/architecture.md CHANGED
@@ -34,6 +34,15 @@ Sequel's cached placeholder literalizer optimization is disabled because it repl
34
34
 
35
35
  Numeric timestamps follow Sequel's SQLite convention: Integer means Unix seconds and Float means Julian day. Numeric TIME values preserve microseconds. BLOB columns containing integer/real/text storage values convert through their string representation, and nonnumeric text in a decimal column remains text, matching Sequel's SQLite adapter.
36
36
 
37
+ Result decoding resolves column types once per result, then uses an indexed loop
38
+ to assemble row hashes, following Sequel's SQLite adapter. The native column count
39
+ is also resolved once per iteration over a result. Signed-integer bounds are
40
+ constants rather than repeated Bignum arithmetic per cell/bind. Native text input
41
+ can reuse a frozen UTF-8 string; mutable strings are still copied before calls
42
+ that release the GVL. These optimizations preserve per-row cleanup and schema
43
+ invalidation; they introduce no statement or result-type cache. The local
44
+ [performance suite](../test/performance/README.md) records timing and allocations.
45
+
37
46
  Sequel invalidates its own schema cache for schema DSL changes. External schema changes and arbitrary DDL via `run` follow Sequel's usual cache rules; reload the affected schema or reconnect the Database object when needed. No extra unbounded type cache is introduced.
38
47
 
39
48
  ## Driver boundary
data/docs/development.md CHANGED
@@ -51,11 +51,21 @@ The fixture needs a free localhost port 8080. It creates a temporary SQLite file
51
51
 
52
52
  For a disposable hosted database, export `LIBSQL_TEST_URL` and `LIBSQL_TEST_AUTH_TOKEN`, then run `bundle exec rake remote`. Tests create randomly named tables and drop only those tables. Do not point destructive integration fixtures from another project at a production database.
53
53
 
54
+ ## Performance benchmarks
55
+
56
+ See [`test/performance/README.md`](../test/performance/README.md) for the standalone
57
+ local native benchmark suite and [`results.md`](../test/performance/results.md)
58
+ for measured optimization results. `bundle exec rake performance:smoke` checks all
59
+ workloads with small fixtures on libSQL memory/file databases and a SQLite file
60
+ database. `bundle exec rake performance` runs the measured memory suite; use the
61
+ runner directly for file mode, filtering, longer samples, and JSON output.
62
+ Benchmarks are opt-in and do not impose noisy wall-clock thresholds on `rake check`.
63
+
54
64
  ## Packaging
55
65
 
56
66
  ```sh
57
67
  mkdir -p pkg
58
- gem build libsql-sequel.gemspec --output pkg/libsql-sequel-0.3.0.gem
68
+ gem build libsql-sequel.gemspec --output pkg/libsql-sequel-0.3.1.gem
59
69
  ```
60
70
 
61
71
  The explicit gem file list includes Ruby library files, public docs, examples, README, and LICENSE. It excludes private notes, cloned repositories, credentials, tests, and temporary databases. Nothing is automatically published.
@@ -132,12 +132,13 @@ module Sequel
132
132
 
133
133
  def each
134
134
  check_open
135
+ column_count = columns.length
135
136
  loop do
136
137
  check_open
137
138
  row = Native.check(Native.rows_next(@rows))
138
139
  break if row[:inner].null?
139
140
  begin
140
- values = Array.new(columns.length) do |index|
141
+ values = Array.new(column_count) do |index|
141
142
  Native.value(Native.check(CLibsql.libsql_row_value(row, index))[:ok])
142
143
  end
143
144
  yield values
@@ -27,13 +27,18 @@ module Sequel
27
27
  execute(sql) do |result|
28
28
  columns = result.columns.map{|column| output_identifier(column)}
29
29
  types = ResultTypes.new(self).for_columns(columns)
30
+ column_types = columns.map{|name| types[name]}
31
+ column_count = columns.length
30
32
  converter = TypeConverter.new(db)
31
33
  self.columns = columns
32
34
  result.each do |values|
33
35
  row = {}
34
- columns.each_with_index do |name, index|
36
+ index = 0
37
+ while index < column_count
35
38
  value = values[index]
36
- row[name] = converter.call(value, types[name])
39
+ type = column_types[index]
40
+ row[columns[index]] = type ? converter.call(value, type) : value
41
+ index += 1
37
42
  end
38
43
  yield row
39
44
  end
@@ -11,6 +11,11 @@ module Sequel
11
11
  module Native
12
12
  class Error < StandardError; end
13
13
 
14
+ # Hoist Bignum arithmetic out of the per-cell decode and bind paths.
15
+ INTEGER_MIN = -(1 << 63)
16
+ INTEGER_LIMIT = 1 << 63
17
+ INTEGER_MODULUS = 1 << 64
18
+
14
19
  extend FFI::Library
15
20
  ffi_lib(*CLibsql.ffi_libraries.map(&:name))
16
21
 
@@ -47,7 +52,9 @@ module Sequel
47
52
  end
48
53
 
49
54
  def self.string(value)
50
- value = value.to_s.encode(Encoding::UTF_8)
55
+ value = value.to_s
56
+ # Keep a stable copy of mutable input while native calls release the GVL.
57
+ value = value.encode(Encoding::UTF_8) unless value.frozen? && value.encoding == Encoding::UTF_8
51
58
  raise Sequel::Error, 'libSQL text cannot contain NUL bytes; use Sequel.blob for binary data' if value.include?("\0")
52
59
  raise Sequel::Error, 'libSQL text must be valid UTF-8' unless value.valid_encoding?
53
60
  value
@@ -69,17 +76,17 @@ module Sequel
69
76
  end
70
77
 
71
78
  def self.value(value)
72
- case value[:type]
79
+ case type = value[:type]
73
80
  when :null
74
81
  nil
75
82
  when :integer
76
83
  # The Ruby driver declares uint64, while the C API returns int64.
77
84
  integer = value[:value][:integer]
78
- integer >= (1 << 63) ? integer - (1 << 64) : integer
85
+ integer >= INTEGER_LIMIT ? integer - INTEGER_MODULUS : integer
79
86
  when :real
80
87
  value[:value][:real]
81
88
  when :text, :blob
82
- read_slice(value[:value][value[:type]], value[:type] == :text)
89
+ read_slice(value[:value][type], type == :text)
83
90
  else
84
91
  raise Error, 'unknown libSQL value type'
85
92
  end
@@ -91,7 +98,7 @@ module Sequel
91
98
  when nil
92
99
  yield CLibsql.libsql_null
93
100
  when Integer
94
- unless (-(1 << 63)...(1 << 63)).cover?(value)
101
+ unless value >= INTEGER_MIN && value < INTEGER_LIMIT
95
102
  raise Sequel::Error, 'libSQL integers must fit in signed 64 bits'
96
103
  end
97
104
  yield CLibsql.libsql_integer(value)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sequel
4
4
  module LibSQL
5
- VERSION = '0.3.0'
5
+ VERSION = '0.3.1'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libsql-sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francesco Canessa