mirror of
https://gitea.invidious.io/iv-org/shard-crystal-db.git
synced 2024-08-15 00:53:32 +00:00
Raise a specific class error instead of string literal (#156)
* Raise a specific class error instead of string literal when the type returned doesn't match the type expected. Allows for drivers to catch the specific error. * Add ResultSet#next_column_index * Add shared specs for next_column_index * Add properties to ColumnTypeMismatchError * Add shared specs for ColumnTypeMismatchError * Fix specs Co-authored-by: Brian J. Cardiff <bcardiff@gmail.com>
This commit is contained in:
parent
a25f33611c
commit
b1299fcada
5 changed files with 92 additions and 3 deletions
|
@ -20,6 +20,10 @@ module GenericResultSet
|
|||
@index += 1
|
||||
@row[@index - 1]
|
||||
end
|
||||
|
||||
def next_column_index : Int32
|
||||
@index
|
||||
end
|
||||
end
|
||||
|
||||
class FooValue
|
||||
|
@ -197,7 +201,7 @@ describe DB do
|
|||
FooDriver.fake_row = [1] of FooDriver::Any
|
||||
db.query "query" do |rs|
|
||||
rs.move_next
|
||||
expect_raises(Exception, "FooResultSet#read returned a Int32. A BarValue was expected.") do
|
||||
expect_raises(DB::ColumnTypeMismatchError, "In FooDriver::FooResultSet#read the column 0 returned a Int32 but a BarValue was expected.") do
|
||||
w.check
|
||||
rs.read(BarValue)
|
||||
end
|
||||
|
@ -210,7 +214,7 @@ describe DB do
|
|||
BarDriver.fake_row = [1] of BarDriver::Any
|
||||
db.query "query" do |rs|
|
||||
rs.move_next
|
||||
expect_raises(Exception, "BarResultSet#read returned a Int32. A FooValue was expected.") do
|
||||
expect_raises(DB::ColumnTypeMismatchError, "In BarDriver::BarResultSet#read the column 0 returned a Int32 but a FooValue was expected.") do
|
||||
w.check
|
||||
rs.read(FooValue)
|
||||
end
|
||||
|
|
|
@ -190,6 +190,10 @@ class DummyDriver < DB::Driver
|
|||
return n
|
||||
end
|
||||
|
||||
def next_column_index : Int32
|
||||
@column_count - @values.not_nil!.size
|
||||
end
|
||||
|
||||
def read(t : String.class)
|
||||
read.to_s
|
||||
end
|
||||
|
|
|
@ -46,4 +46,17 @@ module DB
|
|||
# Raised when a scalar query returns no results.
|
||||
class NoResultsError < Error
|
||||
end
|
||||
|
||||
# Raised when the type returned for the column value
|
||||
# does not match the type expected.
|
||||
class ColumnTypeMismatchError < Error
|
||||
getter column_index : Int32
|
||||
getter column_name : String
|
||||
getter column_type : String
|
||||
getter expected_type : String
|
||||
|
||||
def initialize(*, context : String, @column_index : Int32, @column_name : String, @column_type : String, @expected_type : String)
|
||||
super("In #{context} the column #{column_name} returned a #{column_type} but a #{expected_type} was expected.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,6 +69,11 @@ module DB
|
|||
# Reads the next column value
|
||||
abstract def read
|
||||
|
||||
# Returns the column index that corresponds to the next `#read`.
|
||||
#
|
||||
# If the last column of the current row has been read, it must return `#column_count`.
|
||||
abstract def next_column_index : Int32
|
||||
|
||||
# Reads the next columns and maps them to a class
|
||||
def read(type : DB::Mappable.class)
|
||||
type.new(self)
|
||||
|
@ -76,11 +81,18 @@ module DB
|
|||
|
||||
# Reads the next column value as a **type**
|
||||
def read(type : T.class) : T forall T
|
||||
col_index = next_column_index
|
||||
value = read
|
||||
if value.is_a?(T)
|
||||
value
|
||||
else
|
||||
raise "#{self.class}#read returned a #{value.class}. A #{T} was expected."
|
||||
raise DB::ColumnTypeMismatchError.new(
|
||||
context: "#{self.class}#read",
|
||||
column_index: col_index,
|
||||
column_name: column_name(col_index),
|
||||
column_type: value.class.to_s,
|
||||
expected_type: T.to_s
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
56
src/spec.cr
56
src/spec.cr
|
@ -289,6 +289,62 @@ module DB
|
|||
ages.should eq([10, 20, 30])
|
||||
end
|
||||
|
||||
it "next_column_index" do |db|
|
||||
db.exec sql_create_table_person
|
||||
db.exec sql_insert_person, "foo", 10
|
||||
db.exec sql_insert_person, "bar", 20
|
||||
|
||||
db.query sql_select_person do |rs|
|
||||
rs.move_next
|
||||
rs.next_column_index.should eq(0)
|
||||
rs.read(String)
|
||||
rs.next_column_index.should eq(1)
|
||||
rs.read(Int32)
|
||||
rs.next_column_index.should eq(2)
|
||||
|
||||
rs.move_next
|
||||
rs.next_column_index.should eq(0)
|
||||
rs.read(String)
|
||||
rs.next_column_index.should eq(1)
|
||||
rs.read(Int32)
|
||||
rs.next_column_index.should eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
it "next_column_index when ColumnTypeMismatchError" do |db|
|
||||
db.exec sql_create_table_person
|
||||
db.exec sql_insert_person, "foo", 10
|
||||
db.exec sql_insert_person, "bar", 20
|
||||
|
||||
db.query sql_select_person do |rs|
|
||||
rs.move_next
|
||||
rs.next_column_index.should eq(0)
|
||||
ex = expect_raises(ColumnTypeMismatchError) { rs.read(Int32) }
|
||||
ex.column_index.should eq(0)
|
||||
ex.column_name.should eq("name")
|
||||
# NOTE: sqlite currently returns Int64 due to how Int32 is implemented
|
||||
ex.column_type.should match(/String/)
|
||||
# NOTE: pg currently returns Slice(UInt8) | String due to how String is implemented
|
||||
ex.expected_type.should match(/Int/)
|
||||
rs.next_column_index.should eq(1)
|
||||
ex = expect_raises(ColumnTypeMismatchError) { rs.read(String) }
|
||||
ex.column_index.should eq(1)
|
||||
ex.column_name.should eq("age")
|
||||
# NOTE: sqlite returns Int64
|
||||
ex.column_type.should match(/Int/)
|
||||
# NOTE: pg currently returns Slice(UInt8) | String due to how String is implemented
|
||||
ex.expected_type.should match(/String/)
|
||||
rs.next_column_index.should eq(2)
|
||||
|
||||
rs.move_next
|
||||
rs.next_column_index.should eq(0)
|
||||
expect_raises(ColumnTypeMismatchError) { rs.read(Int32) }
|
||||
rs.next_column_index.should eq(1)
|
||||
expect_raises(ColumnTypeMismatchError) { rs.read(String) }
|
||||
rs.next_column_index.should eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
# describe "transactions" do
|
||||
it "transactions: can read inside transaction and rollback after" do |db|
|
||||
db.exec sql_create_table_person
|
||||
|
|
Loading…
Reference in a new issue