Skip to content

Commit

Permalink
add an ability to work with sqlite3 adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
prog-supdex committed Oct 20, 2023
1 parent 5de703e commit 5b2b036
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 21 deletions.
79 changes: 79 additions & 0 deletions lib/activerecord_slotted_counters/adapters/base_adapter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# frozen_string_literal: true

module ActiveRecordSlottedCounters
module Adapters
class BaseAdapter
attr_reader :klass, :current_adapter_name

def initialize(klass, current_adapter_name)
@klass = klass
@current_adapter_name = current_adapter_name
end

def apply?
raise NoMethodError
end

def bulk_insert(attributes, on_duplicate: nil, unique_by: nil)
raise NoMethodError
end

def wrap_column_name(value)
"EXCLUDED.#{value}"
end

private

def build_base_sql(attributes)
keys = attributes.first.keys + klass.all_timestamp_attributes_in_model

current_time = klass.current_time_from_proper_timezone
data = attributes.map { |attr| attr.values + [current_time, current_time] }

columns = columns_for_attributes(keys)

fields_str = quote_column_names(columns)
values_str = quote_many_records(columns, data)

<<~SQL
INSERT INTO #{klass.quoted_table_name}
(#{fields_str})
VALUES #{values_str}
SQL
end

def unique_indexes
klass.connection.schema_cache.indexes(klass.table_name).select(&:unique)
end

def columns_for_attributes(attributes)
attributes.map do |attribute|
klass.column_for_attribute(attribute)
end
end

def quote_column_names(columns, table_name: false)
columns.map do |column|
column_name = klass.connection.quote_column_name(column.name)
if table_name
"#{klass.quoted_table_name}.#{column_name}"
else
column_name
end
end.join(",")
end

def quote_record(columns, record_values)
values_str = record_values.each_with_index.map do |value, i|
type = klass.connection.lookup_cast_type_from_column(columns[i])
klass.connection.quote(type.serialize(value))
end.join(",")
"(#{values_str})"
end

def quote_many_records(columns, data)
data.map { |values| quote_record(columns, values) }.join(",")
end
end
end
end
12 changes: 5 additions & 7 deletions lib/activerecord_slotted_counters/adapters/pg_upsert.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

module ActiveRecordSlottedCounters
module Adapters
class PgUpsert
attr_reader :klass
class PgUpsert < BaseAdapter
def apply?
return false if ActiveRecord::VERSION::MAJOR >= 7

def initialize(klass)
@klass = klass
end
return false unless defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)

def apply?
ActiveRecord::VERSION::MAJOR < 7 && klass.connection.adapter_name == ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::ADAPTER_NAME
current_adapter_name == ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::ADAPTER_NAME
end

def bulk_insert(attributes, on_duplicate: nil, unique_by: nil)
Expand Down
8 changes: 1 addition & 7 deletions lib/activerecord_slotted_counters/adapters/rails_upsert.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@

module ActiveRecordSlottedCounters
module Adapters
class RailsUpsert
attr_reader :klass

def initialize(klass)
@klass = klass
end

class RailsUpsert < BaseAdapter
def apply?
ActiveRecord::VERSION::MAJOR >= 7
end
Expand Down
15 changes: 15 additions & 0 deletions lib/activerecord_slotted_counters/adapters/sqlite_upsert.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module ActiveRecordSlottedCounters
module Adapters
class SQLiteUpsert < PgUpsert
def apply?
return false if ActiveRecord::VERSION::MAJOR >= 7

return false unless defined?(ActiveRecord::ConnectionAdapters::SQLite3Adapter)

current_adapter_name == ActiveRecord::ConnectionAdapters::SQLite3Adapter::ADAPTER_NAME
end
end
end
end
11 changes: 8 additions & 3 deletions lib/activerecord_slotted_counters/has_slotted_counter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
require "active_support"
require "activerecord_slotted_counters/utils"

require "activerecord_slotted_counters/adapters/base_adapter"
require "activerecord_slotted_counters/adapters/rails_upsert"
require "activerecord_slotted_counters/adapters/pg_upsert"
require "activerecord_slotted_counters/adapters/sqlite_upsert"

module ActiveRecordSlottedCounters
class SlottedCounter < ::ActiveRecord::Base
Expand Down Expand Up @@ -42,14 +44,17 @@ def slotted_counter_db_adapter
def set_slotted_counter_db_adapter
available_adapters = [
ActiveRecordSlottedCounters::Adapters::RailsUpsert,
ActiveRecordSlottedCounters::Adapters::PgUpsert
ActiveRecordSlottedCounters::Adapters::PgUpsert,
ActiveRecordSlottedCounters::Adapters::SQLiteUpsert
]

current_adapter_name = connection.adapter_name

adapter = available_adapters
.map { |adapter| adapter.new(self) }
.map { |adapter| adapter.new(self, current_adapter_name) }
.detect { |adapter| adapter.apply? }

raise NotSupportedAdapter.new(connection.adapter_name) if adapter.nil?
raise NotSupportedAdapter.new(current_adapter_name) if adapter.nil?

adapter
end
Expand Down
11 changes: 9 additions & 2 deletions spec/slotted_counter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,18 @@
def insert_association_sql(association_class, article_id)
association_table = association_class.arel_table
foreign_key = association_class.reflections["article"].foreign_key
current_date_sql_command =
if defined?(ActiveRecord::ConnectionAdapters::SQLite3Adapter)
"date('now')"
else
"now()"
end

insert_manager = Arel::InsertManager.new
insert_manager.insert([
[association_table[foreign_key], article_id],
[association_table[:created_at], Arel.sql("now()")],
[association_table[:updated_at], Arel.sql("now()")]
[association_table[:created_at], Arel.sql(current_date_sql_command)],
[association_table[:updated_at], Arel.sql(current_date_sql_command)]
])

insert_manager.to_sql
Expand Down
11 changes: 9 additions & 2 deletions spec/support/shared_examples_for_cache_counters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,18 @@
def insert_comment_sql(comment_class, article_id)
comment_table = comment_class.arel_table
foreign_key = comment_class.reflections["article"].foreign_key
current_date_sql_command =
if defined?(ActiveRecord::ConnectionAdapters::SQLite3Adapter)
"date('now')"
else
"now()"
end

insert_manager = Arel::InsertManager.new
insert_manager.insert([
[comment_table[foreign_key], article_id],
[comment_table[:created_at], Arel.sql("now()")],
[comment_table[:updated_at], Arel.sql("now()")]
[comment_table[:created_at], Arel.sql(current_date_sql_command)],
[comment_table[:updated_at], Arel.sql(current_date_sql_command)]
])

insert_manager.to_sql
Expand Down

0 comments on commit 5b2b036

Please sign in to comment.