Skip to content

Commit bfdb557

Browse files
committed
Automatically determine flags for array serialization
This way it automatically works with versions of postgres that didn't support null array entries as long as the data doesn't actually have a null
1 parent 77b3f77 commit bfdb557

File tree

2 files changed

+59
-21
lines changed

2 files changed

+59
-21
lines changed

postgres-protocol/src/types.rs

+59-20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
//! Conversions to and from Postgres's binary format for various types.
2-
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
2+
use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
33
use fallible_iterator::FallibleIterator;
4+
use std::boxed::Box as StdBox;
45
use std::error::Error;
56
use std::str;
6-
use std::boxed::Box as StdBox;
77

8-
use {Oid, IsNull, write_nullable, FromUsize};
8+
use {write_nullable, FromUsize, IsNull, Oid};
99

1010
const RANGE_UPPER_UNBOUNDED: u8 = 0b0001_0000;
1111
const RANGE_LOWER_UNBOUNDED: u8 = 0b0000_1000;
@@ -189,9 +189,7 @@ where
189189
}
190190

191191
let count = i32::from_usize(count)?;
192-
(&mut buf[base..base + 4])
193-
.write_i32::<BigEndian>(count)
194-
.unwrap();
192+
BigEndian::write_i32(&mut buf[base..], count);
195193

196194
Ok(())
197195
}
@@ -424,7 +422,6 @@ pub fn uuid_from_sql(buf: &[u8]) -> Result<[u8; 16], StdBox<Error + Sync + Send>
424422
#[inline]
425423
pub fn array_to_sql<T, I, J, F>(
426424
dimensions: I,
427-
has_nulls: bool,
428425
element_type: Oid,
429426
elements: J,
430427
mut serializer: F,
@@ -437,7 +434,8 @@ where
437434
{
438435
let dimensions_idx = buf.len();
439436
buf.extend_from_slice(&[0; 4]);
440-
buf.write_i32::<BigEndian>(has_nulls as i32).unwrap();
437+
let flags_idx = buf.len();
438+
buf.extend_from_slice(&[0; 4]);
441439
buf.write_u32::<BigEndian>(element_type).unwrap();
442440

443441
let mut num_dimensions = 0;
@@ -448,14 +446,24 @@ where
448446
}
449447

450448
let num_dimensions = i32::from_usize(num_dimensions)?;
451-
(&mut buf[dimensions_idx..dimensions_idx + 4])
452-
.write_i32::<BigEndian>(num_dimensions)
453-
.unwrap();
449+
BigEndian::write_i32(&mut buf[dimensions_idx..], num_dimensions);
454450

451+
let mut has_nulls = false;
455452
for element in elements {
456-
write_nullable(|buf| serializer(element, buf), buf)?;
453+
write_nullable(
454+
|buf| {
455+
let r = serializer(element, buf);
456+
if let Ok(IsNull::Yes) = r {
457+
has_nulls = true;
458+
}
459+
r
460+
},
461+
buf,
462+
)?;
457463
}
458464

465+
BigEndian::write_i32(&mut buf[flags_idx..], has_nulls as i32);
466+
459467
Ok(())
460468
}
461469

@@ -674,9 +682,7 @@ where
674682
IsNull::No => i32::from_usize(buf.len() - base - 4)?,
675683
IsNull::Yes => -1,
676684
};
677-
(&mut buf[base..base + 4])
678-
.write_i32::<BigEndian>(len)
679-
.unwrap();
685+
BigEndian::write_i32(&mut buf[base..], len);
680686
}
681687
None => buf.truncate(base),
682688
}
@@ -862,9 +868,7 @@ where
862868
}
863869

864870
let num_points = i32::from_usize(num_points)?;
865-
(&mut buf[points_idx..])
866-
.write_i32::<BigEndian>(num_points)
867-
.unwrap();
871+
BigEndian::write_i32(&mut buf[points_idx..], num_points);
868872

869873
Ok(())
870874
}
@@ -941,8 +945,8 @@ impl<'a> FallibleIterator for PathPoints<'a> {
941945

942946
#[cfg(test)]
943947
mod test {
944-
use std::collections::HashMap;
945948
use fallible_iterator::FallibleIterator;
949+
use std::collections::HashMap;
946950

947951
use super::*;
948952
use IsNull;
@@ -1039,7 +1043,6 @@ mod test {
10391043
let mut buf = vec![];
10401044
array_to_sql(
10411045
dimensions.iter().cloned(),
1042-
true,
10431046
10,
10441047
values.iter().cloned(),
10451048
|v, buf| match v {
@@ -1058,4 +1061,40 @@ mod test {
10581061
assert_eq!(array.dimensions().collect::<Vec<_>>().unwrap(), dimensions);
10591062
assert_eq!(array.values().collect::<Vec<_>>().unwrap(), values);
10601063
}
1064+
1065+
#[test]
1066+
fn non_null_array() {
1067+
let dimensions = [
1068+
ArrayDimension {
1069+
len: 1,
1070+
lower_bound: 10,
1071+
},
1072+
ArrayDimension {
1073+
len: 2,
1074+
lower_bound: 0,
1075+
},
1076+
];
1077+
let values = [Some(&b"hola"[..]), Some(&b"hello"[..])];
1078+
1079+
let mut buf = vec![];
1080+
array_to_sql(
1081+
dimensions.iter().cloned(),
1082+
10,
1083+
values.iter().cloned(),
1084+
|v, buf| match v {
1085+
Some(v) => {
1086+
buf.extend_from_slice(v);
1087+
Ok(IsNull::No)
1088+
}
1089+
None => Ok(IsNull::Yes),
1090+
},
1091+
&mut buf,
1092+
).unwrap();
1093+
1094+
let array = array_from_sql(&buf).unwrap();
1095+
assert_eq!(array.has_nulls(), false);
1096+
assert_eq!(array.element_type(), 10);
1097+
assert_eq!(array.dimensions().collect::<Vec<_>>().unwrap(), dimensions);
1098+
assert_eq!(array.values().collect::<Vec<_>>().unwrap(), values);
1099+
}
10611100
}

postgres-shared/src/types/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,6 @@ impl<'a, T: ToSql> ToSql for &'a [T] {
588588

589589
types::array_to_sql(
590590
Some(dimension),
591-
true,
592591
member_type.oid(),
593592
self.iter(),
594593
|e, w| match e.to_sql(member_type, w)? {

0 commit comments

Comments
 (0)