code review notes

close / do_close in result_set
avoid closing statements
remove named arguments
refactor positioned arguments query
This commit is contained in:
Brian J. Cardiff 2016-02-01 21:55:30 -03:00
parent fd804dd592
commit d01da912f7
10 changed files with 86 additions and 189 deletions

View file

@ -23,7 +23,7 @@ describe DB do
cnn.not_nil!.closed?.should be_true cnn.not_nil!.closed?.should be_true
end end
it "query should close statement" do it "query should close result_set" do
with_witness do |w| with_witness do |w|
with_dummy do |db| with_dummy do |db|
db.query "1,2" do db.query "1,2" do
@ -31,7 +31,7 @@ describe DB do
end end
w.check w.check
db.connection.last_statement.closed?.should be_true DummyDriver::DummyResultSet.last_result_set.closed?.should be_true
end end
end end
end end
@ -39,14 +39,14 @@ describe DB do
it "exec should close statement" do it "exec should close statement" do
with_dummy do |db| with_dummy do |db|
db.exec "" db.exec ""
db.connection.last_statement.closed?.should be_true DummyDriver::DummyResultSet.last_result_set.closed?.should be_true
end end
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"
db.connection.last_statement.closed?.should be_true DummyDriver::DummyResultSet.last_result_set.closed?.should be_true
end end
end end
@ -54,7 +54,6 @@ describe DB do
with_dummy do |db| with_dummy do |db|
db.exec "" db.exec ""
DummyDriver::DummyResultSet.last_result_set.executed?.should be_true DummyDriver::DummyResultSet.last_result_set.executed?.should be_true
db.connection.last_statement.closed?.should be_true
end end
end end
end end

View file

@ -6,10 +6,8 @@ class DummyDriver < DB::Driver
end end
class DummyConnection < DB::Connection class DummyConnection < DB::Connection
getter! last_statement
def prepare(query) def prepare(query)
@last_statement = DummyStatement.new(self, query.split.map { |r| r.split ',' }) DummyStatement.new(self, query)
end end
def last_insert_id : Int64 def last_insert_id : Int64
@ -21,32 +19,27 @@ class DummyDriver < DB::Driver
end end
class DummyStatement < DB::Statement class DummyStatement < DB::Statement
property! params property params
def initialize(driver, @items) def initialize(driver, @query)
@params = Hash(Int32 | String, DB::Any).new
super(driver) super(driver)
end end
protected def begin_parameters protected def perform(args : Slice(DB::Any))
@params = Hash(Int32 | String, DB::Any?).new @params.clear
end args.each_with_index do |arg, index|
@params[index] = arg
protected def add_parameter(index : Int32, value) end
params[index] = value DummyResultSet.new self, @query
end
protected def add_parameter(name : String, value)
params[":#{name}"] = value
end
protected def perform
DummyResultSet.new self, @items.each
end end
end end
class DummyResultSet < DB::ResultSet class DummyResultSet < DB::ResultSet
def initialize(statement, @iterator) def initialize(statement, query)
super(statement) super(statement)
@iterator = query.split.map { |r| r.split(',') }.to_a.each
@executed = false @executed = false
@@last_result_set = self @@last_result_set = self
end end
@ -89,10 +82,6 @@ class DummyDriver < DB::Driver
return @statement.params[0] return @statement.params[0]
end end
if n.starts_with?(":")
return @statement.params[n]
end
return n return n
end end

View file

@ -39,7 +39,7 @@ describe DummyDriver do
end end
end end
it "should query with block shuold executes always" do it "should query with block should executes always" do
with_witness do |w| with_witness do |w|
with_dummy do |db| with_dummy do |db|
db.query "" do |rs| db.query "" do |rs|
@ -47,7 +47,9 @@ describe DummyDriver do
end end
end end
end end
end
it "should query with block should executes always" do
with_witness do |w| with_witness do |w|
with_dummy do |db| with_dummy do |db|
db.query "lorem ipsum" do |rs| db.query "lorem ipsum" do |rs|
@ -136,28 +138,6 @@ describe DummyDriver do
db.scalar(typeof({{value}}), "?", {{value}}).should eq({{value}}) db.scalar(typeof({{value}}), "?", {{value}}).should eq({{value}})
end end
end end
it "should set arguments by symbol for {{value.id}}" do
with_dummy do |db|
db.query ":once :twice", {once: {{value}}, twice: {{value + value}} } do |rs|
rs.move_next.should be_true
rs.read(typeof({{value}})).should eq({{value}})
rs.move_next.should be_true
rs.read(typeof({{value}})).should eq({{value + value}})
end
end
end
it "should set arguments by string for {{value.id}}" do
with_dummy do |db|
db.query ":once :twice", {"once": {{value}}, "twice": {{value + value}} } do |rs|
rs.move_next.should be_true
rs.read(typeof({{value}})).should eq({{value}})
rs.move_next.should be_true
rs.read(typeof({{value}})).should eq({{value + value}})
end
end
end
{% end %} {% end %}
it "executes and selects blob" do it "executes and selects blob" do

View file

