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
|
it "should close pool statements when closing db" do
|
||||||
stmt = uninitialized DB::PoolStatement
|
stmt = uninitialized DB::PoolStatement
|
||||||
with_dummy do |db|
|
with_dummy do |db|
|
||||||
stmt = db.build("query1")
|
# TODO remove cast
|
||||||
|
stmt = db.build("query1").as(DB::PoolStatement)
|
||||||
end
|
end
|
||||||
stmt.closed?.should be_true
|
stmt.closed?.should be_true
|
||||||
end
|
end
|
||||||
|
@ -128,5 +129,47 @@ describe DB::Database do
|
||||||
connection.prepared_statements?.should be_false
|
connection.prepared_statements?.should be_false
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -140,6 +140,7 @@ require "./db/driver"
|
||||||
require "./db/connection"
|
require "./db/connection"
|
||||||
require "./db/statement"
|
require "./db/statement"
|
||||||
require "./db/pool_statement"
|
require "./db/pool_statement"
|
||||||
|
require "./db/pool_unprepared_statement"
|
||||||
require "./db/result_set"
|
require "./db/result_set"
|
||||||
require "./db/error"
|
require "./db/error"
|
||||||
require "./db/mapping"
|
require "./db/mapping"
|
||||||
|
|
|
@ -17,6 +17,8 @@ module DB
|
||||||
#
|
#
|
||||||
# Refer to `QueryMethods` for documentation about querying the database.
|
# Refer to `QueryMethods` for documentation about querying the database.
|
||||||
class Database
|
class Database
|
||||||
|
include QueryMethods
|
||||||
|
|
||||||
# :nodoc:
|
# :nodoc:
|
||||||
getter driver
|
getter driver
|
||||||
# :nodoc:
|
# :nodoc:
|
||||||
|
@ -63,7 +65,26 @@ module DB
|
||||||
|
|
||||||
# :nodoc:
|
# :nodoc:
|
||||||
def build(query)
|
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
|
end
|
||||||
|
|
||||||
# :nodoc:
|
# :nodoc:
|
||||||
|
@ -95,6 +116,48 @@ module DB
|
||||||
end
|
end
|
||||||
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
|
||||||
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