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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f696c91f955a35679cd2b4159bedc0a9b8429cd30397fe9631054de7f63eb510
4
- data.tar.gz: 58df65dd6bfd7aad0a4f187c9935064b4d8d21d57079c44df5f51c765148aca0
3
+ metadata.gz: c7d43524e4ffab6a8d48512dae7494bd422a0484f4d5de4aab9638af9cc05e11
4
+ data.tar.gz: 19c8598dd1fe21df3a71822adfbc03b0f99b01a50d46ec0544a5c9ed54fafd76
5
5
  SHA512:
6
- metadata.gz: 0a1ec1b42c809eb6cc91516796bdd7ece669396f22415f4940e5a1051780638774948e558aaeefbb22f9837052c86575061ed408ae71b24f018b726d66424ce9
7
- data.tar.gz: 8a99cd050bae2dbb898bb74bb798afd7432a943f231efc10685b041ccd98b5a47449ad536f7216be53ed50d9a897bec37fcafd2f7d5f12fe7fd71e77921644ee
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
- def quote_column_name(name)
271
- %("#{name.to_s.gsub('"', '""')}")
272
- end
326
+ # quote_column_name / quote_table_name はクラスメソッド(class << self)で定義済み。
327
+ # AR 8 の Quoting モジュールはインスタンスメソッドを self.class.quote_column_name
328
+ # 委譲する設計のため、インスタンスメソッドの個別定義は不要。
273
329
 
274
- def quote_table_name(name)
275
- %("#{name.to_s.gsub('"', '""')}")
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Libsql
5
- VERSION = '0.1.6'
5
+ VERSION = '0.1.7'
6
6
  end
7
7
  end
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.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - aileron