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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04f8ecc726fa450fadcf8b819a04663b3035bed1c1b5f592d362ec07664d2f14
4
- data.tar.gz: 4ce85117e6f562ead6d2defeaaa5c70fccc2c62492d9916c798e56bcf32acfca
3
+ metadata.gz: b8e00bcf26be0b39f02a05bd0f4850db41d277f7d72bdb15013c218f032d93eb
4
+ data.tar.gz: 9e9ccad1e346648fef21eb2d0a3aceafa7afada0bc67348a0ed912b723e6cca5
5
5
  SHA512:
6
- metadata.gz: 6d18e249a927642d705959341439129523cb3f2763ab699a8c2dfbc1f38d8f52b3b831a96f140a95518f3e932a9da348184af605525d354a54893419f332c0aa
7
- data.tar.gz: 8b1cec4617634414e11928453aeed21e2c382fe4a36357ca435b601f337f8b9ca3fb732f035db02ebd8a0fe8f7dfc35db961c384faffd2cf8937ea612ec6363e
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
- This adapter is under **active** development. It can run basic ActiveRecord operations against Turso today, but several production features are still being hardened. See [Limitations and Risks](#limitations-and-risks) before using it in production.
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 >= 8.0, < 8.2
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
- ## Recommended production configuration
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 a simple string splitter
190
+ ### 2. Batch SQL execution uses the Turso native batch API
172
191
 
173
- The adapter's batch execution path splits multi-statement SQL on semicolons. This means SQL containing semicolons inside string literals, triggers, or stored expressions may be split incorrectly. Avoid relying on multi-statement strings other than simple schema dumps.
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.0 support is CI-tested, not locally tested
209
+ ### 5. Only ActiveRecord 8.1 is supported
191
210
 
192
- Only ActiveRecord 8.1 is installed in the primary development environment. ActiveRecord 8.0 compatibility is validated through CI. If you run into 8.0-specific issues, please report them.
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` in the underlying bindings is a Ruby-side fallback
222
+ ### 8. `execute_batch` uses the Turso native batch API
204
223
 
205
- The `turso` gem provides `DB#execute_batch` as a convenience that splits and executes statements one by one. It does not use a native batch API, so it carries the same semicolon-splitting risk as item 2 above.
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
- ActiveRecord::Deadlocked.new(message, sql: sql, binds: binds)
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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordTurso
4
+ class Error < ActiveRecord::StatementInvalid; end
5
+ class BusyError < Error; end
6
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordTurso
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.2"
5
5
  end
@@ -3,6 +3,7 @@
3
3
  require "active_record"
4
4
  require "turso"
5
5
 
6
+ require_relative "activerecord-turso/error"
6
7
  require_relative "turso/ar/connection"
7
8
  require_relative "active_record/connection_adapters/turso_adapter"
8
9
 
@@ -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
- @db = ::Turso::Database.new(config[:database].to_s, **db_opts)
23
- end
24
-
25
- def last_insert_rowid
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
- split_batch(sql).each do |stmt|
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.1
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: '8.2'
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: '8.2'
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: