BREAKING: Plugin renamed from compounding-engineering to compound-engineering. Users will need to reinstall with the new name: claude /plugin install compound-engineering Changes: - Renamed plugin directory and all references - Updated documentation counts (24 agents, 19 commands) - Added julik-frontend-races-reviewer to docs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
232 lines
4.2 KiB
Markdown
232 lines
4.2 KiB
Markdown
# Database Adapter Patterns
|
|
|
|
## Abstract Base Class Pattern
|
|
|
|
```ruby
|
|
# lib/strong_migrations/adapters/abstract_adapter.rb
|
|
module StrongMigrations
|
|
module Adapters
|
|
class AbstractAdapter
|
|
def initialize(checker)
|
|
@checker = checker
|
|
end
|
|
|
|
def min_version
|
|
nil
|
|
end
|
|
|
|
def set_statement_timeout(timeout)
|
|
# no-op by default
|
|
end
|
|
|
|
def check_lock_timeout
|
|
# no-op by default
|
|
end
|
|
|
|
private
|
|
|
|
def connection
|
|
@checker.send(:connection)
|
|
end
|
|
|
|
def quote(value)
|
|
connection.quote(value)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
## PostgreSQL Adapter
|
|
|
|
```ruby
|
|
# lib/strong_migrations/adapters/postgresql_adapter.rb
|
|
module StrongMigrations
|
|
module Adapters
|
|
class PostgreSQLAdapter < AbstractAdapter
|
|
def min_version
|
|
"12"
|
|
end
|
|
|
|
def set_statement_timeout(timeout)
|
|
select_all("SET statement_timeout = #{timeout.to_i * 1000}")
|
|
end
|
|
|
|
def set_lock_timeout(timeout)
|
|
select_all("SET lock_timeout = #{timeout.to_i * 1000}")
|
|
end
|
|
|
|
def check_lock_timeout
|
|
lock_timeout = connection.select_value("SHOW lock_timeout")
|
|
lock_timeout_sec = timeout_to_sec(lock_timeout)
|
|
# validation logic
|
|
end
|
|
|
|
private
|
|
|
|
def select_all(sql)
|
|
connection.select_all(sql)
|
|
end
|
|
|
|
def timeout_to_sec(timeout)
|
|
units = {"us" => 1e-6, "ms" => 1e-3, "s" => 1, "min" => 60}
|
|
timeout.to_f * (units[timeout.gsub(/\d+/, "")] || 1e-3)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
## MySQL Adapter
|
|
|
|
```ruby
|
|
# lib/strong_migrations/adapters/mysql_adapter.rb
|
|
module StrongMigrations
|
|
module Adapters
|
|
class MySQLAdapter < AbstractAdapter
|
|
def min_version
|
|
"8.0"
|
|
end
|
|
|
|
def set_statement_timeout(timeout)
|
|
select_all("SET max_execution_time = #{timeout.to_i * 1000}")
|
|
end
|
|
|
|
def check_lock_timeout
|
|
lock_timeout = connection.select_value("SELECT @@lock_wait_timeout")
|
|
# validation logic
|
|
end
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
## MariaDB Adapter (MySQL variant)
|
|
|
|
```ruby
|
|
# lib/strong_migrations/adapters/mariadb_adapter.rb
|
|
module StrongMigrations
|
|
module Adapters
|
|
class MariaDBAdapter < MySQLAdapter
|
|
def min_version
|
|
"10.5"
|
|
end
|
|
|
|
# Override MySQL-specific behavior
|
|
def set_statement_timeout(timeout)
|
|
select_all("SET max_statement_time = #{timeout.to_i}")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
## Adapter Detection Pattern
|
|
|
|
Use regex matching on adapter name:
|
|
|
|
```ruby
|
|
def adapter
|
|
@adapter ||= case connection.adapter_name
|
|
when /postg/i
|
|
Adapters::PostgreSQLAdapter.new(self)
|
|
when /mysql|trilogy/i
|
|
if connection.try(:mariadb?)
|
|
Adapters::MariaDBAdapter.new(self)
|
|
else
|
|
Adapters::MySQLAdapter.new(self)
|
|
end
|
|
when /sqlite/i
|
|
Adapters::SQLiteAdapter.new(self)
|
|
else
|
|
Adapters::AbstractAdapter.new(self)
|
|
end
|
|
end
|
|
```
|
|
|
|
## Multi-Database Support (PgHero pattern)
|
|
|
|
```ruby
|
|
module PgHero
|
|
class << self
|
|
attr_accessor :databases
|
|
end
|
|
|
|
self.databases = {}
|
|
|
|
def self.primary_database
|
|
databases.values.first
|
|
end
|
|
|
|
def self.capture_query_stats(database: nil)
|
|
db = database ? databases[database] : primary_database
|
|
db.capture_query_stats
|
|
end
|
|
|
|
class Database
|
|
attr_reader :id, :config
|
|
|
|
def initialize(id, config)
|
|
@id = id
|
|
@config = config
|
|
end
|
|
|
|
def connection_model
|
|
@connection_model ||= begin
|
|
Class.new(ActiveRecord::Base) do
|
|
self.abstract_class = true
|
|
end.tap do |model|
|
|
model.establish_connection(config)
|
|
end
|
|
end
|
|
end
|
|
|
|
def connection
|
|
connection_model.connection
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
## Connection Switching
|
|
|
|
```ruby
|
|
def with_connection(database_name)
|
|
db = databases[database_name.to_s]
|
|
raise Error, "Unknown database: #{database_name}" unless db
|
|
|
|
yield db.connection
|
|
end
|
|
|
|
# Usage
|
|
PgHero.with_connection(:replica) do |conn|
|
|
conn.execute("SELECT * FROM users")
|
|
end
|
|
```
|
|
|
|
## SQL Dialect Handling
|
|
|
|
```ruby
|
|
def quote_column(column)
|
|
case adapter_name
|
|
when /postg/i
|
|
%("#{column}")
|
|
when /mysql/i
|
|
"`#{column}`"
|
|
else
|
|
column
|
|
end
|
|
end
|
|
|
|
def boolean_value(value)
|
|
case adapter_name
|
|
when /postg/i
|
|
value ? "true" : "false"
|
|
when /mysql/i
|
|
value ? "1" : "0"
|
|
else
|
|
value.to_s
|
|
end
|
|
end
|
|
```
|