2016-01-30 22:46:43 +00:00
|
|
|
require "spec"
|
2016-02-26 01:37:10 +00:00
|
|
|
require "../src/db"
|
2016-01-30 22:46:43 +00:00
|
|
|
|
2016-01-28 23:31:35 +00:00
|
|
|
class DummyDriver < DB::Driver
|
2017-03-20 18:08:30 +00:00
|
|
|
def build_connection(context : DB::ConnectionContext) : DB::Connection
|
|
|
|
DummyConnection.new(context)
|
2016-01-30 22:46:43 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
class DummyConnection < DB::Connection
|
2017-03-20 18:08:30 +00:00
|
|
|
def initialize(context)
|
|
|
|
super(context)
|
2016-08-31 20:32:01 +00:00
|
|
|
@connected = true
|
2016-02-03 03:36:50 +00:00
|
|
|
@@connections ||= [] of DummyConnection
|
|
|
|
@@connections.not_nil! << self
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.connections
|
|
|
|
@@connections.not_nil!
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.clear_connections
|
|
|
|
@@connections.try &.clear
|
2016-02-02 02:02:04 +00:00
|
|
|
end
|
|
|
|
|
2019-08-02 14:54:52 +00:00
|
|
|
def build_prepared_statement(query) : DB::Statement
|
2016-11-29 23:14:07 +00:00
|
|
|
DummyStatement.new(self, query, true)
|
|
|
|
end
|
|
|
|
|
2019-08-02 14:54:52 +00:00
|
|
|
def build_unprepared_statement(query) : DB::Statement
|
2016-11-29 23:14:07 +00:00
|
|
|
DummyStatement.new(self, query, false)
|
2016-01-30 22:46:43 +00:00
|
|
|
end
|
|
|
|
|
2016-01-31 17:01:52 +00:00
|
|
|
def last_insert_id : Int64
|
|
|
|
0
|
|
|
|
end
|
|
|
|
|
2016-08-31 20:32:01 +00:00
|
|
|
def check
|
|
|
|
raise DB::ConnectionLost.new(self) unless @connected
|
|
|
|
end
|
|
|
|
|
|
|
|
def disconnect!
|
|
|
|
@connected = false
|
|
|
|
end
|
|
|
|
|
2016-11-16 02:46:11 +00:00
|
|
|
def create_transaction
|
|
|
|
DummyTransaction.new(self)
|
|
|
|
end
|
|
|
|
|
2016-02-03 20:10:03 +00:00
|
|
|
protected def do_close
|
2016-02-03 21:46:37 +00:00
|
|
|
super
|
2016-01-30 22:46:43 +00:00
|
|
|
end
|
2016-01-28 23:31:35 +00:00
|
|
|
end
|
|
|
|
|
2016-11-16 02:46:11 +00:00
|
|
|
class DummyTransaction < DB::TopLevelTransaction
|
|
|
|
getter committed = false
|
|
|
|
getter rolledback = false
|
|
|
|
|
|
|
|
def initialize(connection)
|
|
|
|
super(connection)
|
|
|
|
end
|
|
|
|
|
|
|
|
def commit
|
|
|
|
super
|
|
|
|
@committed = true
|
|
|
|
end
|
|
|
|
|
|
|
|
def rollback
|
|
|
|
super
|
|
|
|
@rolledback = true
|
|
|
|
end
|
2016-12-13 19:15:25 +00:00
|
|
|
|
|
|
|
protected def create_save_point_transaction(parent, savepoint_name : String)
|
|
|
|
DummySavePointTransaction.new(parent, savepoint_name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class DummySavePointTransaction < DB::SavePointTransaction
|
|
|
|
getter committed = false
|
|
|
|
getter rolledback = false
|
|
|
|
|
|
|
|
def initialize(parent, savepoint_name)
|
|
|
|
super(parent, savepoint_name)
|
|
|
|
end
|
|
|
|
|
|
|
|
def commit
|
|
|
|
super
|
|
|
|
@committed = true
|
|
|
|
end
|
|
|
|
|
|
|
|
def rollback
|
|
|
|
super
|
|
|
|
@rolledback = true
|
|
|
|
end
|
2016-11-16 02:46:11 +00:00
|
|
|
end
|
|
|
|
|
2016-01-28 23:31:35 +00:00
|
|
|
class DummyStatement < DB::Statement
|
2016-02-02 00:55:30 +00:00
|
|
|
property params
|
2016-01-29 22:21:48 +00:00
|
|
|
|
2020-09-25 17:49:50 +00:00
|
|
|
def initialize(connection, command : String, @prepared : Bool)
|
2019-09-20 20:23:09 +00:00
|
|
|
@params = Hash(Int32 | String, DB::Any | Array(DB::Any)).new
|
2020-09-25 17:49:50 +00:00
|
|
|
super(connection, command)
|
|
|
|
raise DB::Error.new(command) if command == "syntax error"
|
2016-01-28 23:31:35 +00:00
|
|
|
end
|
|
|
|
|
2019-08-02 14:54:52 +00:00
|
|
|
protected def perform_query(args : Enumerable) : DB::ResultSet
|
2016-08-31 20:32:01 +00:00
|
|
|
@connection.as(DummyConnection).check
|
2016-02-02 02:02:04 +00:00
|
|
|
set_params args
|
2020-09-25 17:49:50 +00:00
|
|
|
DummyResultSet.new self, command
|
2016-02-02 02:02:04 +00:00
|
|
|
end
|
|
|
|
|
2019-08-02 14:54:52 +00:00
|
|
|
protected def perform_exec(args : Enumerable) : DB::ExecResult
|
2016-08-31 20:32:01 +00:00
|
|
|
@connection.as(DummyConnection).check
|
2016-02-02 02:02:04 +00:00
|
|
|
set_params args
|
2020-09-25 17:49:50 +00:00
|
|
|
raise DB::Error.new("forced exception due to query") if command == "raise"
|
2016-06-21 21:31:13 +00:00
|
|
|
DB::ExecResult.new 0i64, 0_i64
|
2016-02-02 02:02:04 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
private def set_params(args)
|
2016-02-02 00:55:30 +00:00
|
|
|
@params.clear
|
|
|
|
args.each_with_index do |arg, index|
|
2016-06-22 03:44:16 +00:00
|
|
|
set_param(index, arg)
|
2016-02-02 00:55:30 +00:00
|
|
|
end
|
2016-01-28 23:31:35 +00:00
|
|
|
end
|
2016-02-03 20:10:03 +00:00
|
|
|
|
2016-06-22 03:44:16 +00:00
|
|
|
private def set_param(index, value : DB::Any)
|
|
|
|
@params[index] = value
|
|
|
|
end
|
|
|
|
|
2019-09-20 20:23:09 +00:00
|
|
|
private def set_param(index, value : Array)
|
|
|
|
@params[index] = value.map(&.as(DB::Any))
|
|
|
|
end
|
|
|
|
|
2016-06-22 03:44:16 +00:00
|
|
|
private def set_param(index, value)
|
|
|
|
raise "not implemented for #{value.class}"
|
|
|
|
end
|
|
|
|
|
2016-11-29 23:14:07 +00:00
|
|
|
def prepared?
|
|
|
|
@prepared
|
|
|
|
end
|
|
|
|
|
2016-02-03 20:10:03 +00:00
|
|
|
protected def do_close
|
2016-02-03 22:30:51 +00:00
|
|
|
super
|
2016-02-03 20:10:03 +00:00
|
|
|
end
|
2016-01-28 23:31:35 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
class DummyResultSet < DB::ResultSet
|
2016-06-16 15:14:57 +00:00
|
|
|
@top_values : Array(Array(String))
|
|
|
|
@values : Array(String)?
|
|
|
|
|
|
|
|
@@last_result_set : self?
|
2016-02-02 01:33:58 +00:00
|
|
|
|
2020-09-25 17:49:50 +00:00
|
|
|
def initialize(statement, command)
|
2016-01-28 23:31:35 +00:00
|
|
|
super(statement)
|
2020-09-25 17:49:50 +00:00
|
|
|
@top_values = command.split.map { |r| r.split(',') }.to_a
|
2016-03-28 23:31:42 +00:00
|
|
|
@column_count = @top_values.size > 0 ? @top_values[0].size : 2
|
2016-02-02 00:55:30 +00:00
|
|
|
|
2016-01-31 00:03:02 +00:00
|
|
|
@@last_result_set = self
|
|
|
|
end
|
|
|
|
|
2016-02-03 20:10:03 +00:00
|
|
|
protected def do_close
|
2016-02-03 22:30:51 +00:00
|
|
|
super
|
2016-02-03 20:10:03 +00:00
|
|
|
end
|
|
|
|
|
2016-01-31 00:03:02 +00:00
|
|
|
def self.last_result_set
|
|
|
|
@@last_result_set.not_nil!
|
|
|
|
end
|
|
|
|
|
2019-08-02 14:54:52 +00:00
|
|
|
def move_next : Bool
|
2016-06-16 15:14:57 +00:00
|
|
|
@values = @top_values.shift?
|
|
|
|
!!@values
|
2016-01-28 23:31:35 +00:00
|
|
|
end
|
|
|
|
|
2019-08-02 14:54:52 +00:00
|
|
|
def column_count : Int32
|
2016-03-28 23:31:42 +00:00
|
|
|
@column_count
|
2016-01-30 00:57:00 +00:00
|
|
|
end
|
|
|
|
|
2019-08-02 14:54:52 +00:00
|
|
|
def column_name(index) : String
|
2016-01-30 00:57:00 +00:00
|
|
|
"c#{index}"
|
|
|
|
end
|
|
|
|
|
2016-06-28 17:02:08 +00:00
|
|
|
def read
|
2016-06-16 15:14:57 +00:00
|
|
|
n = @values.not_nil!.shift?
|
|
|
|
raise "end of row" if n.is_a?(Nil)
|
2016-01-29 19:13:01 +00:00
|
|
|
return nil if n == "NULL"
|
2016-01-29 22:21:48 +00:00
|
|
|
|
|
|
|
if n == "?"
|
2016-04-10 18:21:10 +00:00
|
|
|
return (@statement.as(DummyStatement)).params[0]
|
2016-01-29 22:21:48 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return n
|
|
|
|
end
|
|
|
|
|
2021-10-12 23:49:26 +00:00
|
|
|
def next_column_index : Int32
|
|
|
|
@column_count - @values.not_nil!.size
|
|
|
|
end
|
|
|
|
|
2016-06-28 17:02:08 +00:00
|
|
|
def read(t : String.class)
|
|
|
|
read.to_s
|
2016-04-10 18:21:10 +00:00
|
|
|
end
|
|
|
|
|
2016-06-28 17:02:08 +00:00
|
|
|
def read(t : String?.class)
|
|
|
|
read.try &.to_s
|
2016-01-28 23:31:35 +00:00
|
|
|
end
|
|
|
|
|
2016-06-28 17:02:08 +00:00
|
|
|
def read(t : Int32.class)
|
|
|
|
read(String).to_i32
|
2016-01-29 19:13:01 +00:00
|
|
|
end
|
|
|
|
|
2016-07-04 15:13:39 +00:00
|
|
|
def read(t : Int32?.class)
|
|
|
|
read(String?).try &.to_i32
|
|
|
|
end
|
|
|
|
|
2016-06-28 17:02:08 +00:00
|
|
|
def read(t : Int64.class)
|
|
|
|
read(String).to_i64
|
2016-01-29 19:13:01 +00:00
|
|
|
end
|
|
|
|
|
2016-07-04 15:13:39 +00:00
|
|
|
def read(t : Int64?.class)
|
|
|
|
read(String?).try &.to_i64
|
|
|
|
end
|
|
|
|
|
2016-06-28 17:02:08 +00:00
|
|
|
def read(t : Float32.class)
|
|
|
|
read(String).to_f32
|
2016-01-29 19:13:01 +00:00
|
|
|
end
|
|
|
|
|
2016-06-28 17:02:08 +00:00
|
|
|
def read(t : Float64.class)
|
|
|
|
read(String).to_f64
|
2016-01-28 23:31:35 +00:00
|
|
|
end
|
2016-01-29 22:21:48 +00:00
|
|
|
|
2016-06-28 17:02:08 +00:00
|
|
|
def read(t : Bytes.class)
|
|
|
|
case value = read
|
|
|
|
when String
|
2016-01-29 22:21:48 +00:00
|
|
|
ary = value.bytes
|
|
|
|
Slice.new(ary.to_unsafe, ary.size)
|
2016-06-28 17:02:08 +00:00
|
|
|
when Bytes
|
2016-01-30 22:46:43 +00:00
|
|
|
value
|
2016-01-29 22:21:48 +00:00
|
|
|
else
|
2016-06-22 17:10:09 +00:00
|
|
|
raise "#{value} is not convertible to Bytes"
|
2016-01-29 22:21:48 +00:00
|
|
|
end
|
|
|
|
end
|
2016-01-28 23:31:35 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
DB.register_driver "dummy", DummyDriver
|
|
|
|
|
2016-01-30 22:46:43 +00:00
|
|
|
class Witness
|
|
|
|
getter count
|
|
|
|
|
2016-06-16 15:14:57 +00:00
|
|
|
def initialize(@count = 1)
|
2016-01-30 22:46:43 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def check
|
|
|
|
@count -= 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def with_witness(count = 1)
|
|
|
|
w = Witness.new(count)
|
|
|
|
yield w
|
|
|
|
w.count.should eq(0), "The expected coverage was unmet"
|
|
|
|
end
|
|
|
|
|
2016-10-22 00:49:16 +00:00
|
|
|
def with_dummy(uri : String = "dummy://host?checkout_timeout=0.5")
|
2016-02-03 03:36:50 +00:00
|
|
|
DummyDriver::DummyConnection.clear_connections
|
|
|
|
|
2016-10-22 00:49:16 +00:00
|
|
|
DB.open uri do |db|
|
2016-01-30 22:46:43 +00:00
|
|
|
yield db
|
|
|
|
end
|
2016-01-28 23:31:35 +00:00
|
|
|
end
|
2016-08-29 19:50:37 +00:00
|
|
|
|
2016-12-04 18:19:15 +00:00
|
|
|
def with_dummy_connection(options = "")
|
|
|
|
with_dummy("dummy://host?checkout_timeout=0.5&#{options}") do |db|
|
2016-08-29 19:50:37 +00:00
|
|
|
db.using_connection do |cnn|
|
|
|
|
yield cnn.as(DummyDriver::DummyConnection)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|