mirror of
https://gitea.invidious.io/iv-org/shard-crystal-db.git
synced 2024-08-15 00:53:32 +00:00
split perform_query and perform_exec
add DB::ExecResult to have a concurrency safety exec result remove connection string from abstract DB::Connection
This commit is contained in:
parent
67fe5c9aae
commit
3598dddb65
7 changed files with 47 additions and 57 deletions
|
@ -36,24 +36,10 @@ describe DB do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "exec should close statement" do
|
|
||||||
with_dummy do |db|
|
|
||||||
db.exec ""
|
|
||||||
DummyDriver::DummyResultSet.last_result_set.closed?.should be_true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "scalar should close statement" do
|
it "scalar should close statement" do
|
||||||
with_dummy do |db|
|
with_dummy do |db|
|
||||||
db.scalar "1"
|
db.scalar "1"
|
||||||
DummyDriver::DummyResultSet.last_result_set.closed?.should be_true
|
DummyDriver::DummyResultSet.last_result_set.closed?.should be_true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "exec should perform statement" do
|
|
||||||
with_dummy do |db|
|
|
||||||
db.exec ""
|
|
||||||
DummyDriver::DummyResultSet.last_result_set.executed?.should be_true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,11 @@ class DummyDriver < DB::Driver
|
||||||
end
|
end
|
||||||
|
|
||||||
class DummyConnection < DB::Connection
|
class DummyConnection < DB::Connection
|
||||||
|
getter connection_string
|
||||||
|
|
||||||
|
def initialize(@connection_string)
|
||||||
|
end
|
||||||
|
|
||||||
def prepare(query)
|
def prepare(query)
|
||||||
DummyStatement.new(self, query)
|
DummyStatement.new(self, query)
|
||||||
end
|
end
|
||||||
|
@ -26,12 +31,21 @@ class DummyDriver < DB::Driver
|
||||||
super(driver)
|
super(driver)
|
||||||
end
|
end
|
||||||
|
|
||||||
protected def perform(args : Slice(DB::Any))
|
protected def perform_query(args : Slice(DB::Any))
|
||||||
|
set_params args
|
||||||
|
DummyResultSet.new self, @query
|
||||||
|
end
|
||||||
|
|
||||||
|
protected def perform_exec(args : Slice(DB::Any))
|
||||||
|
set_params args
|
||||||
|
DB::ExecResult.new 0, 0
|
||||||
|
end
|
||||||
|
|
||||||
|
private def set_params(args)
|
||||||
@params.clear
|
@params.clear
|
||||||
args.each_with_index do |arg, index|
|
args.each_with_index do |arg, index|
|
||||||
@params[index] = arg
|
@params[index] = arg
|
||||||
end
|
end
|
||||||
DummyResultSet.new self, @query
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -42,7 +56,6 @@ class DummyDriver < DB::Driver
|
||||||
super(statement)
|
super(statement)
|
||||||
@iterator = query.split.map { |r| r.split(',') }.to_a.each
|
@iterator = query.split.map { |r| r.split(',') }.to_a.each
|
||||||
|
|
||||||
@executed = false
|
|
||||||
@@last_result_set = self
|
@@last_result_set = self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -50,12 +63,7 @@ class DummyDriver < DB::Driver
|
||||||
@@last_result_set.not_nil!
|
@@last_result_set.not_nil!
|
||||||
end
|
end
|
||||||
|
|
||||||
def executed?
|
|
||||||
@executed
|
|
||||||
end
|
|
||||||
|
|
||||||
def move_next
|
def move_next
|
||||||
@executed = true
|
|
||||||
@iterator.next.tap do |n|
|
@iterator.next.tap do |n|
|
||||||
return false if n.is_a?(Iterator::Stop)
|
return false if n.is_a?(Iterator::Stop)
|
||||||
@values = n.each
|
@values = n.each
|
||||||
|
|
|
@ -17,12 +17,8 @@ module DB
|
||||||
abstract class Connection
|
abstract class Connection
|
||||||
# TODO add IDLE status, for connection ppool management.
|
# TODO add IDLE status, for connection ppool management.
|
||||||
|
|
||||||
getter connection_string # TODO Remove
|
|
||||||
@closed = false
|
@closed = false
|
||||||
|
|
||||||
def initialize(@connection_string) # TODO Remove
|
|
||||||
end
|
|
||||||
|
|
||||||
# Closes this connection.
|
# Closes this connection.
|
||||||
def close
|
def close
|
||||||
raise "Connection already closed" if @closed # TODO make it no fail if closed
|
raise "Connection already closed" if @closed # TODO make it no fail if closed
|
||||||
|
@ -45,10 +41,6 @@ module DB
|
||||||
|
|
||||||
include QueryMethods
|
include QueryMethods
|
||||||
|
|
||||||
# Returns the last inserted id through this connection.
|
|
||||||
abstract def last_insert_id : Int64 # TODO move to ExecResult record. plano. with last_rows. eagerly askit.
|
|
||||||
|
|
||||||
|
|
||||||
protected abstract def perform_close # TODO do_close
|
protected abstract def perform_close # TODO do_close
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -67,8 +67,10 @@ module DB
|
||||||
# See `DB::TYPES` in `DB`. `Any` is a nillable version of the union of all types in `DB::TYPES`
|
# See `DB::TYPES` in `DB`. `Any` is a nillable version of the union of all types in `DB::TYPES`
|
||||||
alias Any = Nil | String | Int32 | Int64 | Float32 | Float64 | Slice(UInt8)
|
alias Any = Nil | String | Int32 | Int64 | Float32 | Float64 | Slice(UInt8)
|
||||||
|
|
||||||
# :nodoc:
|
# Result of a `#exec` statement.
|
||||||
|
record ExecResult, rows_affected, last_insert_id
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
def self.driver_class(driver_name) # : Driver.class
|
def self.driver_class(driver_name) # : Driver.class
|
||||||
@@drivers.not_nil![driver_name]
|
@@drivers.not_nil![driver_name]
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,19 +3,22 @@ module DB
|
||||||
# All methods accepts a `query : String` and a set arguments.
|
# All methods accepts a `query : String` and a set arguments.
|
||||||
#
|
#
|
||||||
# Three kind of statements can be performed:
|
# Three kind of statements can be performed:
|
||||||
# 1. `#exec` waits no response from the database.
|
# 1. `#exec` waits no record response from the database. An `ExecResult` is returned.
|
||||||
# 2. `#scalar` reads a single value of the response. Use `#scalar?` if the response is nillable.
|
# 2. `#scalar` reads a single value of the response. A `DB::Any` is returned.
|
||||||
# 3. `#query` returns a ResultSet that allows iteration over the rows in the response and column information.
|
# 3. `#query` returns a `ResultSet` that allows iteration over the rows in the response and column information.
|
||||||
#
|
#
|
||||||
# Arguments can be passed:
|
# Arguments can be passed by position
|
||||||
# * by position: `db.query("SELECT name FROM ... WHERE age > ?", age)`
|
#
|
||||||
# * by symbol: `db.query("SELECT name FROM ... WHERE age > :age", {age: age})`
|
# ```
|
||||||
# * by string: `db.query("SELECT name FROM ... WHERE age > :age", {"age": age})`
|
# db.query("SELECT name FROM ... WHERE age > ?", age)
|
||||||
|
# ```
|
||||||
#
|
#
|
||||||
# Convention of mapping how arguments are mapped to the query depends on each driver.
|
# Convention of mapping how arguments are mapped to the query depends on each driver.
|
||||||
#
|
#
|
||||||
# Including `QueryMethods` requires a `prepare(query) : Statement` method.
|
# Including `QueryMethods` requires a `prepare(query) : Statement` method.
|
||||||
module QueryMethods
|
module QueryMethods
|
||||||
|
abstract def prepare(query) : Statement
|
||||||
|
|
||||||
# Returns a `ResultSet` for the `query`.
|
# Returns a `ResultSet` for the `query`.
|
||||||
# The `ResultSet` must be closed manually.
|
# The `ResultSet` must be closed manually.
|
||||||
def query(query, *args)
|
def query(query, *args)
|
||||||
|
|
|
@ -47,11 +47,6 @@ module DB
|
||||||
protected def do_close
|
protected def do_close
|
||||||
end
|
end
|
||||||
|
|
||||||
# Ensures it executes the query
|
|
||||||
def exec
|
|
||||||
move_next
|
|
||||||
end
|
|
||||||
|
|
||||||
# Move the next row in the result.
|
# Move the next row in the result.
|
||||||
# Return `false` if no more rows are available.
|
# Return `false` if no more rows are available.
|
||||||
# See `#each`
|
# See `#each`
|
||||||
|
|
|
@ -6,10 +6,9 @@ module DB
|
||||||
#
|
#
|
||||||
# 1. Subclass `Statements`
|
# 1. Subclass `Statements`
|
||||||
# 2. `Statements` are created from a custom driver `Connection#prepare` method.
|
# 2. `Statements` are created from a custom driver `Connection#prepare` method.
|
||||||
# 3. `#begin_parameters` is called before the parameters are set.
|
# 3. `#perform_query` executes a query that is expected to return a `ResultSet`
|
||||||
# 4. `#add_parameter` methods helps to support 0-based positional arguments and named arguments
|
# 4. `#perform_exec` executes a query that is expected to return an `ExecResult`
|
||||||
# 5. After parameters are set `#perform` is called to return a `ResultSet`
|
# 6. `#do_close` is called to release the statement resources.
|
||||||
# 6. `#on_close` is called to release the statement resources.
|
|
||||||
abstract class Statement
|
abstract class Statement
|
||||||
getter connection
|
getter connection
|
||||||
|
|
||||||
|
@ -17,11 +16,15 @@ module DB
|
||||||
@closed = false
|
@closed = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# See `QueryMethods#exec`
|
||||||
|
def exec
|
||||||
|
perform_exec(Slice(Any).new(0)) # no overload matches ... with types Slice(NoReturn)
|
||||||
|
end
|
||||||
|
|
||||||
# See `QueryMethods#exec`
|
# See `QueryMethods#exec`
|
||||||
def exec(*args)
|
def exec(*args)
|
||||||
query(*args) do |rs|
|
# TODO better way to do it
|
||||||
rs.exec
|
perform_exec(args.to_a.to_unsafe.to_slice(args.size))
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# See `QueryMethods#scalar`
|
# See `QueryMethods#scalar`
|
||||||
|
@ -55,12 +58,12 @@ module DB
|
||||||
|
|
||||||
# See `QueryMethods#query`
|
# See `QueryMethods#query`
|
||||||
def query(*args)
|
def query(*args)
|
||||||
execute *args
|
perform_query *args
|
||||||
end
|
end
|
||||||
|
|
||||||
# See `QueryMethods#query`
|
# See `QueryMethods#query`
|
||||||
def query(*args)
|
def query(*args)
|
||||||
execute(*args).tap do |rs|
|
perform_query(*args).tap do |rs|
|
||||||
begin
|
begin
|
||||||
yield rs
|
yield rs
|
||||||
ensure
|
ensure
|
||||||
|
@ -69,13 +72,13 @@ module DB
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private def execute : ResultSet
|
private def perform_query : ResultSet
|
||||||
perform(Slice(Any).new(0))
|
perform_query(Slice(Any).new(0)) # no overload matches ... with types Slice(NoReturn)
|
||||||
end
|
end
|
||||||
|
|
||||||
private def execute(*args) : ResultSet
|
private def perform_query(*args) : ResultSet
|
||||||
# TODO better way to do it
|
# TODO better way to do it
|
||||||
perform(args.to_a.to_unsafe.to_slice(args.size))
|
perform_query(args.to_a.to_unsafe.to_slice(args.size))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Closes this statement.
|
# Closes this statement.
|
||||||
|
@ -95,7 +98,8 @@ module DB
|
||||||
close
|
close
|
||||||
end
|
end
|
||||||
|
|
||||||
protected abstract def perform(args : Slice(Any)) : ResultSet
|
protected abstract def perform_query(args : Slice(Any)) : ResultSet
|
||||||
|
protected abstract def perform_exec(args : Slice(Any)) : ExecResult
|
||||||
|
|
||||||
protected def do_close
|
protected def do_close
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue