Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query Basic Types in Binary Format #223

Merged
merged 26 commits into from
Aug 12, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/LibPQ.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module LibPQ

export status, reset!, execute, prepare, async_execute, cancel,
export status, reset!, execute, execute_params, prepare, async_execute, cancel,
num_columns, num_rows, num_params, num_affected_rows


Expand Down Expand Up @@ -83,6 +83,9 @@ LibPQ.jl.
"""
const LIBPQ_CONVERSIONS = PQConversions()

const BINARY = true
const TEXT = false

include("connections.jl")
include("results.jl")
include("statements.jl")
Expand Down
3 changes: 2 additions & 1 deletion src/asyncresults.jl
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ function _async_submit(
conn_ptr::Ptr{libpq_c.PGconn},
query::AbstractString,
parameters::Vector{Ptr{UInt8}},
binary_format::Bool=false,
)
num_params = length(parameters)

Expand All @@ -274,7 +275,7 @@ function _async_submit(
parameters,
C_NULL, # paramLengths is ignored for text format parameters
zeros(Cint, num_params), # all parameters in text format
zero(Cint), # return result in text format
Cint(binary_format), # return result in text format
)

return send_status == 1
Expand Down
33 changes: 24 additions & 9 deletions src/parsing.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"A wrapper for one value in a PostgreSQL result."
struct PQValue{OID}
struct PQValue{OID, BinaryFormat}
"PostgreSQL result"
jl_result::Result

Expand All @@ -9,8 +9,12 @@ struct PQValue{OID}
"Column index of the result (0-indexed)"
col::Cint

function PQValue{OID}(jl_result::Result, row::Integer, col::Integer) where OID
return new{OID}(jl_result, row - 1, col - 1)
function PQValue{OID}(
jl_result::Result{BinaryFormat},
row::Integer,
col::Integer,
) where {OID, BinaryFormat}
return new{OID, BinaryFormat}(jl_result, row - 1, col - 1)
end
end

Expand All @@ -23,10 +27,10 @@ Row and column positions are provided 1-indexed.
If the `OID` type parameter is not provided, the Oid of the field will be retrieved from
the result.
"""
function PQValue(jl_result::Result, row::Integer, col::Integer)
function PQValue(jl_result::Result{BinaryFormat}, row::Integer, col::Integer) where BinaryFormat
oid = libpq_c.PQftype(jl_result.result, col - 1)

return PQValue{oid}(jl_result, row, col)
return PQValue{oid, BinaryFormat}(jl_result, row, col)
end

"""
Expand Down Expand Up @@ -100,6 +104,8 @@ is not in UTF-8.
"""
iamed2 marked this conversation as resolved.
Show resolved Hide resolved
bytes_view(pqv::PQValue) = unsafe_wrap(Vector{UInt8}, data_pointer(pqv), num_bytes(pqv) + 1)

# bytes_view(pqv::PQValue) = bswap(unsafe_load(data_pointer(pq_value_bin)))

iamed2 marked this conversation as resolved.
Show resolved Hide resolved
Base.String(pqv::PQValue) = unsafe_string(pqv)
Base.parse(::Type{String}, pqv::PQValue) = unsafe_string(pqv)
Base.convert(::Type{String}, pqv::PQValue) = String(pqv)
Expand All @@ -121,6 +127,12 @@ You can implement default PostgreSQL-specific parsing for a given type by overri
"""
Base.parse(::Type{T}, pqv::PQValue) where {T} = pqparse(T, string_view(pqv))

Base.parse(::Type{T}, pqv::PQValue{R, BINARY}) where {T <: Integer, R} = T(pqparse(
T,
Ptr{get(pqv.jl_result.type_lookup, R, String)}(data_pointer(pqv)),
))

iamed2 marked this conversation as resolved.
Show resolved Hide resolved

"""
LibPQ.pqparse(::Type{T}, str::AbstractString) -> T

Expand All @@ -131,6 +143,7 @@ function pqparse end

# Fallback method
pqparse(::Type{T}, str::AbstractString) where {T} = parse(T, str)
pqparse(::Type{T}, ptr::Ptr) where {T} = parse(T, ptr)

# allow parsing as a Symbol anything which works as a String
pqparse(::Type{Symbol}, str::AbstractString) = Symbol(str)
Expand All @@ -140,6 +153,8 @@ _DEFAULT_TYPE_MAP[:int2] = Int16
_DEFAULT_TYPE_MAP[:int4] = Int32
_DEFAULT_TYPE_MAP[:int8] = Int64

pqparse(::Type{<:Integer}, pqv::Ptr) = ntoh(unsafe_load(pqv))

## floating point
_DEFAULT_TYPE_MAP[:float4] = Float32
_DEFAULT_TYPE_MAP[:float8] = Float64
Expand All @@ -154,7 +169,7 @@ _DEFAULT_TYPE_MAP[:numeric] = Decimal

## character
# bpchar is char(n)
function Base.parse(::Type{String}, pqv::PQValue{PQ_SYSTEM_TYPES[:bpchar]})
function Base.parse(::Type{String}, pqv::PQValue{PQ_SYSTEM_TYPES[:bpchar], TEXT})
return String(rstrip(string_view(pqv), ' '))
end
# char is "char"
Expand All @@ -168,7 +183,7 @@ pqparse(::Type{Char}, str::AbstractString) = Char(pqparse(PQChar, str))
_DEFAULT_TYPE_MAP[:bytea] = Vector{UInt8}

# Needs it's own `parse` method as it uses bytes_view instead of string_view
function Base.parse(::Type{Vector{UInt8}}, pqv::PQValue{PQ_SYSTEM_TYPES[:bytea]})
function Base.parse(::Type{Vector{UInt8}}, pqv::PQValue{PQ_SYSTEM_TYPES[:bytea], TEXT})
pqparse(Vector{UInt8}, bytes_view(pqv))
end

Expand Down Expand Up @@ -291,11 +306,11 @@ function pqparse(::Type{InfExtendedTime{T}}, str::AbstractString) where {T<:Date
end

# UNIX timestamps
function Base.parse(::Type{DateTime}, pqv::PQValue{PQ_SYSTEM_TYPES[:int8]})
function Base.parse(::Type{DateTime}, pqv::PQValue{PQ_SYSTEM_TYPES[:int8], false})
unix2datetime(parse(Int64, pqv))
end

function Base.parse(::Type{ZonedDateTime}, pqv::PQValue{PQ_SYSTEM_TYPES[:int8]})
function Base.parse(::Type{ZonedDateTime}, pqv::PQValue{PQ_SYSTEM_TYPES[:int8], false})
TimeZones.unix2zdt(parse(Int64, pqv))
end

Expand Down
46 changes: 34 additions & 12 deletions src/results.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"A result from a PostgreSQL database query"
mutable struct Result
mutable struct Result{BinaryFormat}
"A pointer to a libpq PGresult object (C_NULL if cleared)"
result::Ptr{libpq_c.PGresult}

Expand All @@ -21,18 +21,20 @@ mutable struct Result
"Name of each column in the result"
column_names::Vector{String}

type_lookup::LayerDict
iamed2 marked this conversation as resolved.
Show resolved Hide resolved

# TODO: attach encoding per https://wiki.postgresql.org/wiki/Driver_development#Result_object_and_client_encoding
function Result(
function Result{BinaryFormat}(
result::Ptr{libpq_c.PGresult},
jl_conn::Connection;
column_types::Union{AbstractDict, AbstractVector}=ColumnTypeMap(),
type_map::AbstractDict=PQTypeMap(),
conversions::AbstractDict=PQConversions(),
not_null=false,
)
jl_result = new(result, Atomic{Bool}(result == C_NULL))
) where BinaryFormat
jl_result = new{BinaryFormat}(result, Atomic{Bool}(result == C_NULL))

type_lookup = LayerDict(
jl_result.type_lookup = LayerDict(
PQTypeMap(type_map),
jl_conn.type_map,
LIBPQ_TYPE_MAP,
Expand Down Expand Up @@ -63,7 +65,7 @@ mutable struct Result
jl_result.column_types = col_types = collect(Type, imap(enumerate(col_oids)) do itr
col_num, col_oid = itr
get(column_type_map, col_num) do
get(type_lookup, col_oid, String)
get(jl_result.type_lookup, col_oid, String)
end
end)

Expand Down Expand Up @@ -106,6 +108,24 @@ mutable struct Result
end
end

function Result(
result::Ptr{libpq_c.PGresult},
jl_conn::Connection;
column_types::Union{AbstractDict, AbstractVector}=ColumnTypeMap(),
type_map::AbstractDict=PQTypeMap(),
conversions::AbstractDict=PQConversions(),
not_null=false,
)
return Result{TEXT}(
result,
jl_conn;
column_types,
type_map,
conversions,
not_null,
)
end

"""
show(io::IO, jl_result::Result)

Expand Down Expand Up @@ -280,21 +300,22 @@ function execute(
return handle_result(Result(result, jl_conn; kwargs...); throw_error=throw_error)
end

function execute(
function execute_params(
jl_conn::Connection,
query::AbstractString,
parameters::Union{AbstractVector, Tuple};
parameters::Union{AbstractVector, Tuple}=[];
throw_error::Bool=true,
binary_format::Bool=false,
kwargs...
)
string_params = string_parameters(parameters)
pointer_params = parameter_pointers(string_params)

result = lock(jl_conn) do
_execute(jl_conn.conn, query, pointer_params)
_execute(jl_conn.conn, query, pointer_params; binary_format)
end

return handle_result(Result(result, jl_conn; kwargs...); throw_error=throw_error)
return handle_result(Result{binary_format}(result, jl_conn; kwargs...); throw_error=throw_error)
end

function _execute(conn_ptr::Ptr{libpq_c.PGconn}, query::AbstractString)
Expand All @@ -304,7 +325,8 @@ end
function _execute(
conn_ptr::Ptr{libpq_c.PGconn},
query::AbstractString,
parameters::Vector{Ptr{UInt8}},
parameters::Vector{Ptr{UInt8}};
binary_format::Bool,
)
num_params = length(parameters)

Expand All @@ -316,7 +338,7 @@ function _execute(
parameters,
C_NULL, # paramLengths is ignored for text format parameters
zeros(Cint, num_params), # all parameters in text format
zero(Cint), # return result in text format
Cint(binary_format), # return result in text format
)
end

Expand Down
5 changes: 3 additions & 2 deletions src/statements.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ function column_number(stmt::Statement, column_name::AbstractString)
column_number(stmt.description, column_name)
end

function execute(
function execute_params(
stmt::Statement,
parameters::Union{AbstractVector, Tuple};
throw_error::Bool=true,
Expand Down Expand Up @@ -146,6 +146,7 @@ function _execute_prepared(
conn_ptr::Ptr{libpq_c.PGconn},
stmt_name::AbstractString,
parameters::Vector{Ptr{UInt8}}=Ptr{UInt8}[],
binary_format::Bool=false,
)
num_params = length(parameters)

Expand All @@ -156,6 +157,6 @@ function _execute_prepared(
num_params == 0 ? C_NULL : parameters,
C_NULL, # paramLengths is ignored for text format parameters
num_params == 0 ? C_NULL : zeros(Cint, num_params), # all parameters in text format
zero(Cint), # return result in text format
Cint(binary_format), # return result in text format
)
end
2 changes: 1 addition & 1 deletion src/tables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ function load!(table::T, connection::Connection, query::AbstractString) where {T
string_parameter(val)
end
end
close(execute(stmt, parameters; throw_error=true))
close(execute_params(stmt, parameters; throw_error=true))
state = iterate(rows, st)
end
return stmt
Expand Down
Loading