activerecord-turso 0.2.1 → 0.2.2
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 +4 -4
- data/README.md +28 -9
- data/lib/active_record/connection_adapters/turso_adapter/error_translation.rb +3 -1
- data/lib/activerecord-turso/error.rb +6 -0
- data/lib/activerecord-turso/version.rb +1 -1
- data/lib/activerecord-turso.rb +1 -0
- data/lib/turso/ar/connection.rb +7 -103
- metadata +6 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b8e00bcf26be0b39f02a05bd0f4850db41d277f7d72bdb15013c218f032d93eb
|
|
4
|
+
data.tar.gz: 9e9ccad1e346648fef21eb2d0a3aceafa7afada0bc67348a0ed912b723e6cca5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c16dce07f7dbcf2bd60d07e58b10d6960c7d4a2945f88deda0082c532b0e1e42cc58213582acd2548c0c19d72a13ef30e5688503ba8ab9a00b7fa99f882048e8
|
|
7
|
+
data.tar.gz: 22f295b0eda449f11b5f31c63b16718e5f609452904b0c9c5b534117fe4145324460d3ed56f7ac7365f38345ac165eb8969a3e01bb5c21d59c2e9f3bef2f4f4d
|
data/README.md
CHANGED
|
@@ -4,12 +4,12 @@ ActiveRecord adapter for [Turso](https://turso.tech) (SQLite compatible db).
|
|
|
4
4
|
|
|
5
5
|
## Status
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Production ready for ActiveRecord 8.1 applications. The underlying `turso` gem releases the GVL during blocking operations, supports fiber-aware connection ownership, and uses native batch execution.
|
|
8
8
|
|
|
9
9
|
## Requirements
|
|
10
10
|
|
|
11
11
|
- Ruby >= 3.2
|
|
12
|
-
- ActiveRecord
|
|
12
|
+
- ActiveRecord ~> 8.1.0
|
|
13
13
|
- The `turso` Ruby gem
|
|
14
14
|
|
|
15
15
|
## Runtime dependencies
|
|
@@ -51,7 +51,9 @@ end
|
|
|
51
51
|
Post.create!(title: "Hello", body: "World", published: true)
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
##
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
### Recommended production configuration
|
|
55
57
|
|
|
56
58
|
```yaml
|
|
57
59
|
production:
|
|
@@ -65,6 +67,23 @@ production:
|
|
|
65
67
|
experimental_features: "index_method"
|
|
66
68
|
```
|
|
67
69
|
|
|
70
|
+
### Connection options
|
|
71
|
+
|
|
72
|
+
| Option | Description | Default |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `database` | Path to the SQLite database file | Required |
|
|
75
|
+
| `journal_mode` | Journal mode (`wal`, `mvcc`, `delete`, etc.) | WAL |
|
|
76
|
+
| `pool` | Connection pool size | 5 |
|
|
77
|
+
| `timeout` | Busy timeout in milliseconds | 5000 |
|
|
78
|
+
| `busy_timeout` | Busy timeout in milliseconds (overrides `timeout`) | 5000 |
|
|
79
|
+
| `query_timeout` | Query timeout in milliseconds | 30000 |
|
|
80
|
+
| `experimental_features` | Comma-separated experimental features (`index_method`, `generated_columns`) | None |
|
|
81
|
+
| `statement_limit` | Maximum number of cached prepared statements | 1000 |
|
|
82
|
+
|
|
83
|
+
### `query_timeout` and `interrupt`
|
|
84
|
+
|
|
85
|
+
The adapter exposes `query_timeout=` to set a maximum execution time for queries. When a query exceeds this timeout, the Turso engine interrupts the operation and raises `ActiveRecord::QueryCanceled`. Use `interrupt` to cancel a running query from another thread.
|
|
86
|
+
|
|
68
87
|
### MVCC / BEGIN CONCURRENT (experimental)
|
|
69
88
|
|
|
70
89
|
⚠️ **Experimental.** Do not use in production unless you understand the caveats below. Normal ActiveRecord model persistence (`create!`, `save!`, `update!`, `touch`) is **not supported** inside `transaction(concurrent: true)`.
|
|
@@ -168,9 +187,9 @@ The following limitations apply to the current implementation. Read this section
|
|
|
168
187
|
|
|
169
188
|
The adapter builds column type maps from `column_decltype` metadata. This works for most column definitions but may not capture type information for computed expressions or subquery columns.
|
|
170
189
|
|
|
171
|
-
### 2. Batch SQL execution uses
|
|
190
|
+
### 2. Batch SQL execution uses the Turso native batch API
|
|
172
191
|
|
|
173
|
-
The adapter's batch execution path
|
|
192
|
+
The adapter's batch execution path uses the Turso native batch API (`execute_batch`), which correctly handles semicolons inside string literals, triggers, and stored expressions.
|
|
174
193
|
|
|
175
194
|
### 3. MVCC requires opt-in and has ActiveRecord compatibility caveats
|
|
176
195
|
|
|
@@ -187,9 +206,9 @@ Only use `transaction(concurrent: true)` after testing it under your app's concu
|
|
|
187
206
|
|
|
188
207
|
The adapter uses a bounded statement pool with a default limit inherited from Rails (typically `1000`). Statements are evicted with an LRU policy and finalized against the underlying Turso connection. Set `statement_limit` in `database.yml` to tune the pool size.
|
|
189
208
|
|
|
190
|
-
### 5. ActiveRecord 8.
|
|
209
|
+
### 5. Only ActiveRecord 8.1 is supported
|
|
191
210
|
|
|
192
|
-
|
|
211
|
+
The adapter is pinned to ActiveRecord `~> 8.1.0`.
|
|
193
212
|
|
|
194
213
|
### 6. INSERT RETURNING is disabled (not supported by Turso)
|
|
195
214
|
|
|
@@ -200,9 +219,9 @@ The underlying Turso SQLite build does not support `INSERT ... RETURNING` syntax
|
|
|
200
219
|
- Transaction isolation levels other than the default are reported as unsupported (`supports_transaction_isolation?` returns `false`) because Turso remote connections do not provide shared-cache read-uncommitted semantics.
|
|
201
220
|
- `insert_on_conflict` is enabled only when the reported SQLite version is `>= 3.24.0`.
|
|
202
221
|
|
|
203
|
-
### 8. `execute_batch`
|
|
222
|
+
### 8. `execute_batch` uses the Turso native batch API
|
|
204
223
|
|
|
205
|
-
The `turso` gem
|
|
224
|
+
The `turso` gem's `execute_batch` uses the Turso native batch API (`prepare_first`/`execute` loop), correctly handling edge cases like semicolons in string literals and triggers.
|
|
206
225
|
|
|
207
226
|
## Development
|
|
208
227
|
|
|
@@ -14,7 +14,7 @@ module ActiveRecord
|
|
|
14
14
|
when ::Turso::BusySnapshotException
|
|
15
15
|
ActiveRecord::SerializationFailure.new(message, sql: sql, binds: binds)
|
|
16
16
|
when ::Turso::BusyException
|
|
17
|
-
|
|
17
|
+
ActiveRecordTurso::BusyError.new(message, sql: sql, binds: binds)
|
|
18
18
|
when ::Turso::ReadonlyException
|
|
19
19
|
ActiveRecord::ReadOnlyRecord.new(message, sql: sql, binds: binds)
|
|
20
20
|
when ::Turso::IoException, ::Turso::CorruptException
|
|
@@ -38,6 +38,8 @@ module ActiveRecord
|
|
|
38
38
|
ActiveRecord::InvalidForeignKey.new(message, sql: sql, binds: binds)
|
|
39
39
|
when /unique constraint|primary key/i
|
|
40
40
|
ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds)
|
|
41
|
+
when /NOT NULL|cannot be NULL/i
|
|
42
|
+
ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds)
|
|
41
43
|
else
|
|
42
44
|
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
|
43
45
|
end
|
data/lib/activerecord-turso.rb
CHANGED
data/lib/turso/ar/connection.rb
CHANGED
|
@@ -7,7 +7,8 @@ module Turso
|
|
|
7
7
|
class Connection
|
|
8
8
|
extend Forwardable
|
|
9
9
|
|
|
10
|
-
def_delegators :@db, :close, :closed?, :changes, :total_changes
|
|
10
|
+
def_delegators :@db, :close, :closed?, :changes, :total_changes,
|
|
11
|
+
:last_insert_rowid, :prepare, :interrupt, :busy_timeout=
|
|
11
12
|
|
|
12
13
|
DEFAULT_BUSY_TIMEOUT_MS = 5000
|
|
13
14
|
DEFAULT_QUERY_TIMEOUT_MS = 30_000
|
|
@@ -19,11 +20,10 @@ module Turso
|
|
|
19
20
|
query_timeout: config[:query_timeout] || DEFAULT_QUERY_TIMEOUT_MS
|
|
20
21
|
}
|
|
21
22
|
db_opts[:experimental_features] = config[:experimental_features] if config[:experimental_features]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
query("SELECT last_insert_rowid()").first&.to_a&.first.to_i
|
|
23
|
+
database = ::Turso::Database.new(config[:database].to_s, **db_opts)
|
|
24
|
+
@db = database.connection
|
|
25
|
+
@db.busy_timeout = db_opts[:busy_timeout]
|
|
26
|
+
@db.query_timeout = db_opts[:query_timeout]
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def raw_connection
|
|
@@ -48,107 +48,11 @@ module Turso
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def execute_batch(sql)
|
|
51
|
-
|
|
52
|
-
@db.execute(stmt)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def prepare(sql)
|
|
57
|
-
@db.prepare(sql)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def busy_timeout=(ms)
|
|
61
|
-
@db.busy_timeout = ms.to_i
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def interrupt
|
|
65
|
-
@db.interrupt
|
|
51
|
+
@db.execute_batch(sql)
|
|
66
52
|
end
|
|
67
53
|
|
|
68
54
|
private
|
|
69
55
|
|
|
70
|
-
def split_batch(sql)
|
|
71
|
-
statements = []
|
|
72
|
-
current = +""
|
|
73
|
-
in_string = false
|
|
74
|
-
in_line_comment = false
|
|
75
|
-
in_block_comment = false
|
|
76
|
-
i = 0
|
|
77
|
-
while i < sql.length
|
|
78
|
-
char = sql[i]
|
|
79
|
-
next_char = sql[i + 1]
|
|
80
|
-
|
|
81
|
-
if in_line_comment
|
|
82
|
-
if char == "\n"
|
|
83
|
-
in_line_comment = false
|
|
84
|
-
end
|
|
85
|
-
i += 1
|
|
86
|
-
next
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
if in_block_comment
|
|
90
|
-
if char == "*" && next_char == "/"
|
|
91
|
-
in_block_comment = false
|
|
92
|
-
i += 2
|
|
93
|
-
else
|
|
94
|
-
i += 1
|
|
95
|
-
end
|
|
96
|
-
next
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
if in_string
|
|
100
|
-
if char == "'" && next_char == "'"
|
|
101
|
-
current << char << next_char
|
|
102
|
-
i += 2
|
|
103
|
-
next
|
|
104
|
-
elsif char == "'"
|
|
105
|
-
in_string = false
|
|
106
|
-
current << char
|
|
107
|
-
i += 1
|
|
108
|
-
next
|
|
109
|
-
end
|
|
110
|
-
current << char
|
|
111
|
-
i += 1
|
|
112
|
-
next
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
case char
|
|
116
|
-
when "'"
|
|
117
|
-
in_string = true
|
|
118
|
-
current << char
|
|
119
|
-
when "-"
|
|
120
|
-
if next_char == "-"
|
|
121
|
-
in_line_comment = true
|
|
122
|
-
i += 2
|
|
123
|
-
next
|
|
124
|
-
end
|
|
125
|
-
current << char
|
|
126
|
-
when "#"
|
|
127
|
-
in_line_comment = true
|
|
128
|
-
i += 1
|
|
129
|
-
next
|
|
130
|
-
when "/"
|
|
131
|
-
if next_char == "*"
|
|
132
|
-
in_block_comment = true
|
|
133
|
-
i += 2
|
|
134
|
-
next
|
|
135
|
-
end
|
|
136
|
-
current << char
|
|
137
|
-
when ";"
|
|
138
|
-
stmt = current.strip
|
|
139
|
-
statements << stmt unless stmt.empty?
|
|
140
|
-
current = +""
|
|
141
|
-
else
|
|
142
|
-
current << char
|
|
143
|
-
end
|
|
144
|
-
i += 1
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
stmt = current.strip
|
|
148
|
-
statements << stmt unless stmt.empty?
|
|
149
|
-
statements
|
|
150
|
-
end
|
|
151
|
-
|
|
152
56
|
def normalize_binds(binds)
|
|
153
57
|
binds.map do |value|
|
|
154
58
|
case value
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-turso
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ben D'Angelo
|
|
@@ -13,22 +13,16 @@ dependencies:
|
|
|
13
13
|
name: activerecord
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
17
|
-
- !ruby/object:Gem::Version
|
|
18
|
-
version: '8.0'
|
|
19
|
-
- - "<"
|
|
16
|
+
- - "~>"
|
|
20
17
|
- !ruby/object:Gem::Version
|
|
21
|
-
version:
|
|
18
|
+
version: 8.1.0
|
|
22
19
|
type: :runtime
|
|
23
20
|
prerelease: false
|
|
24
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
22
|
requirements:
|
|
26
|
-
- - "
|
|
27
|
-
- !ruby/object:Gem::Version
|
|
28
|
-
version: '8.0'
|
|
29
|
-
- - "<"
|
|
23
|
+
- - "~>"
|
|
30
24
|
- !ruby/object:Gem::Version
|
|
31
|
-
version:
|
|
25
|
+
version: 8.1.0
|
|
32
26
|
- !ruby/object:Gem::Dependency
|
|
33
27
|
name: turso
|
|
34
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -70,6 +64,7 @@ files:
|
|
|
70
64
|
- lib/active_record/connection_adapters/turso_adapter/statement_pool.rb
|
|
71
65
|
- lib/active_record/connection_adapters/turso_adapter/transaction_management.rb
|
|
72
66
|
- lib/activerecord-turso.rb
|
|
67
|
+
- lib/activerecord-turso/error.rb
|
|
73
68
|
- lib/activerecord-turso/version.rb
|
|
74
69
|
- lib/turso/ar/connection.rb
|
|
75
70
|
licenses:
|