Introduce DB::ConnectionContext (#44)

* make Database a ConnectionContext.
* introduce SingleConnectionContext for independant connections.
* add `DB#connect` to create non pooled connections.
This commit is contained in:
Brian J. Cardiff 2017-03-20 15:08:30 -03:00 committed by GitHub
parent c63ea48748
commit 385cf70a8a
8 changed files with 115 additions and 23 deletions

View file

@ -43,8 +43,8 @@ class FooDriver < DB::Driver
@@row @@row
end end
def build_connection(db : DB::Database) : DB::Connection def build_connection(context : DB::ConnectionContext) : DB::Connection
FooConnection.new(db) FooConnection.new(context)
end end
class FooConnection < DB::Connection class FooConnection < DB::Connection
@ -106,8 +106,8 @@ class BarDriver < DB::Driver
@@row @@row
end end
def build_connection(db : DB::Database) : DB::Connection def build_connection(context : DB::ConnectionContext) : DB::Connection
BarConnection.new(db) BarConnection.new(context)
end end
class BarConnection < DB::Connection class BarConnection < DB::Connection

View file

@ -25,6 +25,25 @@ describe DB do
connections.first.closed?.should be_true connections.first.closed?.should be_true
end end
it "should create a connection and close it" do
DummyDriver::DummyConnection.clear_connections
DB.connect "dummy://localhost" do |cnn|
cnn.should be_a(DummyDriver::DummyConnection)
end
connections.size.should eq(1)
connections.first.closed?.should be_true
end
it "should create a connection and wait for explicit closing" do
DummyDriver::DummyConnection.clear_connections
cnn = DB.connect "dummy://localhost"
cnn.should be_a(DummyDriver::DummyConnection)
connections.size.should eq(1)
connections.first.closed?.should be_false
cnn.close
connections.first.closed?.should be_true
end
it "query should close result_set" do it "query should close result_set" do
with_witness do |w| with_witness do |w|
with_dummy do |db| with_dummy do |db|

View file

@ -2,13 +2,13 @@ require "spec"
require "../src/db" require "../src/db"
class DummyDriver < DB::Driver class DummyDriver < DB::Driver
def build_connection(db : DB::Database) : DB::Connection def build_connection(context : DB::ConnectionContext) : DB::Connection
DummyConnection.new(db) DummyConnection.new(context)
end end
class DummyConnection < DB::Connection class DummyConnection < DB::Connection
def initialize(db) def initialize(context)
super(db) super(context)
@connected = true @connected = true
@@connections ||= [] of DummyConnection @@connections ||= [] of DummyConnection
@@connections.not_nil! << self @@connections.not_nil! << self

View file

@ -113,12 +113,42 @@ module DB
end end
end end
# Opens a connection using the specified *uri*.
# The scheme of the *uri* determines the driver to use.
# Returned connection must be closed by `Connection#close`.
# If a block is used the connection is yielded and closed automatically.
def self.connect(uri : URI | String)
build_connection(uri)
end
# ditto
def self.connect(uri : URI | String, &block)
cnn = build_connection(uri)
begin
yield cnn
ensure
cnn.close
end
end
private def self.build_database(connection_string : String) private def self.build_database(connection_string : String)
build_database(URI.parse(connection_string)) build_database(URI.parse(connection_string))
end end
private def self.build_database(uri : URI) private def self.build_database(uri : URI)
Database.new(driver_class(uri.scheme).new, uri) Database.new(build_driver(uri), uri)
end
private def self.build_connection(connection_string : String)
build_connection(URI.parse(connection_string))
end
private def self.build_connection(uri : URI)
build_driver(uri).build_connection(SingleConnectionContext.new(uri)).as(Connection)
end
private def self.build_driver(uri : URI)
driver_class(uri.scheme).new
end end
# :nodoc: # :nodoc:
@ -144,6 +174,7 @@ require "./db/disposable"
require "./db/driver" require "./db/driver"
require "./db/statement" require "./db/statement"
require "./db/begin_transaction" require "./db/begin_transaction"
require "./db/connection_context"
require "./db/connection" require "./db/connection"
require "./db/transaction" require "./db/transaction"
require "./db/statement" require "./db/statement"

View file

@ -24,15 +24,15 @@ module DB
include BeginTransaction include BeginTransaction
# :nodoc: # :nodoc:
getter database getter context
@statements_cache = StringKeyCache(Statement).new @statements_cache = StringKeyCache(Statement).new
@transaction = false @transaction = false
getter? prepared_statements : Bool getter? prepared_statements : Bool
# :nodoc: # :nodoc:
property auto_release : Bool = true property auto_release : Bool = true
def initialize(@database : Database) def initialize(@context : ConnectionContext)
@prepared_statements = @database.prepared_statements? @prepared_statements = @context.prepared_statements?
end end
# :nodoc: # :nodoc:
@ -59,7 +59,7 @@ module DB
protected def do_close protected def do_close
@statements_cache.each_value &.close @statements_cache.each_value &.close
@statements_cache.clear @statements_cache.clear
@database.pool.delete self @context.discard self
end end
# :nodoc: # :nodoc:
@ -75,7 +75,7 @@ module DB
# managed by the database. Should be used # managed by the database. Should be used
# only if the connection was obtained by `Database#checkout`. # only if the connection was obtained by `Database#checkout`.
def release def release
@database.return_to_pool(self) @context.release(self)
end end
# :nodoc: # :nodoc:

View file

@ -0,0 +1,36 @@
module DB
module ConnectionContext
# Returns the uri with the connection settings to the database
abstract def uri : URI
# Return whether the statements should be prepared by default
abstract def prepared_statements? : Bool
# Indicates that the *connection* was permanently closed
# and should not be used in the future.
abstract def discard(connection : Connection)
# Indicates that the *connection* is no longer needed
# and can be reused in the future.
abstract def release(connection : Connection)
end
# :nodoc:
class SingleConnectionContext
include ConnectionContext
getter uri : URI
getter? prepared_statements : Bool
def initialize(@uri : URI)
params = HTTP::Params.parse(uri.query || "")
@prepared_statements = DB.fetch_bool(params, "prepared_statements", true)
end
def discard(connection : Connection)
end
def release(connection : Connection)
end
end
end

View file

@ -23,6 +23,7 @@ module DB
# Refer to `QueryMethods` and `SessionMethods` for documentation about querying the database. # Refer to `QueryMethods` and `SessionMethods` for documentation about querying the database.
class Database class Database
include SessionMethods(Database, PoolStatement) include SessionMethods(Database, PoolStatement)
include ConnectionContext
# :nodoc: # :nodoc:
getter driver getter driver
@ -68,6 +69,16 @@ module DB
@pool.close @pool.close
end end
# :nodoc:
def discard(connection : Connection)
@pool.delete connection
end
# :nodoc:
def release(connection : Connection)
@pool.release connection
end
# :nodoc: # :nodoc:
def fetch_or_build_prepared_statement(query) def fetch_or_build_prepared_statement(query)
@statements_cache.fetch(query) { build_prepared_statement(query) } @statements_cache.fetch(query) { build_prepared_statement(query) }
@ -88,11 +99,6 @@ module DB
@pool.checkout_some candidates @pool.checkout_some candidates
end end
# :nodoc:
def return_to_pool(connection)
@pool.release connection
end
# yields a connection from the pool # yields a connection from the pool
# the connection is returned to the pool # the connection is returned to the pool
# when the block ends # when the block ends

View file

@ -6,9 +6,9 @@ module DB
# ``` # ```
# require "db" # require "db"
# #
# class FakeDriver < Driver # class FakeDriver < DB::Driver
# def build_connection(db) # def build_connection(context : DB::ConnectionContext)
# FakeConnection.new db # FakeConnection.new context
# end # end
# end # end
# #
@ -26,7 +26,7 @@ module DB
# Refer to `Connection`, `Statement` and `ResultSet` for further # Refer to `Connection`, `Statement` and `ResultSet` for further
# driver implementation instructions. # driver implementation instructions.
abstract class Driver abstract class Driver
abstract def build_connection(db : Database) : Connection abstract def build_connection(context : ConnectionContext) : Connection
def connection_pool_options(params : HTTP::Params) def connection_pool_options(params : HTTP::Params)
{ {