Class: ActiveRecord::ConnectionAdapters::LibsqlAdapter

Inherits:
AbstractAdapter
  • Object
show all
Defined in:
lib/active_record/connection_adapters/libsql_adapter.rb

Constant Summary collapse

ADAPTER_NAME =
'Turso'
NATIVE_DATABASE_TYPES =

SQLite 互換の型マッピング(libSQL は SQLite 方言)

{
  primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT',
  string: { name: 'TEXT' },
  text: { name: 'TEXT' },
  integer: { name: 'INTEGER' },
  float: { name: 'REAL' },
  decimal: { name: 'REAL' },
  datetime: { name: 'TEXT' },
  timestamp: { name: 'TEXT' },
  time: { name: 'TEXT' },
  date: { name: 'TEXT' },
  binary: { name: 'BLOB' },
  boolean: { name: 'INTEGER' },
  json: { name: 'TEXT' }
}.freeze

Instance Method Summary collapse

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


107
108
109
110
111
112
113
114
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 107

def active?
  return false unless @raw_connection

  @raw_connection.query('SELECT 1')
  true
rescue StandardError
  false
end

#adapter_nameObject


Adapter 識別




60
61
62
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 60

def adapter_name
  ADAPTER_NAME
end

#affected_rows(_raw_result) ⇒ Object



198
199
200
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 198

def affected_rows(_raw_result)
  @last_affected_rows || 0
end

#begin_db_transactionObject


トランザクション




206
207
208
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 206

def begin_db_transaction
  @raw_connection&.begin_transaction
end

#cast_result(raw_result) ⇒ Object

perform_query が返した結果をそのまま使う(すでに ActiveRecord::Result)



194
195
196
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 194

def cast_result(raw_result)
  raw_result
end

#columns(table_name) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 243

def columns(table_name)
  result = internal_exec_query(
    "PRAGMA table_info(#{quote_table_name(table_name)})",
    'SCHEMA'
  )
  result.map do |row|
    sql_type    = row['type'].to_s
    cast_type   = type_map.lookup(sql_type) || Type::Value.new
    sql_type_md = (sql_type)
    null        = row['notnull'].to_i.zero?
    COLUMN_BUILDER.call(row['name'], cast_type, row['dflt_value'], sql_type_md, null)
  end
end

#commit_db_transactionObject



210
211
212
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 210

def commit_db_transaction
  @raw_connection&.commit_transaction
end

#connect!Object


接続管理(AR 8 スタイル)AR の ConnectionPool はスレッドごとに独立した Adapter インスタンスを払い出すため




102
103
104
105
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 102

def connect!
  @raw_database, @raw_connection = build_libsql_connection
  super
end

#discard!Object

fork 後の子プロセスで呼ばれる。sqlite3 gem の fork safety が接続を強制クローズするため、参照を破棄して子プロセスで新しい接続を確立できるようにする。AR の ConnectionPool が fork 後に各コネクションに対して呼ぶ。



126
127
128
129
130
131
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 126

def discard!
  @raw_connection = nil
  @raw_database = nil
  TursoLibsql.reinitialize_runtime!
  super
end

#disconnect!Object



116
117
118
119
120
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 116

def disconnect!
  @raw_connection = nil
  @raw_database = nil
  super
end

#exec_rollback_db_transactionObject



214
215
216
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 214

def exec_rollback_db_transaction
  @raw_connection&.rollback_transaction
end

#last_inserted_id(_result) ⇒ Object


INSERT 後の id AR 8 は last_inserted_id(result) を呼ぶ




223
224
225
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 223

def last_inserted_id(_result)
  @raw_connection&.last_insert_rowid
end

#native_database_typesObject


スキーマ情報




231
232
233
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 231

def native_database_types
  NATIVE_DATABASE_TYPES
end

#perform_query(raw_connection, sql, _binds, type_casted_binds, prepare:, notification_payload:, batch: false) ⇒ Object

AR 8 が with_raw_connection { |conn| } で呼ぶ中核メソッド



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 162

def perform_query(raw_connection, sql, _binds, type_casted_binds, prepare:, notification_payload:, batch: false)
  # バインドパラメータを SQL に展開する(libsql の ? プレースホルダーに対応)
  expanded_sql = if type_casted_binds&.any?
                   i = -1
                   sql.gsub('?') do
                     i += 1
                     quote(type_casted_binds[i])
                   end
                 else
                   sql
                 end

  # libSQL / SQLite は FOR UPDATE / FOR UPDATE SKIP LOCKED / FOR SHARE を
  # サポートしない。AR の SQLite3 adapter はこれらを除去するが、
  # libsql_adapter は直接 Hrana HTTP / SQLite3::Database を呼ぶため自前で除去する。
  expanded_sql = sanitize_for_update(expanded_sql)

  if read_query?(expanded_sql)
    rows = raw_connection.query(expanded_sql)
    notification_payload[:row_count] = rows.size if notification_payload
    build_result(rows)
  else
    affected = raw_connection.execute(expanded_sql)
    @last_affected_rows = affected.to_i
    notification_payload[:row_count] = @last_affected_rows if notification_payload
    ActiveRecord::Result.empty
  end
rescue RuntimeError => e
  raise translate_exception(e, message: e.message, sql: expanded_sql, binds: [])
end

#quote_column_name(name) ⇒ Object


クォート




265
266
267
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 265

def quote_column_name(name)
  %("#{name.to_s.gsub('"', '""')}")
end

#quote_table_name(name) ⇒ Object



269
270
271
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 269

def quote_table_name(name)
  %("#{name.to_s.gsub('"', '""')}")
end

#quoted_falseObject



277
278
279
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 277

def quoted_false
  '0'
end

#quoted_trueObject



273
274
275
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 273

def quoted_true
  '1'
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 72

def supports_ddl_transactions?
  false
end

#supports_explain?Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 80

def supports_explain?
  false
end

#supports_lazy_transactions?Boolean

Returns:

  • (Boolean)


84
85
86
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 84

def supports_lazy_transactions?
  false
end

#supports_migrations?Boolean

Returns:

  • (Boolean)


64
65
66
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 64

def supports_migrations?
  true
end

#supports_primary_key?Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 68

def supports_primary_key?
  true
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 76

def supports_savepoints?
  false
end

#syncObject

Embedded Replica モードでリモートから最新フレームを手動同期する。Remote モードでは何もしない(no-op)。



152
153
154
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 152

def sync
  @raw_database&.sync
end

#table_exists?(table_name) ⇒ Boolean

Returns:

  • (Boolean)


257
258
259
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 257

def table_exists?(table_name)
  tables.include?(table_name.to_s)
end

#tables(_name = nil) ⇒ Object



235
236
237
238
239
240
241
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 235

def tables(_name = nil)
  result = internal_exec_query(
    "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'",
    'SCHEMA'
  )
  result.rows.flatten
end

#write_query?(sql) ⇒ Boolean

Returns:

  • (Boolean)


88
89
90
91
92
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 88

def write_query?(sql)
  !READ_QUERY.match?(sql)
rescue ArgumentError
  !READ_QUERY.match?(sql.b)
end