mirror of
https://gitea.invidious.io/iv-org/shard-crystal-db.git
synced 2024-08-15 00:53:32 +00:00
Require ResultSet to just implement read
, optionally implementing read(T.class)
. Fixes #5
This commit is contained in:
parent
038ffef33a
commit
9c88f718e8
8 changed files with 329 additions and 96 deletions
|
@ -16,18 +16,7 @@ module GenericResultSet
|
||||||
index.to_s
|
index.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def column_type(index : Int32)
|
def read
|
||||||
@row[index].class
|
|
||||||
end
|
|
||||||
|
|
||||||
{% for t in DB::TYPES %}
|
|
||||||
# Reads the next column as a nillable {{t}}.
|
|
||||||
def read?(t : {{t}}.class) : {{t}}?
|
|
||||||
read_and_move_next_column as {{t}}?
|
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
def read_and_move_next_column
|
|
||||||
@index += 1
|
@index += 1
|
||||||
@row[@index - 1]
|
@row[@index - 1]
|
||||||
end
|
end
|
||||||
|
@ -89,10 +78,6 @@ class FooDriver < DB::Driver
|
||||||
def initialize(statement, @row : Array(FooDriver::Any))
|
def initialize(statement, @row : Array(FooDriver::Any))
|
||||||
super(statement)
|
super(statement)
|
||||||
end
|
end
|
||||||
|
|
||||||
def read?(t : FooValue.class) : FooValue?
|
|
||||||
read_and_move_next_column.as(FooValue?)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -152,10 +137,6 @@ class BarDriver < DB::Driver
|
||||||
def initialize(statement, @row : Array(BarDriver::Any))
|
def initialize(statement, @row : Array(BarDriver::Any))
|
||||||
super(statement)
|
super(statement)
|
||||||
end
|
end
|
||||||
|
|
||||||
def read?(t : BarValue.class) : BarValue?
|
|
||||||
read_and_move_next_column.as(BarValue?)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -174,8 +155,8 @@ describe DB do
|
||||||
db.query "query" do |rs|
|
db.query "query" do |rs|
|
||||||
w.check
|
w.check
|
||||||
rs.move_next
|
rs.move_next
|
||||||
rs.read?(Int32).should eq(1)
|
rs.read(Int32).should eq(1)
|
||||||
rs.read?(String).should eq("string")
|
rs.read(String).should eq("string")
|
||||||
rs.read(FooValue).value.should eq(3)
|
rs.read(FooValue).value.should eq(3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -188,8 +169,8 @@ describe DB do
|
||||||
w.check
|
w.check
|
||||||
rs.move_next
|
rs.move_next
|
||||||
rs.read(BarValue).value.should eq(4)
|
rs.read(BarValue).value.should eq(4)
|
||||||
rs.read?(String).should eq("lorem")
|
rs.read(String).should eq("lorem")
|
||||||
rs.read?(Float64).should eq(1.0)
|
rs.read(Float64).should eq(1.0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -208,7 +189,7 @@ describe DB do
|
||||||
FooDriver.fake_row = [1] of FooDriver::Any
|
FooDriver.fake_row = [1] of FooDriver::Any
|
||||||
db.query "query" do |rs|
|
db.query "query" do |rs|
|
||||||
rs.move_next
|
rs.move_next
|
||||||
expect_raises Exception, "read?(t : BarValue) is not implemented in FooDriver::FooResultSet" do
|
expect_raises(TypeCastError) do
|
||||||
w.check
|
w.check
|
||||||
rs.read(BarValue)
|
rs.read(BarValue)
|
||||||
end
|
end
|
||||||
|
@ -221,7 +202,7 @@ describe DB do
|
||||||
BarDriver.fake_row = [1] of BarDriver::Any
|
BarDriver.fake_row = [1] of BarDriver::Any
|
||||||
db.query "query" do |rs|
|
db.query "query" do |rs|
|
||||||
rs.move_next
|
rs.move_next
|
||||||
expect_raises Exception, "read?(t : FooValue) is not implemented in BarDriver::BarResultSet" do
|
expect_raises(TypeCastError) do
|
||||||
w.check
|
w.check
|
||||||
rs.read(FooValue)
|
rs.read(FooValue)
|
||||||
end
|
end
|
||||||
|
|
|
@ -73,12 +73,10 @@ class DummyDriver < DB::Driver
|
||||||
end
|
end
|
||||||
|
|
||||||
class DummyResultSet < DB::ResultSet
|
class DummyResultSet < DB::ResultSet
|
||||||
@@next_column_type = String
|
|
||||||
@top_values : Array(Array(String))
|
@top_values : Array(Array(String))
|
||||||
@values : Array(String)?
|
@values : Array(String)?
|
||||||
|
|
||||||
@@last_result_set : self?
|
@@last_result_set : self?
|
||||||
@@next_column_type : Nil.class | String.class | Int32.class | Int64.class | Float32.class | Float64.class | Bytes.class
|
|
||||||
|
|
||||||
def initialize(statement, query)
|
def initialize(statement, query)
|
||||||
super(statement)
|
super(statement)
|
||||||
|
@ -108,15 +106,7 @@ class DummyDriver < DB::Driver
|
||||||
"c#{index}"
|
"c#{index}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def column_type(index : Int32)
|
def read
|
||||||
@@next_column_type
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.next_column_type=(value)
|
|
||||||
@@next_column_type = value
|
|
||||||
end
|
|
||||||
|
|
||||||
private def read? : DB::Any?
|
|
||||||
n = @values.not_nil!.shift?
|
n = @values.not_nil!.shift?
|
||||||
raise "end of row" if n.is_a?(Nil)
|
raise "end of row" if n.is_a?(Nil)
|
||||||
return nil if n == "NULL"
|
return nil if n == "NULL"
|
||||||
|
@ -128,38 +118,36 @@ class DummyDriver < DB::Driver
|
||||||
return n
|
return n
|
||||||
end
|
end
|
||||||
|
|
||||||
def read?(t : Nil.class)
|
def read(t : String.class)
|
||||||
read?.as(Nil)
|
read.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def read?(t : String.class)
|
def read(t : String?.class)
|
||||||
read?.try &.to_s
|
read.try &.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def read?(t : Int32.class)
|
def read(t : Int32.class)
|
||||||
read?(String).try &.to_i32
|
read(String).to_i32
|
||||||
end
|
end
|
||||||
|
|
||||||
def read?(t : Int64.class)
|
def read(t : Int64.class)
|
||||||
read?(String).try &.to_i64
|
read(String).to_i64
|
||||||
end
|
end
|
||||||
|
|
||||||
def read?(t : Float32.class)
|
def read(t : Float32.class)
|
||||||
read?(String).try &.to_f32
|
read(String).to_f32
|
||||||
end
|
end
|
||||||
|
|
||||||
def read?(t : Float64.class)
|
def read(t : Float64.class)
|
||||||
read?(String).try &.to_f64
|
read(String).to_f64
|
||||||
end
|
end
|
||||||
|
|
||||||
def read?(t : Bytes.class)
|
def read(t : Bytes.class)
|
||||||
value = read?
|
case value = read
|
||||||
if value.is_a?(Nil)
|
when String
|
||||||
value
|
|
||||||
elsif value.is_a?(String)
|
|
||||||
ary = value.bytes
|
ary = value.bytes
|
||||||
Slice.new(ary.to_unsafe, ary.size)
|
Slice.new(ary.to_unsafe, ary.size)
|
||||||
elsif value.is_a?(Bytes)
|
when Bytes
|
||||||
value
|
value
|
||||||
else
|
else
|
||||||
raise "#{value} is not convertible to Bytes"
|
raise "#{value} is not convertible to Bytes"
|
||||||
|
|
|
@ -74,11 +74,11 @@ describe DummyDriver do
|
||||||
with_dummy do |db|
|
with_dummy do |db|
|
||||||
db.query "a,NULL 1,NULL" do |rs|
|
db.query "a,NULL 1,NULL" do |rs|
|
||||||
rs.move_next
|
rs.move_next
|
||||||
rs.read?(String).should eq("a")
|
rs.read(String).should eq("a")
|
||||||
rs.read?(String).should be_nil
|
rs.read(String | Nil).should be_nil
|
||||||
rs.move_next
|
rs.move_next
|
||||||
rs.read?(Int64).should eq(1)
|
rs.read(Int64).should eq(1)
|
||||||
rs.read?(Int64).should be_nil
|
rs.read(Int64 | Nil).should be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -96,6 +96,116 @@ describe DummyDriver do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "query one" do
|
||||||
|
it "queries" do
|
||||||
|
with_dummy do |db|
|
||||||
|
db.query_one("3,4", &.read(Int64, Int64)).should eq({3i64, 4i64})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises if more than one row" do
|
||||||
|
with_dummy do |db|
|
||||||
|
expect_raises(DB::Error, "more than one row") do
|
||||||
|
db.query_one("3,4 5,6") { }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises if no rows" do
|
||||||
|
with_dummy do |db|
|
||||||
|
expect_raises(DB::Error, "no rows") do
|
||||||
|
db.query_one("") { }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "with as" do
|
||||||
|
with_dummy do |db|
|
||||||
|
db.query_one("3,4", as: {Int64, Int64}).should eq({3i64, 4i64})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "with as, just one" do
|
||||||
|
with_dummy do |db|
|
||||||
|
db.query_one("3", as: Int64).should eq(3i64)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "query one?" do
|
||||||
|
it "queries" do
|
||||||
|
with_dummy do |db|
|
||||||
|
value = db.query_one?("3,4", &.read(Int64, Int64))
|
||||||
|
value.should eq({3i64, 4i64})
|
||||||
|
value.should be_a(Tuple(Int64, Int64)?)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises if more than one row" do
|
||||||
|
with_dummy do |db|
|
||||||
|
expect_raises(DB::Error, "more than one row") do
|
||||||
|
db.query_one?("3,4 5,6") { }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns nil if no rows" do
|
||||||
|
with_dummy do |db|
|
||||||
|
db.query_one?("") { fail("block shouldn't be invoked") }.should be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "with as" do
|
||||||
|
with_dummy do |db|
|
||||||
|
value = db.query_one?("3,4", as: {Int64, Int64})
|
||||||
|
value.should be_a(Tuple(Int64, Int64)?)
|
||||||
|
value.should eq({3i64, 4i64})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "with as, just one" do
|
||||||
|
with_dummy do |db|
|
||||||
|
value = db.query_one?("3", as: Int64)
|
||||||
|
value.should be_a(Int64?)
|
||||||
|
value.should eq(3i64)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "query all" do
|
||||||
|
it "queries" do
|
||||||
|
with_dummy do |db|
|
||||||
|
ary = db.query_all "3,4 1,2", &.read(Int64, Int64)
|
||||||
|
ary.should eq([{3, 4}, {1, 2}])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "queries with as" do
|
||||||
|
with_dummy do |db|
|
||||||
|
ary = db.query_all "3,4 1,2", as: {Int64, Int64}
|
||||||
|
ary.should eq([{3, 4}, {1, 2}])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "queries with as, just one" do
|
||||||
|
with_dummy do |db|
|
||||||
|
ary = db.query_all "3 1", as: Int64
|
||||||
|
ary.should eq([3, 1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "reads multiple values" do
|
||||||
|
with_dummy do |db|
|
||||||
|
db.query "3,4 1,2" do |rs|
|
||||||
|
rs.move_next
|
||||||
|
rs.read(Int64, Int64).should eq({3i64, 4i64})
|
||||||
|
rs.move_next
|
||||||
|
rs.read(Int64, Int64).should eq({1i64, 2i64})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "should enumerate blob fields" do
|
it "should enumerate blob fields" do
|
||||||
with_dummy do |db|
|
with_dummy do |db|
|
||||||
db.query("az,AZ") do |rs|
|
db.query("az,AZ") do |rs|
|
||||||
|
@ -110,22 +220,13 @@ describe DummyDriver do
|
||||||
|
|
||||||
it "should get Nil scalars" do
|
it "should get Nil scalars" do
|
||||||
with_dummy do |db|
|
with_dummy do |db|
|
||||||
DummyDriver::DummyResultSet.next_column_type = Nil
|
|
||||||
db.scalar("NULL").should be_nil
|
db.scalar("NULL").should be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
{% for value in [1, 1_i64, "hello", 1.5, 1.5_f32] %}
|
{% for value in [1, 1_i64, "hello", 1.5, 1.5_f32] %}
|
||||||
it "numeric scalars of type of {{value.id}} should return value or nil" do
|
|
||||||
with_dummy do |db|
|
|
||||||
DummyDriver::DummyResultSet.next_column_type = typeof({{value}})
|
|
||||||
db.scalar("#{{{value}}}").should eq({{value}})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should set positional arguments for {{value.id}}" do
|
it "should set positional arguments for {{value.id}}" do
|
||||||
with_dummy do |db|
|
with_dummy do |db|
|
||||||
DummyDriver::DummyResultSet.next_column_type = typeof({{value}})
|
|
||||||
db.scalar("?", {{value}}).should eq({{value}})
|
db.scalar("?", {{value}}).should eq({{value}})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -135,7 +236,6 @@ describe DummyDriver do
|
||||||
with_dummy do |db|
|
with_dummy do |db|
|
||||||
ary = UInt8[0x53, 0x51, 0x4C]
|
ary = UInt8[0x53, 0x51, 0x4C]
|
||||||
slice = Bytes.new(ary.to_unsafe, ary.size)
|
slice = Bytes.new(ary.to_unsafe, ary.size)
|
||||||
DummyDriver::DummyResultSet.next_column_type = typeof(slice)
|
|
||||||
(db.scalar("?", slice).as(Bytes)).to_a.should eq(ary)
|
(db.scalar("?", slice).as(Bytes)).to_a.should eq(ary)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -126,3 +126,4 @@ require "./db/driver"
|
||||||
require "./db/connection"
|
require "./db/connection"
|
||||||
require "./db/statement"
|
require "./db/statement"
|
||||||
require "./db/result_set"
|
require "./db/result_set"
|
||||||
|
require "./db/error"
|
||||||
|
|
4
src/db/error.cr
Normal file
4
src/db/error.cr
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
module DB
|
||||||
|
class Error < Exception
|
||||||
|
end
|
||||||
|
end
|
|
@ -21,27 +21,190 @@ module DB
|
||||||
# :nodoc:
|
# :nodoc:
|
||||||
abstract def prepare(query) : Statement
|
abstract def prepare(query) : Statement
|
||||||
|
|
||||||
# Returns a `ResultSet` for the `query`.
|
# Executes a *query* and returns a `ResultSet` with the results.
|
||||||
# The `ResultSet` must be closed manually.
|
# The `ResultSet` must be closed manually.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# result = db.query "select name from contacts where id = ?", 10
|
||||||
|
# begin
|
||||||
|
# if result.move_next
|
||||||
|
# id = result.read(Int32)
|
||||||
|
# end
|
||||||
|
# ensure
|
||||||
|
# result.close
|
||||||
|
# end
|
||||||
|
# ```
|
||||||
def query(query, *args)
|
def query(query, *args)
|
||||||
prepare query, &.query(*args)
|
prepare query, &.query(*args)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Yields a `ResultSet` for the `query`.
|
# Executes a *query* and yields a `ResultSet` with the results.
|
||||||
# The `ResultSet` is closed automatically.
|
# The `ResultSet` is closed automatically.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# db.query("select name from contacts where age > ?", 18) do |rs|
|
||||||
|
# rs.each do
|
||||||
|
# name = rs.read(String)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# ```
|
||||||
def query(query, *args)
|
def query(query, *args)
|
||||||
# CHECK prepare(query).query(*args, &block)
|
# CHECK prepare(query).query(*args, &block)
|
||||||
rs = query(query, *args)
|
rs = query(query, *args)
|
||||||
yield rs ensure rs.close
|
yield rs ensure rs.close
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Executes a *query* that expects a single row and yields a `ResultSet`
|
||||||
|
# positioned at that first row.
|
||||||
|
#
|
||||||
|
# The given block must not invoke `move_next` on the yielded result set.
|
||||||
|
#
|
||||||
|
# Raises `DB::Error` if there were no rows, or if there were more than one row.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# name = db.query_one "select name from contacts where id = ?", 18, &.read(String)
|
||||||
|
# ```
|
||||||
|
def query_one(query, *args, &block : ResultSet -> U) : U
|
||||||
|
query(query, *args) do |rs|
|
||||||
|
raise DB::Error.new("no rows") unless rs.move_next
|
||||||
|
|
||||||
|
value = yield rs
|
||||||
|
raise DB::Error.new("more than one row") if rs.move_next
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executes a *query* that expects a single row and returns it
|
||||||
|
# as a tuple of the given *types*.
|
||||||
|
#
|
||||||
|
# Raises `DB::Error` if there were no rows, or if there were more than one row.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# db.query_one "select name, age from contacts where id = ?", 1, as: {String, Int32}
|
||||||
|
# ```
|
||||||
|
def query_one(query, *args, as types : Tuple)
|
||||||
|
query_one(query, *args) do |rs|
|
||||||
|
rs.read(*types)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executes a *query* that expects a single row
|
||||||
|
# and returns the first column's value as the given *type*.
|
||||||
|
#
|
||||||
|
# Raises `DB::Error` if there were no rows, or if there were more than one row.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# db.query_one "select name from contacts where id = ?", 1, as: String
|
||||||
|
# ```
|
||||||
|
def query_one(query, *args, as type : Class)
|
||||||
|
query_one(query, *args) do |rs|
|
||||||
|
rs.read(type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executes a *query* that expects at most a single row and yields a `ResultSet`
|
||||||
|
# positioned at that first row.
|
||||||
|
#
|
||||||
|
# Returns `nil`, not invoking the block, if there were no rows.
|
||||||
|
#
|
||||||
|
# Raises `DB::Error` if there were more than one row
|
||||||
|
# (this ends up invoking the block once).
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# name = db.query_one? "select name from contacts where id = ?", 18, &.read(String)
|
||||||
|
# typeof(name) # => String | Nil
|
||||||
|
# ```
|
||||||
|
def query_one?(query, *args, &block : ResultSet -> U) : U?
|
||||||
|
query(query, *args) do |rs|
|
||||||
|
return nil unless rs.move_next
|
||||||
|
|
||||||
|
value = yield rs
|
||||||
|
raise DB::Error.new("more than one row") if rs.move_next
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executes a *query* that expects a single row and returns it
|
||||||
|
# as a tuple of the given *types*.
|
||||||
|
#
|
||||||
|
# Returns `nil` if there were no rows.
|
||||||
|
#
|
||||||
|
# Raises `DB::Error` if there were more than one row.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# result = db.query_one? "select name, age from contacts where id = ?", 1, as: {String, Int32}
|
||||||
|
# typeof(result) # => Tuple(String, Int32) | Nil
|
||||||
|
# ```
|
||||||
|
def query_one?(query, *args, as types : Tuple)
|
||||||
|
query_one?(query, *args) do |rs|
|
||||||
|
rs.read(*types)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executes a *query* that expects a single row
|
||||||
|
# and returns the first column's value as the given *type*.
|
||||||
|
#
|
||||||
|
# Returns `nil` if there were no rows.
|
||||||
|
#
|
||||||
|
# Raises `DB::Error` if there were more than one row.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# name = db.query_one? "select name from contacts where id = ?", 1, as: String
|
||||||
|
# typeof(name) # => String?
|
||||||
|
# ```
|
||||||
|
def query_one?(query, *args, as type : Class)
|
||||||
|
query_one?(query, *args) do |rs|
|
||||||
|
rs.read(type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executes a *query* and yield a `ResultSet` positioned at the beginning
|
||||||
|
# of each row, returning an array of the values of the blocks.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# names = db.query_all "select name from contacts", &.read(String)
|
||||||
|
# ```
|
||||||
|
def query_all(query, *args, &block : ResultSet -> U) : Array(U)
|
||||||
|
ary = [] of U
|
||||||
|
query(query, *args) do |rs|
|
||||||
|
rs.each do
|
||||||
|
ary.push(yield rs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executes a *query* and returns an array where each row is
|
||||||
|
# read as a tuple of the given *types*.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# contacts = db.query_all "select name, age from contactas", as: {String, Int32}
|
||||||
|
# ```
|
||||||
|
def query_all(query, *args, as types : Tuple)
|
||||||
|
query_all(query, *args) do |rs|
|
||||||
|
rs.read(*types)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executes a *query* and returns an array where there first
|
||||||
|
# column's value of each row is read as the given *type*.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# names = db.query_all "select name from contactas", as: String
|
||||||
|
# ```
|
||||||
|
def query_all(query, *args, as type : Class)
|
||||||
|
query_all(query, *args) do |rs|
|
||||||
|
rs.read(type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Performs the `query` and returns an `ExecResult`
|
# Performs the `query` and returns an `ExecResult`
|
||||||
def exec(query, *args)
|
def exec(query, *args)
|
||||||
prepare query, &.exec(*args)
|
prepare query, &.exec(*args)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Performs the `query` and returns a single scalar `DB::Any` value
|
# Performs the `query` and returns a single scalar value
|
||||||
# puts db.scalar("SELECT MAX(name)") as String # => (a String)
|
# puts db.scalar("SELECT MAX(name)").as(String) # => (a String)
|
||||||
def scalar(query, *args)
|
def scalar(query, *args)
|
||||||
prepare query, &.scalar(*args)
|
prepare query, &.scalar(*args)
|
||||||
end
|
end
|
||||||
|
@ -55,7 +218,5 @@ module DB
|
||||||
raise ex
|
raise ex
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO add query_row
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,10 +16,9 @@ module DB
|
||||||
# ### Note to implementors
|
# ### Note to implementors
|
||||||
#
|
#
|
||||||
# 1. Override `#move_next` to move to the next row.
|
# 1. Override `#move_next` to move to the next row.
|
||||||
# 2. Override `#read?(t)` for all `t` in `DB::TYPES` and any other types the driver should handle.
|
# 2. Override `#read` returning the next value in the row.
|
||||||
# 3. (Optional) Override `#read(t)` for all `t` in `DB::TYPES` and any other.
|
# 3. (Optional) Override `#read(t)` for some types `t` for which custom logic other than a simple cast is needed.
|
||||||
# 4. Override `#column_count`, `#column_name`.
|
# 4. Override `#column_count`, `#column_name`.
|
||||||
# 5. Override `#column_type`. It must return a type in `DB::TYPES`.
|
|
||||||
abstract class ResultSet
|
abstract class ResultSet
|
||||||
include Disposable
|
include Disposable
|
||||||
|
|
||||||
|
@ -55,29 +54,28 @@ module DB
|
||||||
# Returns the name of the column in `index` 0-based position.
|
# Returns the name of the column in `index` 0-based position.
|
||||||
abstract def column_name(index : Int32) : String
|
abstract def column_name(index : Int32) : String
|
||||||
|
|
||||||
# Returns the type of the column in `index` 0-based position.
|
# Reads the next column value
|
||||||
# The result is one of `DB::TYPES`.
|
abstract def read
|
||||||
abstract def column_type(index : Int32)
|
|
||||||
|
|
||||||
def read(t)
|
# Reads the next column value as a **type**
|
||||||
read?(t).not_nil!
|
def read(type : T.class) : T
|
||||||
|
read.as(T)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reads the next column as a Nil.
|
# Reads the next columns and returns a tuple of the values.
|
||||||
def read(t : Nil.class) : Nil
|
def read(*types : Class)
|
||||||
read?(Nil)
|
internal_read(*types)
|
||||||
end
|
end
|
||||||
|
|
||||||
def read?(t)
|
private def internal_read(*types : *T)
|
||||||
raise "read?(t : #{t}) is not implemented in #{self.class}"
|
{% begin %}
|
||||||
end
|
Tuple.new(
|
||||||
|
{% for type in T %}
|
||||||
# list datatypes that must be supported form the driver
|
read({{type.instance}}),
|
||||||
# users will call read(String) or read?(String) for nillables
|
|
||||||
{% for t in DB::TYPES %}
|
|
||||||
# Reads the next column as a nillable {{t}}.
|
|
||||||
abstract def read?(t : {{t}}.class) : {{t}}?
|
|
||||||
{% end %}
|
{% end %}
|
||||||
|
)
|
||||||
|
{% end %}
|
||||||
|
end
|
||||||
|
|
||||||
# def read_blob
|
# def read_blob
|
||||||
# yield ... io ....
|
# yield ... io ....
|
||||||
|
|
|
@ -45,7 +45,7 @@ module DB
|
||||||
def scalar(*args)
|
def scalar(*args)
|
||||||
query(*args) do |rs|
|
query(*args) do |rs|
|
||||||
rs.each do
|
rs.each do
|
||||||
return rs.read?(rs.column_type(0))
|
return rs.read
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue