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
|
@index += 1
|
||||||
@row[@index - 1]
|
@row[@index - 1]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def next_column_index : Int32
|
||||||
|
@index
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class FooValue
|
class FooValue
|
||||||
|
@ -197,7 +201,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, "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
|
w.check
|
||||||
rs.read(BarValue)
|
rs.read(BarValue)
|
||||||
end
|
end
|
||||||
|
@ -210,7 +214,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, "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
|
w.check
|
||||||
rs.read(FooValue)
|
rs.read(FooValue)
|
||||||
end
|
end
|
||||||
|
|
|
@ -190,6 +190,10 @@ class DummyDriver < DB::Driver
|
||||||
return n
|
return n
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def next_column_index : Int32
|
||||||
|
@column_count - @values.not_nil!.size
|
||||||
|
end
|
||||||
|
|
||||||
def read(t : String.class)
|
def read(t : String.class)
|
||||||
read.to_s
|
read.to_s
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,4 +46,17 @@ module DB
|
||||||
# Raised when a scalar query returns no results.
|
# Raised when a scalar query returns no results.
|
||||||
class NoResultsError < Error
|
class NoResultsError < Error
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -69,6 +69,11 @@ module DB
|
||||||
# Reads the next column value
|
# Reads the next column value
|
||||||
abstract def read
|
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
|
# Reads the next columns and maps them to a class
|
||||||
def read(type : DB::Mappable.class)
|
def read(type : DB::Mappable.class)
|
||||||
type.new(self)
|
type.new(self)
|
||||||
|
@ -76,11 +81,18 @@ module DB
|
||||||
|
|
||||||
# Reads the next column value as a **type**
|
# Reads the next column value as a **type**
|
||||||
def read(type : T.class) : T forall T
|
def read(type : T.class) : T forall T
|
||||||
|
col_index = next_column_index
|
||||||
value = read
|
value = read
|
||||||
if value.is_a?(T)
|
if value.is_a?(T)
|
||||||
value
|
value
|
||||||
else
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
56
src/spec.cr
56
src/spec.cr
|
@ -289,6 +289,62 @@ module DB
|
||||||
ages.should eq([10, 20, 30])
|
ages.should eq([10, 20, 30])
|
||||||
end
|
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
|
# describe "transactions" do
|
||||||
it "transactions: can read inside transaction and rollback after" do |db|
|
it "transactions: can read inside transaction and rollback after" do |db|
|
||||||
db.exec sql_create_table_person
|
db.exec sql_create_table_person
|
||||||
|
|
Loading…
Reference in a new issue