mirror of
https://gitea.invidious.io/iv-org/shard-crystal-db.git
synced 2024-08-15 00:53:32 +00:00
major db refactor for a better api.
`#query`, `#exec`, `#scalar`, `#scalar?` as main query methods from `Database` blocks overrides that ensure statements are closed.
This commit is contained in:
parent
caf2676aad
commit
683e6bdfa7
12 changed files with 574 additions and 139 deletions
25
src/db/connection.cr
Normal file
25
src/db/connection.cr
Normal file
|
@ -0,0 +1,25 @@
|
|||
module DB
|
||||
abstract class Connection
|
||||
getter options
|
||||
|
||||
def initialize(@options)
|
||||
@closed = false
|
||||
end
|
||||
|
||||
# Closes this connection.
|
||||
def close
|
||||
raise "Connection already closed" if @closed
|
||||
@closed = true
|
||||
perform_close
|
||||
end
|
||||
|
||||
# Returns `true` if this statement is closed. See `#close`.
|
||||
def closed?
|
||||
@closed
|
||||
end
|
||||
|
||||
abstract def prepare(query) : Statement
|
||||
|
||||
protected abstract def perform_close
|
||||
end
|
||||
end
|
|
@ -1,43 +1,69 @@
|
|||
module DB
|
||||
# Acts as an entry point for database access.
|
||||
# Offers a com
|
||||
# Currently it creates a single connection to the database.
|
||||
# Eventually a connection pool will be handled.
|
||||
#
|
||||
# It should be created from DB module. See `DB.open`.
|
||||
class Database
|
||||
getter driver_class
|
||||
getter options
|
||||
|
||||
def initialize(@driver_class, @options)
|
||||
@driver = @driver_class.new(@options)
|
||||
@connection = @driver.build_connection
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
# Closes all connection to the database
|
||||
def close
|
||||
@connection.close
|
||||
end
|
||||
|
||||
# Returns a `Connection` to the database
|
||||
def connection
|
||||
@connection
|
||||
end
|
||||
|
||||
# Prepares a `Statement`. The Statement must be closed explicitly
|
||||
# after is not longer in use.
|
||||
#
|
||||
# Usually `#exec`, `#query` or `#scalar` should be used.
|
||||
def prepare(query)
|
||||
@driver.prepare(query)
|
||||
connection.prepare(query)
|
||||
end
|
||||
|
||||
def query(query, *args)
|
||||
prepare(query).query(*args)
|
||||
end
|
||||
|
||||
def query(query, *args)
|
||||
# CHECK prepare(query).query(*args, &block)
|
||||
query(query, *args).tap do |rs|
|
||||
begin
|
||||
yield rs
|
||||
ensure
|
||||
rs.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
def exec(query, *args)
|
||||
prepare(query).exec(*args)
|
||||
end
|
||||
|
||||
def exec_non_query(query, *args)
|
||||
exec_query(query) do |result_set|
|
||||
result_set.move_next
|
||||
end
|
||||
def scalar(query, *args)
|
||||
prepare(query).scalar(*args)
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
def exec_query(query, *args)
|
||||
result_set = exec(query, *args)
|
||||
yield result_set
|
||||
result_set.close
|
||||
def scalar(t, query, *args)
|
||||
prepare(query).scalar(t, *args)
|
||||
end
|
||||
|
||||
def exec_query_each(query, *args)
|
||||
exec_query(query) do |result_set|
|
||||
result_set.each do
|
||||
yield result_set
|
||||
end
|
||||
end
|
||||
def scalar?(query, *args)
|
||||
prepare(query).scalar?(*args)
|
||||
end
|
||||
|
||||
def scalar?(t, query, *args)
|
||||
prepare(query).scalar?(t, *args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,6 +2,7 @@ module DB
|
|||
TYPES = [String, Int32, Int64, Float32, Float64, Slice(UInt8)]
|
||||
alias Any = String | Int32 | Int64 | Float32 | Float64 | Slice(UInt8)
|
||||
|
||||
# :nodoc:
|
||||
def self.driver_class(name) # : Driver.class
|
||||
@@drivers.not_nil![name]
|
||||
end
|
||||
|
@ -14,9 +15,17 @@ module DB
|
|||
def self.open(name, options)
|
||||
Database.new(driver_class(name), options)
|
||||
end
|
||||
|
||||
def self.open(name, options, &block)
|
||||
open(name, options).tap do |db|
|
||||
yield db
|
||||
db.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "./database"
|
||||
require "./driver"
|
||||
require "./connection"
|
||||
require "./statement"
|
||||
require "./result_set"
|
||||
|
|
|
@ -5,6 +5,6 @@ module DB
|
|||
def initialize(@options)
|
||||
end
|
||||
|
||||
abstract def prepare(query) : Statement
|
||||
abstract def build_connection : Connection
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,10 +17,11 @@ module DB
|
|||
|
||||
abstract def move_next : Bool
|
||||
|
||||
# TODO def empty? : Bool, handle internally with move_next (?)
|
||||
|
||||
abstract def column_count : Int32
|
||||
abstract def column_name(index : Int32) : String
|
||||
|
||||
# abstract def column_type(index : Int32)
|
||||
abstract def column_type(index : Int32)
|
||||
|
||||
# list datatypes that must be supported form the driver
|
||||
# users will call read(String) or read?(String) for nillables
|
||||
|
|
|
@ -1,23 +1,71 @@
|
|||
module DB
|
||||
abstract class Statement
|
||||
getter driver
|
||||
getter connection
|
||||
|
||||
def initialize(@driver)
|
||||
def initialize(@connection)
|
||||
@closed = false
|
||||
end
|
||||
|
||||
def exec(*args) : ResultSet
|
||||
exec args
|
||||
def exec(*args)
|
||||
execute(*args).close
|
||||
end
|
||||
|
||||
def exec(arg : Slice(UInt8))
|
||||
before_execute
|
||||
def scalar(*args)
|
||||
scalar(Int32, *args)
|
||||
end
|
||||
|
||||
# t in DB::TYPES
|
||||
def scalar(t, *args)
|
||||
query(*args) do |rs|
|
||||
rs.each do
|
||||
return rs.read(t)
|
||||
end
|
||||
end
|
||||
|
||||
raise "unreachable"
|
||||
end
|
||||
|
||||
def scalar?(*args)
|
||||
scalar?(Int32, *args)
|
||||
end
|
||||
|
||||
# t in DB::TYPES
|
||||
def scalar?(t, *args)
|
||||
query(*args) do |rs|
|
||||
rs.each do
|
||||
return rs.read?(t)
|
||||
end
|
||||
end
|
||||
|
||||
raise "unreachable"
|
||||
end
|
||||
|
||||
def query(*args)
|
||||
execute *args
|
||||
end
|
||||
|
||||
def query(*args)
|
||||
execute(*args).tap do |rs|
|
||||
begin
|
||||
yield rs
|
||||
ensure
|
||||
rs.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private def execute(*args) : ResultSet
|
||||
execute args
|
||||
end
|
||||
|
||||
private def execute(arg : Slice(UInt8))
|
||||
begin_parameters
|
||||
add_parameter 1, arg
|
||||
execute
|
||||
perform
|
||||
end
|
||||
|
||||
def exec(args : Enumerable)
|
||||
before_execute
|
||||
private def execute(args : Enumerable)
|
||||
begin_parameters
|
||||
args.each_with_index(1) do |arg, index|
|
||||
if arg.is_a?(Hash)
|
||||
arg.each do |key, value|
|
||||
|
@ -27,10 +75,7 @@ module DB
|
|||
add_parameter index, arg
|
||||
end
|
||||
end
|
||||
execute
|
||||
end
|
||||
|
||||
protected def before_execute
|
||||
perform
|
||||
end
|
||||
|
||||
# Closes this statement.
|
||||
|
@ -46,10 +91,12 @@ module DB
|
|||
end
|
||||
|
||||
# 1-based positional arguments
|
||||
protected def begin_parameters
|
||||
end
|
||||
protected abstract def add_parameter(index : Int32, value)
|
||||
protected abstract def add_parameter(name : String, value)
|
||||
|
||||
protected abstract def execute : ResultSet
|
||||
protected abstract def perform : ResultSet
|
||||
protected def on_close
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue