mirror of
https://gitea.invidious.io/iv-org/shard-crystal-db.git
synced 2024-08-15 00:53:32 +00:00
add pool unprepared statements
unprepared statements are executed in any free connection of the pool at the moment of executing them
This commit is contained in:
parent
9ef9d19d1d
commit
0593f63dbb
4 changed files with 168 additions and 3 deletions
|
@ -59,7 +59,8 @@ describe DB::Database do
|
|||
it "should close pool statements when closing db" do
|
||||
stmt = uninitialized DB::PoolStatement
|
||||
with_dummy do |db|
|
||||
stmt = db.build("query1")
|
||||
# TODO remove cast
|
||||
stmt = db.build("query1").as(DB::PoolStatement)
|
||||
end
|
||||
stmt.closed?.should be_true
|
||||
end
|
||||
|
@ -128,5 +129,47 @@ describe DB::Database do
|
|||
connection.prepared_statements?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "should build prepared statements if true" do
|
||||
with_dummy "dummy://localhost:1027?prepared_statements=true" do |db|
|
||||
db.build("the query").should be_a(DB::PoolStatement)
|
||||
end
|
||||
end
|
||||
|
||||
it "should build unprepared statements if false" do
|
||||
with_dummy "dummy://localhost:1027?prepared_statements=false" do |db|
|
||||
db.build("the query").should be_a(DB::PoolUnpreparedStatement)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "unprepared statements in pool" do
|
||||
it "creating statements should not create new connections" do
|
||||
with_dummy "dummy://localhost:1027?initial_pool_size=1" do |db|
|
||||
stmt1 = db.unprepared.build("query1")
|
||||
stmt2 = db.unprepared.build("query2")
|
||||
DummyDriver::DummyConnection.connections.size.should eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
it "simultaneous statements should go to different connections" do
|
||||
with_dummy "dummy://localhost:1027?initial_pool_size=1" do |db|
|
||||
rs1 = db.unprepared.query("query1")
|
||||
rs2 = db.unprepared.query("query2")
|
||||
rs1.statement.connection.should_not eq(rs2.statement.connection)
|
||||
DummyDriver::DummyConnection.connections.size.should eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
it "sequential statements should go to different connections" do
|
||||
with_dummy "dummy://localhost:1027?initial_pool_size=1" do |db|
|
||||
rs1 = db.unprepared.query("query1")
|
||||
rs1.close
|
||||
rs2 = db.unprepared.query("query2")
|
||||
rs2.close
|
||||
rs1.statement.connection.should eq(rs2.statement.connection)
|
||||
DummyDriver::DummyConnection.connections.size.should eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -140,6 +140,7 @@ require "./db/driver"
|
|||
require "./db/connection"
|
||||
require "./db/statement"
|
||||
require "./db/pool_statement"
|
||||
require "./db/pool_unprepared_statement"
|
||||
require "./db/result_set"
|
||||
require "./db/error"
|
||||
require "./db/mapping"
|
||||
|
|
|
@ -17,6 +17,8 @@ module DB
|
|||
#
|
||||
# Refer to `QueryMethods` for documentation about querying the database.
|
||||
class Database
|
||||
include QueryMethods
|
||||
|
||||
# :nodoc:
|
||||
getter driver
|
||||
# :nodoc:
|
||||
|
@ -63,7 +65,26 @@ module DB
|
|||
|
||||
# :nodoc:
|
||||
def build(query)
|
||||
@statements_cache.fetch(query) { PoolStatement.new(self, query) }
|
||||
if prepared_statements?
|
||||
fetch_or_build_prepared_statement(query)
|
||||
else
|
||||
build_unprepared_statement(query)
|
||||
end
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
def fetch_or_build_prepared_statement(query)
|
||||
@statements_cache.fetch(query) { build_prepared_statement(query) }
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
def build_prepared_statement(query)
|
||||
PoolStatement.new(self, query)
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
def build_unprepared_statement(query)
|
||||
PoolUnpreparedStatement.new(self, query)
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
|
@ -95,6 +116,48 @@ module DB
|
|||
end
|
||||
end
|
||||
|
||||
include QueryMethods
|
||||
# dsl helper to build prepared statements
|
||||
# returns a value that includes `QueryMethods`
|
||||
def prepared
|
||||
PreparedQuery.new(self)
|
||||
end
|
||||
|
||||
# Returns a prepared `Statement` that has not been executed yet.
|
||||
def prepared(query)
|
||||
prepared.build(query)
|
||||
end
|
||||
|
||||
# dsl helper to build unprepared statements
|
||||
# returns a value that includes `QueryMethods`
|
||||
def unprepared
|
||||
UnpreparedQuery.new(self)
|
||||
end
|
||||
|
||||
# Returns an unprepared `Statement` that has not been executed yet.
|
||||
def unprepared(query)
|
||||
unprepared.build(query)
|
||||
end
|
||||
|
||||
struct PreparedQuery
|
||||
include QueryMethods
|
||||
|
||||
def initialize(@db : Database)
|
||||
end
|
||||
|
||||
def build(query)
|
||||
@db.fetch_or_build_prepared_statement(query)
|
||||
end
|
||||
end
|
||||
|
||||
struct UnpreparedQuery
|
||||
include QueryMethods
|
||||
|
||||
def initialize(@db : Database)
|
||||
end
|
||||
|
||||
def build(query)
|
||||
@db.build_unprepared_statement(query)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
58
src/db/pool_unprepared_statement.cr
Normal file
58
src/db/pool_unprepared_statement.cr
Normal file
|
@ -0,0 +1,58 @@
|
|||
module DB
|
||||
# Represents a statement to be executed in any of the connections
|
||||
# of the pool. The statement is not be executed in a non prepared fashion.
|
||||
# The execution of the statement is retried according to the pool configuration.
|
||||
#
|
||||
# See `PoolStatement`
|
||||
class PoolUnpreparedStatement
|
||||
include StatementMethods
|
||||
|
||||
def initialize(@db : Database, @query : String)
|
||||
end
|
||||
|
||||
protected def do_close
|
||||
# unprepared statements do not need to be release in each connection
|
||||
end
|
||||
|
||||
# See `QueryMethods#exec`
|
||||
def exec : ExecResult
|
||||
statement_with_retry &.exec
|
||||
end
|
||||
|
||||
# See `QueryMethods#exec`
|
||||
def exec(*args) : ExecResult
|
||||
statement_with_retry &.exec(*args)
|
||||
end
|
||||
|
||||
# See `QueryMethods#exec`
|
||||
def exec(args : Array) : ExecResult
|
||||
statement_with_retry &.exec(args)
|
||||
end
|
||||
|
||||
# See `QueryMethods#query`
|
||||
def query : ResultSet
|
||||
statement_with_retry &.query
|
||||
end
|
||||
|
||||
# See `QueryMethods#query`
|
||||
def query(*args) : ResultSet
|
||||
statement_with_retry &.query(*args)
|
||||
end
|
||||
|
||||
# See `QueryMethods#query`
|
||||
def query(args : Array) : ResultSet
|
||||
statement_with_retry &.query(args)
|
||||
end
|
||||
|
||||
# builds a statement over a real connection
|
||||
private def build_statement
|
||||
@db.pool.checkout.unprepared.build(@query)
|
||||
end
|
||||
|
||||
private def statement_with_retry
|
||||
@db.retry do
|
||||
return yield build_statement
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue