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



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

def affected_rows(_raw_result)
  @last_affected_rows || 0
end

#begin_db_transactionObject


トランザクション




201
202
203
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 201

def begin_db_transaction
  @raw_connection&.begin_transaction
end

#cast_result(raw_result) ⇒ Object

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



189
190
191
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 189

def cast_result(raw_result)
  raw_result
end

#columns(table_name) ⇒ Object



238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 238

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



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

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



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

def exec_rollback_db_transaction
  @raw_connection&.rollback_transaction
end

#last_inserted_id(_result) ⇒ Object


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




218
219
220
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 218

def last_inserted_id(_result)
  @raw_connection&.last_insert_rowid
end

#native_database_typesObject


スキーマ情報




226
227
228
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 226

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
# 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

  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


クォート




260
261
262
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 260

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

#quote_table_name(name) ⇒ Object



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

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

#quoted_falseObject



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

def quoted_false
  '0'
end

#quoted_trueObject



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

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)


252
253
254
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 252

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

#tables(_name = nil) ⇒ Object



230
231
232
233
234
235
236
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 230

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