activerecord-libsql 0.1.5 → 0.1.6

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: 91c46e715160345fb5ea4da1089dfcdf14c9f0cdfa57c07d3156487e5a82b5a6
4
- data.tar.gz: f1c7cb98472f91cd10d6859a731dcebb1db99c57889f22afd2374ca0a9c535fb
3
+ metadata.gz: f696c91f955a35679cd2b4159bedc0a9b8429cd30397fe9631054de7f63eb510
4
+ data.tar.gz: 58df65dd6bfd7aad0a4f187c9935064b4d8d21d57079c44df5f51c765148aca0
5
5
  SHA512:
6
- metadata.gz: 6f62ea58378238d64192c0e0875d303fc41c96bb1ccfe28f6849ec88f12f0ce3e314bfa3c762d77d69443808e03276f5c9f80400949dfd16e39d5561e4c8310c
7
- data.tar.gz: '094e0a7294dc60962b2c826de5cbc0fa3c156f4df2c33da9aaaeda6f38df8844ddb5f9319c6c6219ff7674d10fbad9d20aa3df98e020ff4945b3388b6926c712'
6
+ metadata.gz: 0a1ec1b42c809eb6cc91516796bdd7ece669396f22415f4940e5a1051780638774948e558aaeefbb22f9837052c86575061ed408ae71b24f018b726d66424ce9
7
+ data.tar.gz: 8a99cd050bae2dbb898bb74bb798afd7432a943f231efc10685b041ccd98b5a47449ad536f7216be53ed50d9a897bec37fcafd2f7d5f12fe7fd71e77921644ee
data/CHANGELOG.md ADDED
@@ -0,0 +1,128 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.1.6] - 2026-03-16
6
+
7
+ ### Fixed
8
+
9
+ - **`datetime` columns not stored in UTC, breaking `WHERE scheduled_at <= ?` comparisons**
10
+ - `NATIVE_DATABASE_TYPES` mapped `datetime`/`timestamp` to `'TEXT'`, causing `PRAGMA table_info`
11
+ to return `TEXT` for those columns. ActiveRecord's type map then resolved them to `Type::Text`,
12
+ which serializes `Time` objects via `to_s` without UTC normalization.
13
+ - Changed `datetime` → `'datetime'`, `timestamp` → `'datetime'`, `time` → `'time'`,
14
+ `date` → `'date'` so that `PRAGMA table_info` returns the correct type name and AR maps
15
+ them to `Type::DateTime` / `Type::Time` / `Type::Date`.
16
+ - `Type::DateTime` serializes `Time` objects to UTC strings (e.g. `"2026-03-16 04:18:14"`),
17
+ making string-based `<=` / `>=` comparisons consistent.
18
+ - This fixes Solid Queue's Dispatcher, which queries `WHERE scheduled_at <= ?` to find
19
+ due jobs — the query was always returning empty results when the server timezone was non-UTC.
20
+ - Added `initialize_type_map` override to explicitly register `datetime`/`timestamp` →
21
+ `Type::DateTime` as a safety net for existing databases with `TEXT`-typed datetime columns.
22
+
23
+ - **`cannot commit - no transaction is active` in Hrana HTTP client after fork**
24
+ - `TursoLibsql::Connection#commit_transaction` now rescues `RuntimeError` containing
25
+ `"no transaction is active"` or `"cannot commit"`, matching the existing behavior of
26
+ `LocalConnection` (SQLite3 backend) added in v0.1.4.
27
+ - Occurs when `ActiveSupport::ForkTracker` triggers `PoolConfig.discard_pools!` after fork,
28
+ and AR attempts to commit a transaction on the discarded connection before reconnecting.
29
+
30
+ ### Added
31
+
32
+ - **Solid Queue fork simulation integration tests** (`spec/integration/solid_queue_fork_spec.rb`)
33
+ - 5 examples reproducing the actual Solid Queue supervisor → worker fork flow.
34
+ - Verifies that child processes can `INSERT`, call `SolidQueue::Process.create!`,
35
+ run `FOR UPDATE SKIP LOCKED` queries, and that multiple concurrent workers all succeed.
36
+ - Verifies that the parent process continues to work after child forks.
37
+
38
+ ## [0.1.5] - 2026-03-13
39
+
40
+ ### Fixed
41
+
42
+ - **`FOR UPDATE SKIP LOCKED` syntax error with Solid Queue** (#12)
43
+ - Solid Queue uses `SELECT ... FOR UPDATE SKIP LOCKED` by default (`use_skip_locked: true`).
44
+ libSQL and SQLite do not support this clause, causing a parse error at the backend.
45
+ - `LibsqlAdapter#perform_query` now strips `FOR UPDATE` / `FOR UPDATE SKIP LOCKED` / `FOR SHARE`
46
+ before sending SQL to either the Hrana HTTP API or the local SQLite file.
47
+ - SQLite serializes all writes, so row-level locking is semantically unnecessary.
48
+ - Added a regression test that reproduces the exact query Solid Queue's Dispatcher sends.
49
+
50
+ ## [0.1.4] - 2026-03-13
51
+
52
+ ### Fixed
53
+
54
+ - **Fork safety for Embedded Replica mode** (#9)
55
+ - `sqlite3` gem 2.x automatically closes SQLite connections after `fork()` (ForkSafety).
56
+ In Solid Queue, the supervisor forks worker processes. If a transaction was open at fork
57
+ time, the child process would receive `cannot commit/rollback - no transaction is active`.
58
+ - `LocalConnection#commit_transaction` and `#rollback_transaction` now rescue
59
+ `SQLite3::Exception` and silently ignore the "no transaction is active" case.
60
+ - ActiveRecord's `discard!` already nils `@raw_connection`, so child processes reconnect
61
+ cleanly on the next query.
62
+
63
+ ### Changed
64
+
65
+ - Rakefile: removed `rb_sys` / `rake/extensiontask` dependencies (no longer needed after #7).
66
+ `task default` changed from `:compile` to `:spec`.
67
+ - `rake release` now uses `jj bookmark track` before `jj git push` to avoid the
68
+ "Refusing to create new remote bookmark" error for new version tags.
69
+
70
+ ## [0.1.3] - 2026-03-13
71
+
72
+ ### Changed
73
+
74
+ - **Replaced Rust native extension with pure Ruby implementation** (#7)
75
+ - The original Rust extension used `tokio` + `rustls` (and later `curl` + OpenSSL) for HTTP
76
+ transport. All Rust TLS implementations are unsafe after `fork()` on macOS, causing SEGV
77
+ or deadlocks in Puma / Unicorn / Solid Queue multi-process environments.
78
+ - New implementation uses Ruby's built-in `Net::HTTP` (fork-safe, no native dependencies).
79
+ - Hrana v2 HTTP protocol is implemented directly in `TursoLibsql::Connection`.
80
+ - `TursoLibsql::Database` provides a factory for remote, Embedded Replica, and offline modes.
81
+ - `TursoLibsql.reinitialize_runtime!` is now a no-op (kept for API compatibility).
82
+ - Added `fork_spec.rb` with 7 integration examples verifying fork safety.
83
+
84
+ ### Added
85
+
86
+ - `sqlite3 >= 1.4` runtime dependency for Embedded Replica / offline write mode.
87
+
88
+ ### Removed
89
+
90
+ - Rust extension (`ext/turso_libsql/`), `rb_sys`, and `rake-compiler` dependencies.
91
+ No `bundle exec rake compile` step is needed anymore.
92
+
93
+ ### Fixed
94
+
95
+ - `replica_sync`: SQL generation bug — `CREATE TABLE "foo" (...)` was being mangled.
96
+ Fixed by using `schema.sub(/\ACREATE TABLE\b/i, 'CREATE TABLE IF NOT EXISTS')`.
97
+ - Offline push: `results_as_hash = true` returns Hash rows; use `row.keys` / `row.values`.
98
+
99
+ ## [0.1.2] - 2026-03-12
100
+
101
+ ### Fixed
102
+
103
+ - `ActiveRecord::Result.empty` compatibility for `affected_rows` (#5).
104
+ `Result.empty` is a frozen singleton in AR 8; calling `.rows` on it is safe but
105
+ the adapter was incorrectly reading `affected_row_count` from it.
106
+ - Added Solid Queue integration tests (10 examples) covering `INSERT` into
107
+ `solid_queue_processes`, `solid_queue_jobs`, and related tables.
108
+
109
+ ## [0.1.1] - 2026-03-12
110
+
111
+ ### Fixed
112
+
113
+ - `Column.new` signature compatibility across ActiveRecord versions (#4):
114
+ - AR <= 8.0: `Column.new(name, default, sql_type_metadata, null)`
115
+ - AR >= 8.1: `Column.new(name, cast_type, default, sql_type_metadata, null)`
116
+ - Resolved at load time via `COLUMN_BUILDER` lambda to avoid per-call version checks.
117
+
118
+ ## [0.1.0] - 2026-03-10
119
+
120
+ ### Added
121
+
122
+ - Initial release: ActiveRecord adapter for Turso (libSQL) using a Rust native extension.
123
+ - Remote connection mode via libSQL remote protocol.
124
+ - Embedded Replica mode (`replica_path:`) with optional background sync (`sync_interval:`).
125
+ - Offline write mode (`offline: true`) — writes locally, syncs to remote on `#sync`.
126
+ - `rake turso:schema:apply` and `rake turso:schema:diff` via sqldef (#2).
127
+ - `rake build`, `rake install`, `rake release` tasks (#3).
128
+ - Unit and integration test suite — 57 examples (#1).
data/README.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  ActiveRecord adapter for [Turso](https://turso.tech) (libSQL) database.
4
4
 
5
- Connects Rails/ActiveRecord models to Turso via a native Rust extension ([magnus](https://github.com/matsadler/magnus) + [libsql](https://github.com/tursodatabase/libsql)), using the libSQL remote protocol directly — no HTTP client wrapper required.
5
+ Connects Rails / ActiveRecord models to Turso Cloud via the **Hrana v2 HTTP protocol**,
6
+ implemented in pure Ruby using `Net::HTTP` — no native extension, no Rust toolchain required.
6
7
 
7
8
  ## Requirements
8
9
 
9
10
  - Ruby >= 3.1
10
- - Rust >= 1.70 (install via [rustup](https://rustup.rs))
11
11
  - ActiveRecord >= 7.0
12
12
 
13
13
  ## Installation
@@ -19,9 +19,10 @@ gem "activerecord-libsql"
19
19
 
20
20
  ```bash
21
21
  bundle install
22
- bundle exec rake compile
23
22
  ```
24
23
 
24
+ No compile step needed. The gem is pure Ruby.
25
+
25
26
  ## Configuration
26
27
 
27
28
  ### database.yml
@@ -39,7 +40,8 @@ production:
39
40
  <<: *default
40
41
  ```
41
42
 
42
- > **Note**: Use the `database:` key, not `url:`. ActiveRecord tries to resolve the adapter from the URL scheme when `url:` is used, which causes a lookup failure.
43
+ > **Note**: Use the `database:` key, not `url:`. ActiveRecord tries to resolve the adapter
44
+ > from the URL scheme when `url:` is used, which causes a lookup failure.
43
45
 
44
46
  ### Direct connection
45
47
 
@@ -76,7 +78,8 @@ User.find(1).destroy
76
78
 
77
79
  ## Embedded Replicas
78
80
 
79
- Embedded Replicas keep a local SQLite copy of your Turso database on disk, synced from the remote. Reads are served locally (sub-millisecond), writes go to the remote.
81
+ Embedded Replicas keep a local SQLite copy of your Turso database on disk, synced from the
82
+ remote. Reads are served locally (sub-millisecond), writes go to the remote.
80
83
 
81
84
  ### Configuration
82
85
 
@@ -111,14 +114,46 @@ ActiveRecord::Base.connection.sync
111
114
 
112
115
  ### Notes
113
116
 
114
- - `replica_path` must point to a clean (empty) file or a previously synced replica. Using an existing SQLite file from another source will cause an error.
117
+ - `replica_path` must point to a writable path. The file is created automatically on first connect.
115
118
  - `sync_interval` is in seconds. Set to `0` or omit to use manual sync only.
116
- - **Multi-process caution**: Do not share the same `replica_path` across multiple Puma workers. Each worker should use a unique path (e.g. `/var/data/myapp-worker-#{worker_id}.db`).
117
- - The background sync task runs as long as the `Database` object is alive. The adapter holds the `Database` for the lifetime of the connection.
119
+ - **Multi-process (Puma / Solid Queue)**: Each worker process gets its own SQLite connection.
120
+ `sqlite3` gem 2.x handles fork safety automatically connections are closed after `fork()`
121
+ and reopened in the child process. Do not share the same `replica_path` across multiple
122
+ Puma workers; use a unique path per worker (e.g. `/var/data/myapp-worker-#{worker_id}.db`).
123
+
124
+ ## Solid Queue
125
+
126
+ This adapter is compatible with [Solid Queue](https://github.com/rails/solid_queue).
127
+
128
+ ### Known behaviour
129
+
130
+ - **`FOR UPDATE SKIP LOCKED`**: Solid Queue uses this clause by default. libSQL and SQLite
131
+ do not support it, so the adapter strips it automatically before sending SQL to the backend.
132
+ SQLite serializes all writes, so row-level locking is not needed.
133
+ - **Fork safety**: Solid Queue forks worker processes. The adapter handles this correctly —
134
+ `sqlite3` gem 2.x closes connections after `fork()`, and ActiveRecord's `discard!` /
135
+ `reconnect` flow re-establishes them in the child process.
136
+
137
+ ### Example config
138
+
139
+ ```yaml
140
+ # config/queue.yml
141
+ default: &default
142
+ dispatchers:
143
+ - polling_interval: 1
144
+ batch_size: 500
145
+ workers:
146
+ - queues: "*"
147
+ threads: 3
148
+ polling_interval: 0.1
149
+ ```
118
150
 
119
151
  ## Schema Management
120
152
 
121
- `turso:schema:apply` and `turso:schema:diff` use [sqldef](https://github.com/sqldef/sqldef) (`sqlite3def`) to manage your Turso schema declaratively — no migration files, no version tracking. You define the desired schema in a `.sql` file and the task computes and applies only the diff.
153
+ `turso:schema:apply` and `turso:schema:diff` use [sqldef](https://github.com/sqldef/sqldef)
154
+ (`sqlite3def`) to manage your Turso schema declaratively — no migration files, no version
155
+ tracking. You define the desired schema in a `.sql` file and the task computes and applies
156
+ only the diff.
122
157
 
123
158
  ### Prerequisites
124
159
 
@@ -129,7 +164,8 @@ brew install sqldef/sqldef/sqlite3def
129
164
  # Other platforms: https://github.com/sqldef/sqldef/releases
130
165
  ```
131
166
 
132
- `replica_path` must be configured in `database.yml` (the tasks use the local replica to compute the diff without touching the remote directly).
167
+ `replica_path` must be configured in `database.yml` (the tasks use the local replica to
168
+ compute the diff without touching the remote directly).
133
169
 
134
170
  ### turso:schema:apply
135
171
 
@@ -139,31 +175,6 @@ Applies the diff between your desired schema and the current remote schema.
139
175
  rake turso:schema:apply[db/schema.sql]
140
176
  ```
141
177
 
142
- Example output:
143
-
144
- ```
145
- ==> [1/4] Pulling latest schema from remote...
146
- Done.
147
- ==> [2/4] Computing schema diff...
148
- 2 statement(s) to apply:
149
- ALTER TABLE users ADD COLUMN bio TEXT;
150
- CREATE INDEX idx_users_email ON users (email);
151
- ==> [3/4] Applying schema to Turso Cloud...
152
- Done.
153
- ==> [4/4] Pulling to confirm...
154
- Done.
155
- ==> Schema applied successfully!
156
- ```
157
-
158
- If the schema is already up to date:
159
-
160
- ```
161
- ==> [1/4] Pulling latest schema from remote...
162
- Done.
163
- ==> [2/4] Computing schema diff...
164
- Already up to date.
165
- ```
166
-
167
178
  ### turso:schema:diff
168
179
 
169
180
  Shows what would be applied without making any changes (dry-run).
@@ -174,7 +185,8 @@ rake turso:schema:diff[db/schema.sql]
174
185
 
175
186
  ### schema.sql format
176
187
 
177
- Plain SQL `CREATE TABLE` statements. sqldef handles `ALTER TABLE` / `CREATE INDEX` / `DROP` automatically based on the diff.
188
+ Plain SQL `CREATE TABLE` statements. sqldef handles `ALTER TABLE` / `CREATE INDEX` / `DROP`
189
+ automatically based on the diff.
178
190
 
179
191
  ```sql
180
192
  CREATE TABLE users (
@@ -182,14 +194,6 @@ CREATE TABLE users (
182
194
  name TEXT NOT NULL,
183
195
  email TEXT NOT NULL
184
196
  );
185
-
186
- CREATE TABLE posts (
187
- id TEXT PRIMARY KEY,
188
- user_id TEXT NOT NULL,
189
- title TEXT NOT NULL,
190
- body TEXT,
191
- created_at TEXT NOT NULL
192
- );
193
197
  ```
194
198
 
195
199
  ## Architecture
@@ -198,21 +202,36 @@ CREATE TABLE posts (
198
202
  Rails Model (ActiveRecord)
199
203
  ↓ Arel → SQL string
200
204
  LibsqlAdapter (lib/active_record/connection_adapters/libsql_adapter.rb)
201
- ↓ perform_query / exec_update
202
- TursoLibsql::Database + Connection (Rust native extension)
203
- ↓ libsql::Database / Connection (async Tokio runtime block_on)
204
-
205
- Remote mode: Turso Cloud (libSQL remote protocol over HTTPS)
206
- Replica mode: Local SQLite file ←sync→ Turso Cloud
205
+ ↓ perform_query (strips FOR UPDATE, expands bind params)
206
+
207
+ ├─ Remote mode ──→ TursoLibsql::Connection (Hrana v2 HTTP via Net::HTTP)
208
+ │ ↓
209
+ Turso Cloud (HTTPS)
210
+
211
+ └─ Embedded Replica / Offline mode
212
+
213
+ TursoLibsql::LocalConnection (sqlite3 gem)
214
+
215
+ Local SQLite file ←─ sync ─→ Turso Cloud
207
216
  ```
208
217
 
218
+ ### Why pure Ruby?
219
+
220
+ The original implementation used a Rust native extension (`tokio` + `rustls`). On macOS,
221
+ all Rust TLS libraries are unsafe after `fork()` — they cause SEGV or deadlocks in
222
+ multi-process servers (Puma, Unicorn, Solid Queue). Ruby's `Net::HTTP` has no such
223
+ restriction and is fully fork-safe.
224
+
209
225
  ## Thread Safety
210
226
 
211
- `libsql::Connection` implements `Send + Sync`, making it thread-safe. ActiveRecord's `ConnectionPool` issues a separate `Adapter` instance per thread, so `@raw_connection` is never shared across threads.
227
+ ActiveRecord's `ConnectionPool` issues a separate `Adapter` instance per thread, so
228
+ `@raw_connection` is never shared across threads. `Net::HTTP` opens a new TCP connection
229
+ per request, which is safe for concurrent use.
212
230
 
213
231
  ## Performance
214
232
 
215
- Benchmarked against a **Turso cloud database** (remote, over HTTPS) from a MacBook on a home network. All numbers include full round-trip network latency.
233
+ Benchmarked against a **Turso cloud database** (remote, over HTTPS) from a MacBook on a
234
+ home network. All numbers include full round-trip network latency.
216
235
 
217
236
  | Operation | ops/sec | avg latency |
218
237
  |-----------|--------:|------------:|
@@ -224,26 +243,30 @@ Benchmarked against a **Turso cloud database** (remote, over HTTPS) from a MacBo
224
243
  | DELETE single row | 6.9 | 145.2 ms |
225
244
  | Transaction (10 inserts) | 1.9 | 539.0 ms |
226
245
 
227
- > **Environment**: Ruby 3.4.8 · ActiveRecord 8.1.2 · Turso cloud (remote) · macOS arm64
246
+ > **Environment**: Ruby 3.4.8 · ActiveRecord 8.1.2 · Turso cloud (remote) · macOS arm64
228
247
  > Run `bundle exec ruby bench/benchmark.rb` to reproduce.
229
248
 
230
- Latency is dominated by network round-trips to the Turso cloud endpoint. For lower latency, use [Embedded Replicas](#embedded-replicas) — reads are served from a local SQLite file with sub-millisecond latency.
249
+ Latency is dominated by network round-trips to the Turso cloud endpoint. For lower latency,
250
+ use [Embedded Replicas](#embedded-replicas) — reads are served from a local SQLite file with
251
+ sub-millisecond latency.
231
252
 
232
253
  ## Feature Support
233
254
 
234
255
  | Feature | Status |
235
256
  |---------|--------|
236
- | SELECT | ✅ |
237
- | INSERT | ✅ |
238
- | UPDATE | ✅ |
239
- | DELETE | ✅ |
257
+ | SELECT / INSERT / UPDATE / DELETE | ✅ |
240
258
  | Transactions | ✅ |
241
- | Migrations (basic) | ✅ |
259
+ | Migrations (basic DDL) | ✅ |
242
260
  | Schema management (sqldef) | ✅ |
243
- | Prepared statements | ✅ |
244
- | BLOB | ✅ |
245
- | NOT NULL / UNIQUE constraint errors → AR exceptions | ✅ |
246
- | Embedded Replica | ✅ |
261
+ | Bind parameters | ✅ |
262
+ | NOT NULL / UNIQUE / FK constraint → AR exceptions | ✅ |
263
+ | Embedded Replica (local reads) | ✅ |
264
+ | Offline write mode | ✅ |
265
+ | Solid Queue compatibility | ✅ |
266
+ | Fork safety (Puma / Solid Queue) | ✅ |
267
+ | Prepared statements (server-side) | ❌ libSQL HTTP does not support them |
268
+ | EXPLAIN | ❌ |
269
+ | Savepoints | ❌ |
247
270
 
248
271
  ## Testing
249
272
 
@@ -258,7 +281,8 @@ bundle exec rake spec:integration
258
281
  bundle exec rake spec:all
259
282
  ```
260
283
 
261
- Set `SKIP_INTEGRATION_TESTS=1` to skip integration tests in CI environments without Turso credentials.
284
+ Set `SKIP_INTEGRATION_TESTS=1` to skip integration tests in CI environments without Turso
285
+ credentials.
262
286
 
263
287
  ## License
264
288
 
@@ -19,6 +19,11 @@ module ActiveRecord
19
19
  ADAPTER_NAME = 'Turso'
20
20
 
21
21
  # SQLite 互換の型マッピング(libSQL は SQLite 方言)
22
+ # datetime / timestamp は 'datetime' を使う。
23
+ # 'TEXT' にすると PRAGMA table_info が 'TEXT' を返し、
24
+ # AR の type_map が Type::Text にマッピングしてしまう。
25
+ # Type::Text は Time を UTC に変換せず文字列化するため、
26
+ # WHERE scheduled_at <= ? の比較が文字列比較で壊れる。
22
27
  NATIVE_DATABASE_TYPES = {
23
28
  primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT',
24
29
  string: { name: 'TEXT' },
@@ -26,10 +31,10 @@ module ActiveRecord
26
31
  integer: { name: 'INTEGER' },
27
32
  float: { name: 'REAL' },
28
33
  decimal: { name: 'REAL' },
29
- datetime: { name: 'TEXT' },
30
- timestamp: { name: 'TEXT' },
31
- time: { name: 'TEXT' },
32
- date: { name: 'TEXT' },
34
+ datetime: { name: 'datetime' },
35
+ timestamp: { name: 'datetime' },
36
+ time: { name: 'time' },
37
+ date: { name: 'date' },
33
38
  binary: { name: 'BLOB' },
34
39
  boolean: { name: 'INTEGER' },
35
40
  json: { name: 'TEXT' }
@@ -394,10 +399,15 @@ module ActiveRecord
394
399
  def initialize_type_map(m = type_map)
395
400
  m.register_type(/^integer/i, Type::Integer.new)
396
401
  m.register_type(/^real/i, Type::Float.new)
397
- m.register_type(/^text/i, Type::String.new)
398
402
  m.register_type(/^blob/i, Type::Binary.new)
399
403
  m.register_type(/^boolean/i, Type::Boolean.new)
400
- m.register_type(/./, Type::String.new)
404
+ # datetime / timestamp は Type::DateTime を使う。
405
+ # libSQL は datetime を TEXT として保存するが、AR の型キャストで
406
+ # UTC に正規化してから保存する必要がある(文字列比較の一貫性のため)。
407
+ register_class_with_precision m, /^datetime/i, Type::DateTime
408
+ m.alias_type(/^timestamp/i, 'datetime')
409
+ m.register_type(/^text/i, Type::String.new)
410
+ m.register_type(/./, Type::String.new)
401
411
  end
402
412
 
403
413
  def fetch_type_metadata(sql_type)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Libsql
5
- VERSION = '0.1.5'
5
+ VERSION = '0.1.6'
6
6
  end
7
7
  end
@@ -41,6 +41,10 @@ module TursoLibsql
41
41
  end
42
42
 
43
43
  # トランザクションをコミットする
44
+ # fork 後に sqlite3 gem の ForkSafety が接続を強制クローズした場合や、
45
+ # baton が無効になった場合は "cannot commit - no transaction is active" が返る。
46
+ # AR の discard! → reconnect フローで子プロセスは正常に動作するため、
47
+ # このエラーは無視して良い。
44
48
  def commit_transaction
45
49
  requests = [
46
50
  { 'type' => 'execute', 'stmt' => { 'sql' => 'COMMIT' } },
@@ -49,6 +53,9 @@ module TursoLibsql
49
53
  resp = hrana_pipeline(@baton, requests)
50
54
  @baton = nil
51
55
  check_errors(resp)
56
+ rescue RuntimeError => e
57
+ raise unless e.message.include?('no transaction is active') ||
58
+ e.message.include?('cannot commit')
52
59
  end
53
60
 
54
61
  # トランザクションをロールバックする
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-libsql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - aileron
@@ -89,6 +89,7 @@ executables: []
89
89
  extensions: []
90
90
  extra_rdoc_files: []
91
91
  files:
92
+ - CHANGELOG.md
92
93
  - README.md
93
94
  - activerecord-libsql.gemspec
94
95
  - lib/active_record/connection_adapters/libsql_adapter.rb