114 lines
4.8 KiB
Julia
114 lines
4.8 KiB
Julia
mutable struct Stmt <: DBInterface.Statement
|
|
con::Connection
|
|
handle::duckdb_prepared_statement
|
|
sql::AbstractString
|
|
result_type::Type
|
|
|
|
function Stmt(con::Connection, sql::AbstractString, result_type::Type)
|
|
handle = Ref{duckdb_prepared_statement}()
|
|
result = duckdb_prepare(con.handle, sql, handle)
|
|
if result != DuckDBSuccess
|
|
ptr = duckdb_prepare_error(handle[])
|
|
if ptr == C_NULL
|
|
error_message = "Preparation of statement failed: unknown error"
|
|
else
|
|
error_message = unsafe_string(ptr)
|
|
end
|
|
duckdb_destroy_prepare(handle)
|
|
throw(QueryException(error_message))
|
|
end
|
|
stmt = new(con, handle[], sql, result_type)
|
|
finalizer(_close_stmt, stmt)
|
|
return stmt
|
|
end
|
|
|
|
function Stmt(db::DB, sql::AbstractString, result_type::Type)
|
|
return Stmt(db.main_connection, sql, result_type)
|
|
end
|
|
end
|
|
|
|
function _close_stmt(stmt::Stmt)
|
|
if stmt.handle != C_NULL
|
|
duckdb_destroy_prepare(stmt.handle)
|
|
end
|
|
stmt.handle = C_NULL
|
|
return
|
|
end
|
|
|
|
DBInterface.getconnection(stmt::Stmt) = stmt.con
|
|
|
|
|
|
function nparameters(stmt::Stmt)
|
|
return Int(duckdb_nparams(stmt.handle))
|
|
end
|
|
|
|
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::AbstractFloat) = duckdb_bind_double(stmt.handle, i, Float64(val));
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Bool) = duckdb_bind_boolean(stmt.handle, i, val);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Int8) = duckdb_bind_int8(stmt.handle, i, val);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Int16) = duckdb_bind_int16(stmt.handle, i, val);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Int32) = duckdb_bind_int32(stmt.handle, i, val);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Int64) = duckdb_bind_int64(stmt.handle, i, val);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::UInt8) = duckdb_bind_uint8(stmt.handle, i, val);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::UInt16) = duckdb_bind_uint16(stmt.handle, i, val);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::UInt32) = duckdb_bind_uint32(stmt.handle, i, val);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::UInt64) = duckdb_bind_uint64(stmt.handle, i, val);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Float32) = duckdb_bind_float(stmt.handle, i, val);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Float64) = duckdb_bind_double(stmt.handle, i, val);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Date) = duckdb_bind_date(stmt.handle, i, value_to_duckdb(val));
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Time) = duckdb_bind_time(stmt.handle, i, value_to_duckdb(val));
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::DateTime) =
|
|
duckdb_bind_timestamp(stmt.handle, i, value_to_duckdb(val));
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Missing) = duckdb_bind_null(stmt.handle, i);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Nothing) = duckdb_bind_null(stmt.handle, i);
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::AbstractString) =
|
|
duckdb_bind_varchar_length(stmt.handle, i, val, ncodeunits(val));
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::Vector{UInt8}) = duckdb_bind_blob(stmt.handle, i, val, sizeof(val));
|
|
duckdb_bind_internal(stmt::Stmt, i::Integer, val::WeakRefString{UInt8}) =
|
|
duckdb_bind_varchar_length(stmt.handle, i, val.ptr, val.len);
|
|
function duckdb_bind_internal(stmt::Stmt, i::Integer, val::AbstractVector{T}) where {T}
|
|
value = create_value(val)
|
|
return duckdb_bind_value(stmt.handle, i, value.handle)
|
|
end
|
|
|
|
function duckdb_bind_internal(stmt::Stmt, i::Integer, val::Any)
|
|
println(val)
|
|
throw(NotImplementedException("unsupported type for bind"))
|
|
end
|
|
|
|
function bind_parameters(stmt::Stmt, params::DBInterface.PositionalStatementParams)
|
|
i = 1
|
|
for param in params
|
|
if duckdb_bind_internal(stmt, i, param) != DuckDBSuccess
|
|
throw(QueryException("Failed to bind parameter"))
|
|
end
|
|
i += 1
|
|
end
|
|
end
|
|
|
|
function bind_parameters(stmt::Stmt, params::DBInterface.NamedStatementParams)
|
|
N = nparameters(stmt)
|
|
if length(params) == 0
|
|
return # no parameters to bind
|
|
end
|
|
K = eltype(keys(params))
|
|
for i in 1:N
|
|
name_ptr = duckdb_parameter_name(stmt.handle, i)
|
|
name = unsafe_string(name_ptr)
|
|
duckdb_free(name_ptr)
|
|
name_key = K(name)
|
|
if !haskey(params, name_key)
|
|
if isa(params, NamedTuple)
|
|
value = params[i] # FIXME this is a workaround to keep the interface consistent, see the test in test_sqlite.jl
|
|
else
|
|
throw(QueryException("Parameter '$name' not found"))
|
|
end
|
|
else
|
|
value = getindex(params, name_key)
|
|
end
|
|
if duckdb_bind_internal(stmt, i, value) != DuckDBSuccess
|
|
throw(QueryException("Failed to bind parameter '$name'"))
|
|
end
|
|
end
|
|
end
|