diff --git a/spec/std/db/dummy_driver.cr b/spec/std/db/dummy_driver.cr index 13cbbe6..9b9b5d9 100644 --- a/spec/std/db/dummy_driver.cr +++ b/spec/std/db/dummy_driver.cr @@ -4,16 +4,22 @@ class DummyDriver < DB::Driver end class DummyStatement < DB::Statement + property! params + def initialize(driver, @items) super(driver) end protected def add_parameter(index : Int32, value) - raise "not implemented" + params[index] = value end protected def add_parameter(name : String, value) - raise "not implemented" + params[":#{name}"] = value + end + + protected def before_execute + @params = Hash(Int32 | String, DB::Any).new end protected def execute @@ -34,11 +40,24 @@ class DummyDriver < DB::Driver end end - def read?(t : String.class) + private def read? : DB::Any? n = @values.not_nil!.next raise "end of row" if n.is_a?(Iterator::Stop) return nil if n == "NULL" - return n as String + + if n == "?" + return @statement.params[1] + end + + if n.starts_with?(":") + return @statement.params[n] + end + + return n + end + + def read?(t : String.class) + read?.try &.to_s end def read?(t : Int32.class) @@ -50,12 +69,24 @@ class DummyDriver < DB::Driver end def read?(t : Float32.class) - read?(String).try &.to_f23 + read?(String).try &.to_f32 end def read?(t : Float64.class) read?(String).try &.to_f64 end + + def read?(t : Slice(UInt8).class) + value = read? + if value.is_a?(Nil) + value + elsif value.is_a?(String) + ary = value.bytes + Slice.new(ary.to_unsafe, ary.size) + else + value as Slice(UInt8) + end + end end end diff --git a/spec/std/db/dummy_driver_spec.cr b/spec/std/db/dummy_driver_spec.cr index f910301..6ba3994 100644 --- a/spec/std/db/dummy_driver_spec.cr +++ b/spec/std/db/dummy_driver_spec.cr @@ -65,6 +65,15 @@ describe DummyDriver do result_set.read(Int64).should eq(2i64) end + it "should enumerate blob fields" do + result_set = get_dummy.prepare("az,AZ").exec + result_set.move_next + ary = [97u8, 122u8] + result_set.read(Slice(UInt8)).should eq(Slice.new(ary.to_unsafe, ary.size)) + ary = [65u8, 90u8] + result_set.read(Slice(UInt8)).should eq(Slice.new(ary.to_unsafe, ary.size)) + end + it "should enumerate records using each" do nums = [] of Int32 result_set = get_dummy.prepare("3,4 1,2").exec @@ -75,5 +84,37 @@ describe DummyDriver do nums.should eq([3, 4, 1, 2]) end + + {% for value in [1, 1_i64, "hello", 1.5, 1.5_f32] %} + it "should set arguments for {{value.id}}" do + result_set = get_dummy.exec "?", {{value}} + result_set.move_next.should be_true + result_set.read(typeof({{value}})).should eq({{value}}) + end + + it "should set arguments by symbol for {{value.id}}" do + result_set = get_dummy.exec ":once :twice", {once: {{value}}, twice: {{value + value}} } + result_set.move_next.should be_true + result_set.read(typeof({{value}})).should eq({{value}}) + result_set.move_next.should be_true + result_set.read(typeof({{value}})).should eq({{value + value}}) + end + + it "should set arguments by string for {{value.id}}" do + result_set = get_dummy.exec ":once :twice", {"once": {{value}}, "twice": {{value + value}} } + result_set.move_next.should be_true + result_set.read(typeof({{value}})).should eq({{value}}) + result_set.move_next.should be_true + result_set.read(typeof({{value}})).should eq({{value + value}}) + end + {% end %} + + it "executes and selects blob" do + ary = UInt8[0x53, 0x51, 0x4C] + slice = Slice.new(ary.to_unsafe, ary.size) + result_set = get_dummy.exec "?", slice + result_set.move_next + result_set.read(Slice(UInt8)).to_a.should eq(ary) + end end end diff --git a/src/db/db.cr b/src/db/db.cr index 77cea9d..52b0f9a 100644 --- a/src/db/db.cr +++ b/src/db/db.cr @@ -1,5 +1,6 @@ module DB - TYPES = [String, Int32, Int64, Float32, Float64] + TYPES = [String, Int32, Int64, Float32, Float64, Slice(UInt8)] + alias Any = String | Int32 | Int64 | Float32 | Float64 | Slice(UInt8) def self.driver_class(name) # : Driver.class @@drivers.not_nil![name] diff --git a/src/db/statement.cr b/src/db/statement.cr index c9c4e40..30f2440 100644 --- a/src/db/statement.cr +++ b/src/db/statement.cr @@ -9,6 +9,12 @@ module DB exec args end + def exec(arg : Slice(UInt8)) + before_execute + add_parameter 1, arg + execute + end + def exec(args : Enumerable) before_execute args.each_with_index(1) do |arg, index|