Class: ActiveRecord::ConnectionAdapters::LibsqlAdapter
- Inherits:
-
AbstractAdapter
- Object
- AbstractAdapter
- ActiveRecord::ConnectionAdapters::LibsqlAdapter
- Defined in:
- lib/active_record/connection_adapters/libsql_adapter.rb
Overview
:nodoc:
Constant Summary collapse
- ADAPTER_NAME =
'libSQL'- NATIVE_DATABASE_TYPES =
{ primary_key: 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', string: { name: 'varchar' }, text: { name: 'text' }, integer: { name: 'integer' }, float: { name: 'float' }, decimal: { name: 'decimal' }, datetime: { name: 'datetime' }, time: { name: 'time' }, date: { name: 'date' }, binary: { name: 'blob' }, boolean: { name: 'boolean' }, json: { name: 'json' } }.freeze
Class Method Summary collapse
Instance Method Summary collapse
- #affected_rows(_result) ⇒ Object
- #cast_result(result) ⇒ Object
- #column_definitions(table_name) ⇒ Object
- #column_the_rowid?(field, column_definitions) ⇒ Boolean
- #connect ⇒ Object
- #data_source_sql(name = nil, type: nil) ⇒ Object
- #default_function?(default_value, default) ⇒ Boolean
- #extract_default_function(default_value, default) ⇒ Object
- #extract_generated_type(field) ⇒ Object
- #extract_value_from_default(default) ⇒ Object
- #indexes(table_name) ⇒ Object
-
#initialize ⇒ LibsqlAdapter
constructor
A new instance of LibsqlAdapter.
- #last_inserted_id(result) ⇒ Object
-
#native_database_types ⇒ Object
:nodoc:.
- #new_column_from_field(_table_name, field, definitions) ⇒ Object
- #perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false) ⇒ Object
-
#primary_keys(table_name) ⇒ Object
:nodoc:.
- #quote_column_name(name) ⇒ Object
- #quote_table_name(name) ⇒ Object
- #quoted_scope(name = nil, type: nil) ⇒ Object
- #reconnect ⇒ Object
-
#write_query?(sql) ⇒ Boolean
:nodoc:.
Constructor Details
#initialize ⇒ LibsqlAdapter
Returns a new instance of LibsqlAdapter.
65 66 67 68 69 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 65 def initialize(...) super @connection_parameters = @config.reject { |k| k == :adapter } @connection_parameters[:url] = @connection_parameters[:host] end |
Class Method Details
.new_client(config) ⇒ Object
59 60 61 62 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 59 def new_client(config) db = Libsql::Database.new(config || {}) db.connect end |
Instance Method Details
#affected_rows(_result) ⇒ Object
111 112 113 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 111 def affected_rows(_result) @last_affected_rows end |
#cast_result(result) ⇒ Object
115 116 117 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 115 def cast_result(result) result end |
#column_definitions(table_name) ⇒ Object
127 128 129 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 127 def column_definitions(table_name) internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", 'SCHEMA') end |
#column_the_rowid?(field, column_definitions) ⇒ Boolean
180 181 182 183 184 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 180 def column_the_rowid?(field, column_definitions) return false unless /integer/i.match?(field['type']) && field['pk'] == 1 column_definitions.one? { |c| c['pk'].positive? } end |
#connect ⇒ Object
71 72 73 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 71 def connect @raw_connection = self.class.new_client(@connection_parameters) end |
#data_source_sql(name = nil, type: nil) ⇒ Object
131 132 133 134 135 136 137 138 139 140 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 131 def data_source_sql(name = nil, type: nil) scope = quoted_scope(name, type:) scope[:type] ||= "'table','view'" sql = +"SELECT name FROM pragma_table_list WHERE schema <> 'temp'" sql << " AND name NOT IN ('sqlite_sequence', 'sqlite_schema')" sql << " AND name = #{scope[:name]}" if scope[:name] sql << " AND type IN (#{scope[:type]})" sql end |
#default_function?(default_value, default) ⇒ Boolean
169 170 171 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 169 def default_function?(default_value, default) !default_value && /\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|/.match?(default) end |
#extract_default_function(default_value, default) ⇒ Object
165 166 167 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 165 def extract_default_function(default_value, default) default if default_function?(default_value, default) end |
#extract_generated_type(field) ⇒ Object
173 174 175 176 177 178 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 173 def extract_generated_type(field) case field['hidden'] when 2 then :virtual when 3 then :stored end end |
#extract_value_from_default(default) ⇒ Object
155 156 157 158 159 160 161 162 163 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 155 def extract_value_from_default(default) case default when /^null$/i then nil when /^'([^|]*)'$/m then::Regexp.last_match(1).gsub("''", "'") when /^"([^|]*)"$/m then ::Regexp.last_match(1).gsub('""', '"') when /\A-?\d+(\.\d*)?\z/ then ::Regexp.last_match(0) when /x'(.*)'/ then [::Regexp.last_match(1)].pack('H*') end end |
#indexes(table_name) ⇒ Object
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 226 def indexes(table_name) internal_exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').filter_map do |row| # Indexes SQLite creates implicitly for internal use start with "sqlite_". # See https://www.sqlite.org/fileformat2.html#intschema next if row['name'].start_with?('sqlite_') index_sql = query_value(<<~SQL, 'SCHEMA') SELECT sql FROM sqlite_master WHERE name = #{quote(row['name'])} AND type = 'index' UNION ALL SELECT sql FROM sqlite_temp_master WHERE name = #{quote(row['name'])} AND type = 'index' SQL %r{\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?(?:\s*/\*.*\*/)?\z}i =~ index_sql columns = internal_exec_query("PRAGMA index_info(#{quote(row['name'])})", 'SCHEMA').map do |col| col['name'] end where = where.sub(%r{\s*/\*.*\*/\z}, '') if where orders = {} if columns.any?(&:nil?) # index created with an expression columns = expressions elsif index_sql # Add info on sort order for columns (only desc order is explicitly specified, # asc is the default) index_sql.scan(/"(\w+)" DESC/).flatten.each do |order_column| orders[order_column] = :desc end # index_sql can be null in case of primary key indexes end IndexDefinition.new( table_name, row['name'], row['unique'] != 0, columns, where:, orders: ) end end |
#last_inserted_id(result) ⇒ Object
222 223 224 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 222 def last_inserted_id(result) @raw_connection.last_inserted_id end |
#native_database_types ⇒ Object
:nodoc:
54 55 56 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 54 def native_database_types # :nodoc: NATIVE_DATABASE_TYPES end |
#new_column_from_field(_table_name, field, definitions) ⇒ Object
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 186 def new_column_from_field(_table_name, field, definitions) default = field['dflt_value'] = (field['type']) default_value = extract_value_from_default(default) generated_type = extract_generated_type(field) default_function = if generated_type.present? default else extract_default_function(default_value, default) end rowid = column_the_rowid?(field, definitions) Column.new( field['name'], default_value, , field['notnull'].to_i.zero?, default_function, collation: field['collation'], auto_increment: field['auto_increment'], rowid:, generated_type: ) end |
#perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 80 def perform_query( raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false ) _ = prepare _ = notification_payload _ = binds if batch raw_connection.execute_batch(sql) else stmt = raw_connection.prepare(sql) begin result = if stmt.column_count.zero? @last_affected_rows = stmt.execute type_casted_binds ActiveRecord::Result.empty else rows = stmt.query(type_casted_binds) @last_affected_rows = nil ActiveRecord::Result.new(rows.columns, rows.to_a.map(&:values)) end ensure stmt.close end end verified! result end |
#primary_keys(table_name) ⇒ Object
:nodoc:
215 216 217 218 219 220 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 215 def primary_keys(table_name) # :nodoc: column_definitions(table_name) .select { |f| f['pk'].positive? } .sort_by { |f| f['pk'] } .map { |f| f['name'] } end |
#quote_column_name(name) ⇒ Object
119 120 121 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 119 def quote_column_name(name) %("#{name.to_s.gsub('"', '""')}").freeze end |
#quote_table_name(name) ⇒ Object
123 124 125 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 123 def quote_table_name(name) %("#{name.to_s.gsub('"', '""').gsub('.', '"."')}").freeze end |
#quoted_scope(name = nil, type: nil) ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 142 def quoted_scope(name = nil, type: nil) type = { 'BASE_TABLE': "'table'", 'VIEW': "'view'", 'VIRTUAL TABLE': "'virtual'" }[type] scope = {} scope[:name] = quote(name) if name scope[:type] = type if type scope end |
#reconnect ⇒ Object
75 76 77 78 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 75 def reconnect @raw_connection&.close connect end |
#write_query?(sql) ⇒ Boolean
:nodoc:
50 51 52 |
# File 'lib/active_record/connection_adapters/libsql_adapter.rb', line 50 def write_query?(sql) # :nodoc: !READ_QUERY.match?(sql) end |