Skip to content

Commit

Permalink
Handle nil values inside arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
jgaskins committed Dec 1, 2024
1 parent c8bc7a7 commit f636bbf
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 8 deletions.
1 change: 1 addition & 0 deletions spec/pg/encoder_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ describe PG::Driver, "encoder" do
test_insert_and_read "integer[][][]", [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

test_insert_and_read "text[]", ["t", "f", "t"]
test_insert_and_read "text[]", ["non-nil value", nil]
test_insert_and_read "text[]", [%("a), %(\\b~), %(c\\"d), %(\uFF8F)]
test_insert_and_read "text[]", ["baz, bar"]
test_insert_and_read "text[]", ["foo}"]
Expand Down
38 changes: 30 additions & 8 deletions src/pq/param.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ module PQ
# Internal wrapper to represent an encoded parameter

def self.encode(val : Nil)
binary Bytes.empty, -1
encode val, into: Bytes.empty
end

def self.encode(val : Nil, into slice : Bytes)
binary slice, -1
end

def self.encode(val : Bool, into slice : Bytes = Bytes.new(1))
Expand Down Expand Up @@ -176,7 +180,11 @@ module PQ
} of String => Int32

protected def self.oid_for(type : T.class) forall T
OID_MAP[type.name]
{% if T.union? %}
oid_for({{T.union_types.reject(&.nilable?).first}})
{% else %}
OID_MAP[type.name]
{% end %}
end

protected def self.oid_for(type : Array(T).class) forall T
Expand All @@ -188,7 +196,9 @@ module PQ
# expression like `1 + 1 + 1 + 0` for a 3-dimensional array, which will be
# inlined into the numeric literal `3` at compile time.
macro dimension_count(type)
{% if type.resolve < Array %}
{% if type.is_a? Expressions %}
::PQ::Param::ArrayEncoder.dimension_count(Union({{type}}))
{% elsif type.resolve < Array %}
1 + dimension_count({{type.resolve.type_vars.first}})
{% else %}
0
Expand Down Expand Up @@ -232,7 +242,11 @@ module PQ
data_offset = dimensions_offset + 8 * collect_dimensions.size
flat_data.each do |element|
# pp encoding: element, into: data_offset
size = size_for(element)
if element.nil?
size = -1
else
size = size_for(element)
end
Param.encode size, into: bytes + data_offset
Param.encode element, into: bytes + data_offset + 4
data_offset += size + 4
Expand All @@ -249,13 +263,21 @@ module PQ
Int32.name => sizeof(Int32),
Int64.name => sizeof(Int64),
Float64.name => sizeof(Float64),
UUID.name => sizeof(UUID),
UUID.name => sizeof(UUID),
}

def size_for(value : T) forall T
SIZE_MAP.fetch(T.name) do
raise "Could not determine encoding size for #{T}"
end
{% if T.union? %}
if value.nil?
0
else
size_for(value.as({{T.union_types.reject(&.nilable?).first}}))
end
{% else %}
SIZE_MAP.fetch(value.class.name) do
raise "Could not determine encoding size for #{T}"
end
{% end %}
end

def size_for(value : Bool)
Expand Down

0 comments on commit f636bbf

Please sign in to comment.