From d45427dfddc2ca006324913d980b29eff74d7300 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 3 Feb 2016 19:30:51 -0300 Subject: [PATCH] expose database in connection get/return from pool while result set is been used. still single connection pool --- spec/std/db/db_spec.cr | 20 ++++++++++++++------ spec/std/db/dummy_driver.cr | 11 ++++++----- src/db/connection.cr | 6 ++++++ src/db/database.cr | 31 +++++++++++++++++++------------ src/db/db.cr | 2 +- src/db/driver.cr | 12 +++++------- src/db/result_set.cr | 5 +++++ src/db/statement.cr | 3 +++ 8 files changed, 59 insertions(+), 31 deletions(-) diff --git a/spec/std/db/db_spec.cr b/spec/std/db/db_spec.cr index 17a9da1..c62e46e 100644 --- a/spec/std/db/db_spec.cr +++ b/spec/std/db/db_spec.cr @@ -13,20 +13,18 @@ describe DB do it "should instantiate driver with connection uri" do db = DB.open "dummy://localhost:1027" - db.driver_class.should eq(DummyDriver) + db.driver.should be_a(DummyDriver) db.uri.scheme.should eq("dummy") db.uri.host.should eq("localhost") db.uri.port.should eq(1027) end it "should create a connection and close it" do - cnn = nil + DummyDriver::DummyConnection.clear_connections DB.open "dummy://localhost" do |db| - cnn = db.connection end - - cnn.should be_a(DummyDriver::DummyConnection) - cnn.not_nil!.closed?.should be_true + connections.size.should eq(1) + connections.first.closed?.should be_true end it "query should close result_set" do @@ -61,4 +59,14 @@ describe DB do end connections.first.closed?.should be_true end + + it "should raise if the sole connection is been used" do + with_dummy do |db| + db.query "1" do |rs| + expect_raises Exception, /DB Pool Exhausted/ do + db.scalar "2" + end + end + end + end end diff --git a/spec/std/db/dummy_driver.cr b/spec/std/db/dummy_driver.cr index 10d925d..b721c14 100644 --- a/spec/std/db/dummy_driver.cr +++ b/spec/std/db/dummy_driver.cr @@ -1,14 +1,13 @@ require "spec" class DummyDriver < DB::Driver - def build_connection - DummyConnection.new(uri) + def build_connection(db : DB::Database) : DB::Connection + DummyConnection.new(db) end class DummyConnection < DB::Connection - getter uri - - def initialize(@uri) + def initialize(db) + super(db) @@connections ||= [] of DummyConnection @@connections.not_nil! << self end @@ -60,6 +59,7 @@ class DummyDriver < DB::Driver end protected def do_close + super end end @@ -74,6 +74,7 @@ class DummyDriver < DB::Driver end protected def do_close + super end def self.last_result_set diff --git a/src/db/connection.cr b/src/db/connection.cr index 08c19c7..5980cb9 100644 --- a/src/db/connection.cr +++ b/src/db/connection.cr @@ -17,8 +17,14 @@ module DB abstract class Connection include Disposable include QueryMethods + + # :nodoc: + getter database @statements_cache = {} of String => Statement + def initialize(@database : Database) + end + # :nodoc: def prepare(query) : Statement stmt = @statements_cache.fetch(query, nil) diff --git a/src/db/database.cr b/src/db/database.cr index 38be06b..ef2a459 100644 --- a/src/db/database.cr +++ b/src/db/database.cr @@ -8,30 +8,37 @@ module DB # Refer to `QueryMethods` for documentation about querying the database. class Database # :nodoc: - getter driver_class + getter driver - # Connection configuration to the database. + # Returns the uri with the connection settings to the database getter uri # :nodoc: - def initialize(@driver_class, @uri) - @driver = @driver_class.new(@uri) - @connection = @driver.build_connection + def initialize(@driver, @uri) + @in_pool = true + @connection = @driver.build_connection(self) end # Closes all connection to the database. def close - @connection.close - end - - # :nodoc: - def connection - @connection + @connection.try &.close end # :nodoc: def prepare(query) - connection.prepare(query) + get_from_pool.prepare(query) + end + + # :nodoc: + def get_from_pool + raise "DB Pool Exhausted" unless @in_pool + @in_pool = false + @connection.not_nil! + end + + # :nodoc: + def return_to_pool(connection) + @in_pool = true end include QueryMethods diff --git a/src/db/db.cr b/src/db/db.cr index a8decf2..44a3e72 100644 --- a/src/db/db.cr +++ b/src/db/db.cr @@ -105,7 +105,7 @@ module DB end private def self.build_database(uri : URI) - Database.new(driver_class(uri.scheme), uri) + Database.new(driver_class(uri.scheme).new, uri) end end diff --git a/src/db/driver.cr b/src/db/driver.cr index 4115f42..35e5713 100644 --- a/src/db/driver.cr +++ b/src/db/driver.cr @@ -7,8 +7,8 @@ module DB # require "db" # # class FakeDriver < Driver - # def build_connection - # FakeConnection.new uri + # def build_connection(db) + # FakeConnection.new db # end # end # @@ -18,7 +18,7 @@ module DB # Access to this fake datbase will be available with # # ``` - # DB.open "fake", "..." do |db| + # DB.open "fake://..." do |db| # # ... use db ... # end # ``` @@ -26,11 +26,9 @@ module DB # Refer to `Connection`, `Statement` and `ResultSet` for further # driver implementation instructions. abstract class Driver - getter uri - - def initialize(@uri : URI) + def initialize end - abstract def build_connection : Connection + abstract def build_connection(db : Database) : Connection end end diff --git a/src/db/result_set.cr b/src/db/result_set.cr index 1ad6f96..ade02c7 100644 --- a/src/db/result_set.cr +++ b/src/db/result_set.cr @@ -21,6 +21,11 @@ module DB def initialize(@statement : Statement) end + protected def do_close + cnn = statement.connection + cnn.database.return_to_pool(cnn) + end + # TODO add_next_result_set : Bool # Iterates over all the rows diff --git a/src/db/statement.cr b/src/db/statement.cr index 3debd7d..71136a2 100644 --- a/src/db/statement.cr +++ b/src/db/statement.cr @@ -18,6 +18,9 @@ module DB def initialize(@connection) end + protected def do_close + end + # See `QueryMethods#exec` def exec perform_exec(Slice(Any).new(0)) # no overload matches ... with types Slice(NoReturn)