@ -19,26 +19,6 @@ describe DB::Statement do
end end
end end
it "should initialize symbol named params in query" do
with_dummy do |db|
stmt = db.prepare("the query")
stmt.query({a: "a", b: 1, c: nil})
stmt.params[":a"].should eq("a")
stmt.params[":b"].should eq(1)
stmt.params[":c"].should eq(nil)
end
end
it "should initialize string named params in query" do
with_dummy do |db|
stmt = db.prepare("the query")
stmt.query({"a": "a", "b": 1, "c": nil})
stmt.params[":a"].should eq("a")
stmt.params[":b"].should eq(1)
stmt.params[":c"].should eq(nil)
end
end
it "should initialize positional params in exec" do it "should initialize positional params in exec" do
with_dummy do |db| with_dummy do |db|
stmt = db.prepare("the query") stmt = db.prepare("the query")
@ -49,26 +29,6 @@ describe DB::Statement do
end end
end end
it "should initialize symbol named params in exec" do
with_dummy do |db|
stmt = db.prepare("the query")
stmt.exec({a: "a", b: 1, c: nil})
stmt.params[":a"].should eq("a")
stmt.params[":b"].should eq(1)
stmt.params[":c"].should eq(nil)
end
end
it "should initialize string named params in exec" do
with_dummy do |db|
stmt = db.prepare("the query")
stmt.exec({"a": "a", "b": 1, "c": nil})
stmt.params[":a"].should eq("a")
stmt.params[":b"].should eq(1)
stmt.params[":c"].should eq(nil)
end
end
it "should initialize positional params in scalar" do it "should initialize positional params in scalar" do
with_dummy do |db| with_dummy do |db|
stmt = db.prepare("the query") stmt = db.prepare("the query")
@ -79,26 +39,6 @@ describe DB::Statement do
end end
end end
it "should initialize symbol named params in scalar" do
with_dummy do |db|
stmt = db.prepare("the query")
stmt.scalar(String, {a: "a", b: 1, c: nil})
stmt.params[":a"].should eq("a")
stmt.params[":b"].should eq(1)
stmt.params[":c"].should eq(nil)
end
end
it "should initialize string named params in scalar" do
with_dummy do |db|
stmt = db.prepare("the query")
stmt.scalar(String, {"a": "a", "b": 1, "c": nil})
stmt.params[":a"].should eq("a")
stmt.params[":b"].should eq(1)
stmt.params[":c"].should eq(nil)
end
end
it "should initialize positional params in scalar?" do it "should initialize positional params in scalar?" do
with_dummy do |db| with_dummy do |db|
stmt = db.prepare("the query") stmt = db.prepare("the query")
@ -109,26 +49,6 @@ describe DB::Statement do
end end
end end
it "should initialize symbol named params in scalar?" do
with_dummy do |db|
stmt = db.prepare("the query")
stmt.scalar?(String, {a: "a", b: 1, c: nil})
stmt.params[":a"].should eq("a")
stmt.params[":b"].should eq(1)
stmt.params[":c"].should eq(nil)
end
end
it "should initialize string named params in scalar?" do
with_dummy do |db|
stmt = db.prepare("the query")
stmt.scalar?(String, {"a": "a", "b": 1, "c": nil})
stmt.params[":a"].should eq("a")
stmt.params[":b"].should eq(1)
stmt.params[":c"].should eq(nil)
end
end
it "query with block should not close statement" do it "query with block should not close statement" do
with_dummy do |db| with_dummy do |db|
stmt = db.prepare "3,4 1,2" stmt = db.prepare "3,4 1,2"
@ -137,45 +57,45 @@ describe DB::Statement do
end end
end end
it "query with block should close statement" do it "query with block should not close statement" do
with_dummy do |db| with_dummy do |db|
stmt = db.prepare "3,4 1,2" stmt = db.prepare "3,4 1,2"
stmt.query do |rs| stmt.query do |rs|
end end
stmt.closed?.should be_true stmt.closed?.should be_false
end end
end end
it "query should close statement" do it "query should not close statement" do
with_dummy do |db| with_dummy do |db|
stmt = db.prepare "3,4 1,2" stmt = db.prepare "3,4 1,2"
stmt.query do |rs| stmt.query do |rs|
end end
stmt.closed?.should be_true stmt.closed?.should be_false
end end
end end
it "scalar should close statement" do it "scalar should not close statement" do
with_dummy do |db| with_dummy do |db|
stmt = db.prepare "3,4 1,2" stmt = db.prepare "3,4 1,2"
stmt.scalar stmt.scalar
stmt.closed?.should be_true stmt.closed?.should be_false
end end
end end
it "scalar should close statement" do it "scalar should not close statement" do
with_dummy do |db| with_dummy do |db|
stmt = db.prepare "3,4 1,2" stmt = db.prepare "3,4 1,2"
stmt.scalar? stmt.scalar?
stmt.closed?.should be_true stmt.closed?.should be_false
end end
end end
it "exec should close statement" do it "exec should not close statement" do
with_dummy do |db| with_dummy do |db|
stmt = db.prepare "3,4 1,2" stmt = db.prepare "3,4 1,2"
stmt.exec stmt.exec
stmt.closed?.should be_true stmt.closed?.should be_false
end end
end end
end end

