activerecord-libsql 0.1.6 → 0.1.7
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/CHANGELOG.md +20 -0
- data/lib/active_record/connection_adapters/libsql_adapter.rb +65 -5
- data/lib/activerecord/libsql/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c7d43524e4ffab6a8d48512dae7494bd422a0484f4d5de4aab9638af9cc05e11
|
|
4
|
+
data.tar.gz: 19c8598dd1fe21df3a71822adfbc03b0f99b01a50d46ec0544a5c9ed54fafd76
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec59f0d00947a93fc541d2fbe27bbef5132208a472199ad1869187e85beb0a936d49258f3330be290ede8c01f15c3c0eae851d8518725ba2abe2d64a1d38e1a0
|
|
7
|
+
data.tar.gz: 2823e8e01b17e6553a9ae4f676d589f9e11349200bd133a0e7a20bd372480e5d46efa8098afb6b9780146efb1e5d82722dfa120400d8345c0fd5c1cd8a2c6e77
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.1.7] - 2026-03-27
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`NotImplementedError` on `insert_all!` / `upsert_all` with Solid Queue** (#17)
|
|
10
|
+
- Solid Queue's `ClaimedExecution.claiming` calls `insert_all!`, which internally calls
|
|
11
|
+
`self.class.quote_column_name` (a class-level method).
|
|
12
|
+
- `AbstractAdapter::ClassMethods#quote_column_name` raises `NotImplementedError` by design.
|
|
13
|
+
`LibsqlAdapter` only defined the instance-level method, leaving the class-level one unimplemented.
|
|
14
|
+
- Added `class << self` block with `quote_column_name` / `quote_table_name` (cached via `Concurrent::Map`).
|
|
15
|
+
- Removed redundant instance-level `quote_column_name` / `quote_table_name` — AR 8's `Quoting` module
|
|
16
|
+
delegates instance calls to `self.class.quote_column_name`, so only the class-level definition is needed.
|
|
17
|
+
- Added `quote_string` override: SQLite / libSQL do not treat backslash as an escape character,
|
|
18
|
+
so the default `AbstractAdapter` implementation (which doubles backslashes) would corrupt values
|
|
19
|
+
containing `\`. Now only single-quotes are escaped, matching `SQLite3Adapter`.
|
|
20
|
+
- Implemented `build_insert_sql` with `ON CONFLICT ... DO NOTHING / DO UPDATE SET` syntax
|
|
21
|
+
(same as `SQLite3Adapter`).
|
|
22
|
+
- Declared `supports_insert_on_duplicate_skip?`, `supports_insert_on_duplicate_update?`,
|
|
23
|
+
and `supports_insert_conflict_target?` as `true`.
|
|
24
|
+
|
|
5
25
|
## [0.1.6] - 2026-03-16
|
|
6
26
|
|
|
7
27
|
### Fixed
|
|
@@ -18,6 +18,22 @@ module ActiveRecord
|
|
|
18
18
|
class LibsqlAdapter < AbstractAdapter
|
|
19
19
|
ADAPTER_NAME = 'Turso'
|
|
20
20
|
|
|
21
|
+
# insert_all! / upsert_all は self.class.quote_column_name(クラスメソッド)を呼ぶ。
|
|
22
|
+
# AbstractAdapter の ClassMethods はこれを NotImplementedError にするため、
|
|
23
|
+
# クラスメソッドとして上書きする。
|
|
24
|
+
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
|
25
|
+
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
|
26
|
+
|
|
27
|
+
class << self
|
|
28
|
+
def quote_column_name(name)
|
|
29
|
+
QUOTED_COLUMN_NAMES[name] ||= %("#{name.to_s.gsub('"', '""')}").freeze
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def quote_table_name(name)
|
|
33
|
+
QUOTED_TABLE_NAMES[name] ||= %("#{name.to_s.gsub('"', '""').gsub('.', '"."')}").freeze
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
21
37
|
# SQLite 互換の型マッピング(libSQL は SQLite 方言)
|
|
22
38
|
# datetime / timestamp は 'datetime' を使う。
|
|
23
39
|
# 'TEXT' にすると PRAGMA table_info が 'TEXT' を返し、
|
|
@@ -90,6 +106,20 @@ module ActiveRecord
|
|
|
90
106
|
false
|
|
91
107
|
end
|
|
92
108
|
|
|
109
|
+
# insert_all! / upsert_all のサポート
|
|
110
|
+
# libSQL / SQLite は ON CONFLICT 構文をサポートする
|
|
111
|
+
def supports_insert_on_duplicate_skip?
|
|
112
|
+
true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def supports_insert_on_duplicate_update?
|
|
116
|
+
true
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def supports_insert_conflict_target?
|
|
120
|
+
true
|
|
121
|
+
end
|
|
122
|
+
|
|
93
123
|
def write_query?(sql)
|
|
94
124
|
!READ_QUERY.match?(sql)
|
|
95
125
|
rescue ArgumentError
|
|
@@ -158,6 +188,32 @@ module ActiveRecord
|
|
|
158
188
|
@raw_database&.sync
|
|
159
189
|
end
|
|
160
190
|
|
|
191
|
+
# -----------------------------------------------------------------------
|
|
192
|
+
# insert_all! / upsert_all(ON CONFLICT 構文)
|
|
193
|
+
# SQLite3 adapter と同じ実装
|
|
194
|
+
# -----------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
def build_insert_sql(insert) # :nodoc:
|
|
197
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
|
198
|
+
|
|
199
|
+
if insert.skip_duplicates?
|
|
200
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
|
201
|
+
elsif insert.update_duplicates?
|
|
202
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
|
203
|
+
if insert.raw_update_sql?
|
|
204
|
+
sql << insert.raw_update_sql
|
|
205
|
+
else
|
|
206
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
|
|
207
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(',')
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# RETURNING 句は supports_insert_returning? が false(デフォルト)のため生成されない。
|
|
212
|
+
# perform_query は INSERT を write クエリとして扱い結果を捨てるため、
|
|
213
|
+
# RETURNING を有効にする場合は perform_query の read_query? 判定も合わせて修正が必要。
|
|
214
|
+
sql
|
|
215
|
+
end
|
|
216
|
+
|
|
161
217
|
# -----------------------------------------------------------------------
|
|
162
218
|
# AR 8 クエリパイプライン
|
|
163
219
|
# raw_execute → perform_query → cast_result の流れ
|
|
@@ -267,12 +323,16 @@ module ActiveRecord
|
|
|
267
323
|
# クォート
|
|
268
324
|
# -----------------------------------------------------------------------
|
|
269
325
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
326
|
+
# quote_column_name / quote_table_name はクラスメソッド(class << self)で定義済み。
|
|
327
|
+
# AR 8 の Quoting モジュールはインスタンスメソッドを self.class.quote_column_name に
|
|
328
|
+
# 委譲する設計のため、インスタンスメソッドの個別定義は不要。
|
|
273
329
|
|
|
274
|
-
|
|
275
|
-
|
|
330
|
+
# SQLite / libSQL はバックスラッシュをエスケープ文字として扱わない。
|
|
331
|
+
# AbstractAdapter のデフォルト実装は \\ → \\\\ の変換を行うため、
|
|
332
|
+
# バックスラッシュを含む文字列の保存・取得で値が化ける。
|
|
333
|
+
# SQLite3Adapter と同様に、シングルクォートのエスケープのみ行う。
|
|
334
|
+
def quote_string(s)
|
|
335
|
+
s.gsub("'", "''")
|
|
276
336
|
end
|
|
277
337
|
|
|
278
338
|
def quoted_true
|