Files
email-tracker/external/duckdb/tools/juliapkg/src/statement.jl
2025-10-24 19:21:19 -05:00

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