View file

@ -15,15 +15,17 @@ module DB
# Also override `#last_insert_id` to allow safe access to the last inserted id through this connection. # Also override `#last_insert_id` to allow safe access to the last inserted id through this connection.
# #
abstract class Connection abstract class Connection
getter connection_string # TODO add IDLE status, for connection ppool management.
def initialize(@connection_string) getter connection_string # TODO Remove
@closed = false @closed = false
def initialize(@connection_string) # TODO Remove
end end
# Closes this connection. # Closes this connection.
def close def close
raise "Connection already closed" if @closed raise "Connection already closed" if @closed # TODO make it no fail if closed
@closed = true @closed = true
perform_close perform_close
end end
@ -44,8 +46,9 @@ module DB
include QueryMethods include QueryMethods
# Returns the last inserted id through this connection. # Returns the last inserted id through this connection.
abstract def last_insert_id : Int64 abstract def last_insert_id : Int64 # TODO move to ExecResult record. plano. with last_rows. eagerly askit.
protected abstract def perform_close
protected abstract def perform_close # TODO do_close
end end
end end

View file

@ -24,8 +24,7 @@ module DB
@connection.close @connection.close
end end
# Returns a `Connection` to the database. # :nodoc:
# Useful if you need to ensure the statements are executed in the connection.
def connection def connection
@connection @connection
end end

View file

@ -64,10 +64,11 @@ module DB
# method to be used as query parameters # method to be used as query parameters
TYPES = [String, Int32, Int64, Float32, Float64, Slice(UInt8)] TYPES = [String, Int32, Int64, Float32, Float64, Slice(UInt8)]
# See `DB::TYPES` in `DB` # See `DB::TYPES` in `DB`. `Any` is a nillable version of the union of all types in `DB::TYPES`
alias Any = String | Int32 | Int64 | Float32 | Float64 | Slice(UInt8) alias Any = Nil | String | Int32 | Int64 | Float32 | Float64 | Slice(UInt8)
# :nodoc: # :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

View file

@ -45,6 +45,8 @@ module DB
prepare(query).scalar(*args) prepare(query).scalar(*args)
end end
# TODO remove scalar? make it always nillable. raise if 0-rows raise +1-rows
# Performs the `query` and returns a single scalar value of type `t`. # Performs the `query` and returns a single scalar value of type `t`.
# `t` must be any of the allowed `DB::Any` types. # `t` must be any of the allowed `DB::Any` types.
# #

View file

@ -13,12 +13,13 @@ module DB
# 4. Override `#column_count`, `#column_name`. # 4. Override `#column_count`, `#column_name`.
# 5. Override `#column_type`. It must return a type in `DB::TYPES`. # 5. Override `#column_type`. It must return a type in `DB::TYPES`.
abstract class ResultSet abstract class ResultSet
# :nodoc:
getter statement getter statement
def initialize(@statement : Statement) def initialize(@statement : Statement)
end end
# TODO add_next_result_set : Bool
# Iterates over all the rows # Iterates over all the rows
def each def each
while move_next while move_next
@ -26,9 +27,24 @@ module DB
end end
end end
# Closes the result set. # Closes this result set.
def close def close
@statement.close return if @closed
@closed = true
do_close
end
# Returns `true` if this result set is closed. See `#close`.
def closed?
@closed
end
# :nodoc:
def finalize
close
end
protected def do_close
end end
# Ensures it executes the query # Ensures it executes the query
@ -65,5 +81,13 @@ module DB
read?({{t}}).not_nil! read?({{t}}).not_nil!
end end
{% end %} {% end %}
# def read_blob
# yield ... io ....
# end
# def read_text
# yield ... io ....
# end
end end
end end

View file

@ -72,35 +72,20 @@ module DB
end end
end end
private def execute : ResultSet
perform(Slice(Any).new(0))
end
private def execute(*args) : ResultSet private def execute(*args) : ResultSet
execute args # TODO better way to do it
end perform(args.to_a.to_unsafe.to_slice(args.size))
private def execute(arg : Slice(UInt8))
begin_parameters
add_parameter 0, arg
perform
end
private def execute(args : Enumerable)
begin_parameters
args.each_with_index do |arg, index|
if arg.is_a?(Hash)
arg.each do |key, value|
add_parameter key.to_s, value
end
else
add_parameter index, arg
end
end
perform
end end
# Closes this statement. # Closes this statement.
def close def close
raise "Statement already closed" if @closed return if @closed # make it work if closed
@closed = true @closed = true
on_close do_close
end end
# Returns `true` if this statement is closed. See `#close`. # Returns `true` if this statement is closed. See `#close`.
@ -108,19 +93,14 @@ module DB
@closed @closed
end end
# # :nodoc: # :nodoc:
# def finalize def finalize
# close unless closed? close
# end
# 0-based positional arguments
protected def begin_parameters
end end
protected abstract def add_parameter(index : Int32, value)
protected abstract def add_parameter(name : String, value)
protected abstract def perform : ResultSet protected abstract def perform(args : Slice(Any)) : ResultSet
protected def on_close
protected def do_close
end end
end end
end end