Pull array support out to a separate crate

It's pretty obscure and the API is kind of bad. Pulling it out should
allow it to evolve independently from the main library.
This commit is contained in:
Steven Fackler 2014-12-24 23:58:27 -05:00
parent 76e6ecf0e7
commit d0868ed4fd
9 changed files with 21 additions and 819 deletions

View File

@ -234,62 +234,6 @@ types. The driver currently supports the following conversions:
<td>types::range::Range&lt;Timespec&gt;</td>
<td>TSRANGE, TSTZRANGE</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;bool&gt;&gt;</td>
<td>BOOL[], BOOL[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;Vec&lt;u8&gt;&gt;&gt;</td>
<td>BYTEA[], BYTEA[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;i8&gt;&gt;</td>
<td>"char"[], "char"[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;i16&gt;&gt;</td>
<td>INT2[], INT2[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;i32&gt;&gt;</td>
<td>INT4[], INT4[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;String&gt;&gt;</td>
<td>TEXT[], CHAR(n)[], VARCHAR[], TEXT[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;Json&gt;&gt;</td>
<td>JSON[], JSON[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;i64&gt;&gt;</td>
<td>INT8[], INT8[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;Timespec&gt;&gt;</td>
<td>TIMESTAMP[], TIMESTAMPTZ[], TIMESTAMP[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;f32&gt;&gt;</td>
<td>FLOAT4[], FLOAT4[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;f64&gt;&gt;</td>
<td>FLOAT8[], FLOAT8[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;Range&lt;i32&gt;&gt;&gt;</td>
<td>INT4RANGE[], INT4RANGE[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;Range&lt;Timespec&gt;&gt;&gt;</td>
<td>TSRANGE[], TSTZRANGE[], TSRANGE[][], ...</td>
</tr>
<tr>
<td>types::array::ArrayBase&lt;Option&lt;Range&lt;i64&gt;&gt;&gt;</td>
<td>INT8RANGE[], INT8RANGE[][], ...</td>
</tr>
<tr>
<td>std::collections::HashMap&lt;String, Option&lt;String&gt;&gt;</td>
<td>HSTORE</td>
@ -300,6 +244,9 @@ types. The driver currently supports the following conversions:
More conversions can be defined by implementing the `ToSql` and `FromSql`
traits.
Support for Postgres arrays is located in the
[postgres-arrays](https://github.com/sfackler/rust-postgres-arrays) crate.
## Optional features
### UUID type

View File

@ -1,343 +0,0 @@
//! Multi-dimensional arrays with per-dimension specifiable lower bounds
use std::mem;
use std::slice;
use self::ArrayParent::{SliceParent, MutSliceParent, BaseParent};
use self::MutArrayParent::{MutSliceMutParent, MutBaseParent};
/// Information about a dimension of an array
#[deriving(PartialEq, Eq, Clone, Copy)]
pub struct DimensionInfo {
/// The size of the dimension
pub len: uint,
/// The index of the first element of the dimension
pub lower_bound: int,
}
/// Specifies methods that can be performed on multi-dimensional arrays
pub trait Array<T> {
/// Returns information about the dimensions of this array
fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo];
/// Slices into this array, returning an immutable view of a subarray.
///
/// ## Failure
///
/// Fails if the array is one-dimensional or the index is out of bounds.
fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T>;
/// Retrieves an immutable reference to a value in this array.
///
///
/// ## Failure
///
/// Fails if the array is multi-dimensional or the index is out of bounds.
fn get<'a>(&'a self, idx: int) -> &'a T;
}
/// Specifies methods that can be performed on mutable multi-dimensional arrays
pub trait MutableArray<T> : Array<T> {
/// Slices into this array, returning a mutable view of a subarray.
///
/// ## Failure
///
/// Fails if the array is one-dimensional or the index is out of bounds.
fn slice_mut<'a>(&'a mut self, idx: int) -> MutArraySlice<'a, T>;
/// Retrieves a mutable reference to a value in this array.
///
///
/// ## Failure
///
/// Fails if the array is multi-dimensional or the index is out of bounds.
fn get_mut<'a>(&'a mut self, idx: int) -> &'a mut T;
}
#[doc(hidden)]
trait InternalArray<T>: Array<T> {
fn shift_idx(&self, idx: int) -> uint {
let shifted_idx = idx - self.dimension_info()[0].lower_bound;
assert!(shifted_idx >= 0 &&
shifted_idx < self.dimension_info()[0].len as int,
"Out of bounds array access");
shifted_idx as uint
}
fn raw_get<'a>(&'a self, idx: uint, size: uint) -> &'a T;
}
#[doc(hidden)]
trait InternalMutableArray<T>: MutableArray<T> {
fn raw_get_mut<'a>(&'a mut self, idx: uint, size: uint) -> &'a mut T;
}
/// A multi-dimensional array
#[deriving(PartialEq, Eq, Clone)]
pub struct ArrayBase<T> {
info: Vec<DimensionInfo>,
data: Vec<T>,
}
impl<T> ArrayBase<T> {
/// Creates a new multi-dimensional array from its underlying components.
///
/// The data array should be provided in the higher-dimensional equivalent
/// of row-major order.
///
/// ## Failure
///
/// Fails if there are 0 dimensions or the number of elements provided does
/// not match the number of elements specified.
pub fn from_raw(data: Vec<T>, info: Vec<DimensionInfo>)
-> ArrayBase<T> {
assert!(!info.is_empty(), "Cannot create a 0x0 array");
assert!(data.len() == info.iter().fold(1, |acc, i| acc * i.len),
"Size mismatch");
ArrayBase {
info: info,
data: data,
}
}
/// Creates a new one-dimensional array from a vector.
pub fn from_vec(data: Vec<T>, lower_bound: int) -> ArrayBase<T> {
ArrayBase {
info: vec!(DimensionInfo {
len: data.len(),
lower_bound: lower_bound
}),
data: data
}
}
/// Wraps this array in a new dimension of size 1.
///
/// For example the one-dimensional array `[1,2]` would turn into
/// the two-dimensional array `[[1,2]]`.
pub fn wrap(&mut self, lower_bound: int) {
self.info.insert(0, DimensionInfo {
len: 1,
lower_bound: lower_bound
})
}
/// Takes ownership of another array, appending it to the top-level
/// dimension of this array.
///
/// The dimensions of the other array must have an identical shape to the
/// dimensions of a slice of this array. This includes both the sizes of
/// the dimensions as well as their lower bounds.
///
/// For example, if `[3,4]` is pushed onto `[[1,2]]`, the result is
/// `[[1,2],[3,4]]`.
///
/// ## Failure
///
/// Fails if the other array does not have dimensions identical to the
/// dimensions of a slice of this array.
pub fn push_move(&mut self, other: ArrayBase<T>) {
assert!(self.info.len() - 1 == other.info.len(),
"Cannot append differently shaped arrays");
for (info1, info2) in self.info.iter().skip(1).zip(other.info.iter()) {
assert!(info1 == info2, "Cannot join differently shaped arrays");
}
self.info[0].len += 1;
self.data.extend(other.data.into_iter());
}
/// Returns an iterator over the values in this array, in the
/// higher-dimensional equivalent of row-major order.
pub fn values<'a>(&'a self) -> slice::Iter<'a, T> {
self.data.iter()
}
}
impl<T> Array<T> for ArrayBase<T> {
fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo] {
&*self.info
}
fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T> {
assert!(self.info.len() != 1,
"Attempted to slice a one-dimensional array");
ArraySlice {
parent: BaseParent(self),
idx: self.shift_idx(idx),
}
}
fn get<'a>(&'a self, idx: int) -> &'a T {
assert!(self.info.len() == 1,
"Attempted to get from a multi-dimensional array");
self.raw_get(self.shift_idx(idx), 1)
}
}
impl<T> MutableArray<T> for ArrayBase<T> {
fn slice_mut<'a>(&'a mut self, idx: int) -> MutArraySlice<'a, T> {
assert!(self.info.len() != 1,
"Attempted to slice_mut into a one-dimensional array");
MutArraySlice {
idx: self.shift_idx(idx),
parent: MutBaseParent(self),
}
}
fn get_mut<'a>(&'a mut self, idx: int) -> &'a mut T {
assert!(self.info.len() == 1,
"Attempted to get_mut from a multi-dimensional array");
let idx = self.shift_idx(idx);
self.raw_get_mut(idx, 1)
}
}
impl<T> InternalArray<T> for ArrayBase<T> {
fn raw_get<'a>(&'a self, idx: uint, _size: uint) -> &'a T {
&self.data[idx]
}
}
impl<T> InternalMutableArray<T> for ArrayBase<T> {
fn raw_get_mut<'a>(&'a mut self, idx: uint, _size: uint) -> &'a mut T {
&mut self.data[idx]
}
}
enum ArrayParent<'parent, T:'static> {
SliceParent(&'parent ArraySlice<'static, T>),
MutSliceParent(&'parent MutArraySlice<'static, T>),
BaseParent(&'parent ArrayBase<T>),
}
/// An immutable slice of a multi-dimensional array
pub struct ArraySlice<'parent, T:'static> {
parent: ArrayParent<'parent, T>,
idx: uint,
}
impl<'parent, T> Array<T> for ArraySlice<'parent, T> {
fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo] {
let info = match self.parent {
SliceParent(p) => p.dimension_info(),
MutSliceParent(p) => p.dimension_info(),
BaseParent(p) => p.dimension_info()
};
info[1..]
}
fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T> {
assert!(self.dimension_info().len() != 1,
"Attempted to slice a one-dimensional array");
unsafe {
ArraySlice {
parent: SliceParent(mem::transmute(self)),
idx: self.shift_idx(idx),
}
}
}
fn get<'a>(&'a self, idx: int) -> &'a T {
assert!(self.dimension_info().len() == 1,
"Attempted to get from a multi-dimensional array");
self.raw_get(self.shift_idx(idx), 1)
}
}
impl<'parent, T> InternalArray<T> for ArraySlice<'parent, T> {
fn raw_get<'a>(&'a self, idx: uint, size: uint) -> &'a T {
let size = size * self.dimension_info()[0].len;
let idx = size * self.idx + idx;
match self.parent {
SliceParent(p) => p.raw_get(idx, size),
MutSliceParent(p) => p.raw_get(idx, size),
BaseParent(p) => p.raw_get(idx, size)
}
}
}
enum MutArrayParent<'parent, T:'static> {
MutSliceMutParent(&'parent mut MutArraySlice<'static, T>),
MutBaseParent(&'parent mut ArrayBase<T>),
}
/// A mutable slice of a multi-dimensional array
pub struct MutArraySlice<'parent, T:'static> {
parent: MutArrayParent<'parent, T>,
idx: uint,
}
impl<'parent, T> Array<T> for MutArraySlice<'parent, T> {
fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo] {
let info : &'a [DimensionInfo] = unsafe {
match self.parent {
MutSliceMutParent(ref p) => mem::transmute(p.dimension_info()),
MutBaseParent(ref p) => mem::transmute(p.dimension_info()),
}
};
info[1..]
}
fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T> {
assert!(self.dimension_info().len() != 1,
"Attempted to slice a one-dimensional array");
unsafe {
ArraySlice {
parent: MutSliceParent(mem::transmute(self)),
idx: self.shift_idx(idx),
}
}
}
fn get<'a>(&'a self, idx: int) -> &'a T {
assert!(self.dimension_info().len() == 1,
"Attempted to get from a multi-dimensional array");
self.raw_get(self.shift_idx(idx), 1)
}
}
impl<'parent, T> MutableArray<T> for MutArraySlice<'parent, T> {
fn slice_mut<'a>(&'a mut self, idx: int) -> MutArraySlice<'a, T> {
assert!(self.dimension_info().len() != 1,
"Attempted to slice_mut a one-dimensional array");
unsafe {
MutArraySlice {
idx: self.shift_idx(idx),
parent: MutSliceMutParent(mem::transmute(self)),
}
}
}
fn get_mut<'a>(&'a mut self, idx: int) -> &'a mut T {
assert!(self.dimension_info().len() == 1,
"Attempted to get_mut from a multi-dimensional array");
let idx = self.shift_idx(idx);
self.raw_get_mut(idx, 1)
}
}
impl<'parent, T> InternalArray<T> for MutArraySlice<'parent, T> {
fn raw_get<'a>(&'a self, idx: uint, size: uint) -> &'a T {
let size = size * self.dimension_info()[0].len;
let idx = size * self.idx + idx;
unsafe {
match self.parent {
MutSliceMutParent(ref p) => mem::transmute(p.raw_get(idx, size)),
MutBaseParent(ref p) => mem::transmute(p.raw_get(idx, size))
}
}
}
}
impl<'parent, T> InternalMutableArray<T> for MutArraySlice<'parent, T> {
fn raw_get_mut<'a>(&'a mut self, idx: uint, size: uint) -> &'a mut T {
let size = size * self.dimension_info()[0].len;
let idx = size * self.idx + idx;
unsafe {
match self.parent {
MutSliceMutParent(ref mut p) => mem::transmute(p.raw_get_mut(idx, size)),
MutBaseParent(ref mut p) => mem::transmute(p.raw_get_mut(idx, size))
}
}
}
}

View File

@ -8,7 +8,6 @@ use std::io::net::ip::IpAddr;
use Result;
use error::Error;
use types::range::{Range, RangeBound, BoundSided, BoundType, Normalizable};
use types::array::{Array, ArrayBase};
macro_rules! check_types {
($($expected:pat)|+, $actual:ident) => (
@ -76,50 +75,6 @@ macro_rules! from_raw_from_impl {
)
}
macro_rules! from_array_impl {
($($oid:pat)|+, $t:ty $(, $a:meta)*) => (
from_map_impl!($($oid)|+, ::types::array::ArrayBase<Option<$t>>, |buf: &[u8]| {
use std::io::ByRefReader;
use std::io::util::LimitReader;
use std::iter::MultiplicativeIterator;
use types::{Oid, RawFromSql};
use types::array::{ArrayBase, DimensionInfo};
use Error;
let mut rdr = buf;
let ndim = try!(rdr.read_be_i32()) as uint;
let _has_null = try!(rdr.read_be_i32()) == 1;
let _element_type: Oid = try!(rdr.read_be_u32());
let mut dim_info = Vec::with_capacity(ndim);
for _ in range(0, ndim) {
dim_info.push(DimensionInfo {
len: try!(rdr.read_be_i32()) as uint,
lower_bound: try!(rdr.read_be_i32()) as int
});
}
let nele = dim_info.iter().map(|info| info.len).product();
let mut elements = Vec::with_capacity(nele);
for _ in range(0, nele) {
let len = try!(rdr.read_be_i32());
if len < 0 {
elements.push(None);
} else {
let mut limit = LimitReader::new(rdr.by_ref(), len as uint);
elements.push(Some(try!(RawFromSql::raw_from_sql(&mut limit))));
if limit.limit() != 0 {
return Err(Error::BadData);
}
}
}
Ok(ArrayBase::from_raw(elements, dim_info))
} $(, $a)*);
)
}
macro_rules! raw_to_impl {
($t:ty, $f:ident) => (
impl RawToSql for $t {
@ -178,50 +133,6 @@ macro_rules! to_raw_to_impl {
)
}
macro_rules! to_array_impl {
($($oid:pat)|+, $t:ty $(, $a:meta)*) => (
$(#[$a])*
impl ::types::ToSql for ::types::array::ArrayBase<Option<$t>> {
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
check_types!($($oid)|+, ty);
Ok(Some(::types::raw_to_array(self, ty)))
}
}
to_option_impl!($($oid)|+, ::types::array::ArrayBase<Option<$t>> $(, $a)*);
)
}
fn raw_to_array<T>(array: &ArrayBase<Option<T>>, ty: &Type) -> Vec<u8> where T: RawToSql {
let mut buf = vec![];
let _ = buf.write_be_i32(array.dimension_info().len() as i32);
let _ = buf.write_be_i32(1);
let _ = buf.write_be_u32(ty.member_type().to_oid());
for info in array.dimension_info().iter() {
let _ = buf.write_be_i32(info.len as i32);
let _ = buf.write_be_i32(info.lower_bound as i32);
}
for v in array.values() {
match *v {
Some(ref val) => {
let mut inner_buf = vec![];
let _ = val.raw_to_sql(&mut inner_buf);
let _ = buf.write_be_i32(inner_buf.len() as i32);
let _ = buf.write(&*inner_buf);
}
None => {
let _ = buf.write_be_i32(-1);
}
}
}
buf
}
pub mod array;
pub mod range;
#[cfg(feature = "uuid")]
mod uuid;
@ -302,7 +213,10 @@ macro_rules! make_postgres_type {
}
impl Type {
#[doc(hidden)]
/// Creates a `Type` from an OID.
///
/// If the OID is unknown, the `name` field is initialized to an
/// empty string.
pub fn from_oid(oid: Oid) -> Type {
match oid {
$($oid => Type::$variant,)+
@ -311,7 +225,7 @@ macro_rules! make_postgres_type {
}
}
#[doc(hidden)]
/// Returns the OID of the `Type`.
pub fn to_oid(&self) -> Oid {
match *self {
$(Type::$variant => $oid,)+
@ -319,7 +233,12 @@ macro_rules! make_postgres_type {
}
}
fn member_type(&self) -> Type {
/// Returns the element `Type` if this `Type` is an array.
///
/// # Panics
///
/// Panics if this `Type` is not an array.
pub fn member_type(&self) -> Type {
match *self {
$(
$(Type::$variant => Type::$member,)*
@ -432,8 +351,9 @@ pub trait FromSql {
fn from_sql(ty: &Type, raw: Option<&[u8]>) -> Result<Self>;
}
#[doc(hidden)]
trait RawFromSql {
/// A utility trait used by `FromSql` implementations
pub trait RawFromSql {
/// Creates a new value of this type from a reader of Postgre data.
fn raw_from_sql<R: Reader>(raw: &mut R) -> Result<Self>;
}
@ -547,19 +467,6 @@ from_raw_from_impl!(Type::Inet | Type::Cidr, IpAddr);
from_raw_from_impl!(Type::Int4Range, Range<i32>);
from_raw_from_impl!(Type::Int8Range, Range<i64>);
from_array_impl!(Type::BoolArray, bool);
from_array_impl!(Type::ByteAArray, Vec<u8>);
from_array_impl!(Type::CharArray, i8);
from_array_impl!(Type::Int2Array, i16);
from_array_impl!(Type::Int4Array, i32);
from_array_impl!(Type::TextArray | Type::CharNArray | Type::VarcharArray | Type::NameArray, String);
from_array_impl!(Type::Int8Array, i64);
from_array_impl!(Type::JsonArray, json::Json);
from_array_impl!(Type::Float4Array, f32);
from_array_impl!(Type::Float8Array, f64);
from_array_impl!(Type::Int4RangeArray, Range<i32>);
from_array_impl!(Type::Int8RangeArray, Range<i64>);
impl FromSql for Option<String> {
fn from_sql(ty: &Type, raw: Option<&[u8]>) -> Result<Option<String>> {
match *ty {
@ -631,8 +538,10 @@ pub trait ToSql {
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>>;
}
#[doc(hidden)]
trait RawToSql {
/// A utility trait used by `ToSql` implementations.
pub trait RawToSql {
/// Converts the value of `self` into the binary format appropriate for the
/// Postgres backend, writing it to `w`.
fn raw_to_sql<W: Writer>(&self, w: &mut W) -> Result<()>;
}
@ -797,19 +706,6 @@ impl<'a> ToSql for &'a [u8] {
to_option_impl_lifetime!(Type::ByteA, &'a [u8]);
to_array_impl!(Type::BoolArray, bool);
to_array_impl!(Type::ByteAArray, Vec<u8>);
to_array_impl!(Type::CharArray, i8);
to_array_impl!(Type::Int2Array, i16);
to_array_impl!(Type::Int4Array, i32);
to_array_impl!(Type::Int8Array, i64);
to_array_impl!(Type::TextArray | Type::CharNArray | Type::VarcharArray | Type::NameArray, String);
to_array_impl!(Type::Float4Array, f32);
to_array_impl!(Type::Float8Array, f64);
to_array_impl!(Type::Int4RangeArray, Range<i32>);
to_array_impl!(Type::Int8RangeArray, Range<i64>);
to_array_impl!(Type::JsonArray, json::Json);
impl ToSql for HashMap<String, Option<String>> {
fn to_sql(&self, ty: &Type) -> Result<Option<Vec<u8>>> {
match *ty {

View File

@ -26,8 +26,6 @@ impl RawFromSql for Timespec {
from_raw_from_impl!(Type::Timestamp | Type::TimestampTZ, Timespec);
from_raw_from_impl!(Type::TsRange | Type::TstzRange, Range<Timespec>);
from_array_impl!(Type::TimestampArray | Type::TimestampTZArray, Timespec);
from_array_impl!(Type::TsRangeArray | Type::TstzRangeArray, Range<Timespec>);
impl RawToSql for Timespec {
fn raw_to_sql<W: Writer>(&self, w: &mut W) -> Result<()> {
@ -38,8 +36,6 @@ impl RawToSql for Timespec {
to_raw_to_impl!(Type::TsRange | Type::TstzRange, Range<Timespec>);
to_raw_to_impl!(Type::Timestamp | Type::TimestampTZ, Timespec);
to_array_impl!(Type::TimestampArray | Type::TimestampTZArray, Timespec);
to_array_impl!(Type::TsRangeArray | Type::TstzRangeArray, Range<Timespec>);
impl Normalizable for Timespec {
fn normalize<S>(bound: RangeBound<S, Timespec>) -> RangeBound<S, Timespec> where S: BoundSided {

View File

@ -15,7 +15,6 @@ impl RawFromSql for Uuid {
}
from_raw_from_impl!(Type::Uuid, Uuid, doc = "requires the \"uuid\" feature");
from_array_impl!(Type::UuidArray, Uuid, doc = "requires the \"uuid\" feature");
impl RawToSql for Uuid {
fn raw_to_sql<W: Writer>(&self, w: &mut W) -> Result<()> {
@ -24,4 +23,3 @@ impl RawToSql for Uuid {
}
to_raw_to_impl!(Type::Uuid, Uuid, doc = "requires the \"uuid\" feature");
to_array_impl!(Type::UuidArray, Uuid, doc = "requires the \"uuid\" feature");

View File

@ -1,137 +0,0 @@
use postgres::types::array::{DimensionInfo, ArrayBase, Array, MutableArray};
#[test]
fn test_from_vec() {
let a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
assert!([DimensionInfo { len: 3, lower_bound: -1 }][] ==
a.dimension_info());
assert_eq!(&0, a.get(-1));
assert_eq!(&1, a.get(0));
assert_eq!(&2, a.get(1));
}
#[test]
#[should_fail]
fn test_get_2d_fail() {
let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
a.wrap(1);
a.get(1);
}
#[test]
#[should_fail]
fn test_2d_slice_range_fail_low() {
let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
a.wrap(1);
a.slice(0);
}
#[test]
#[should_fail]
fn test_2d_slice_range_fail_high() {
let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
a.wrap(1);
a.slice(2);
}
#[test]
fn test_2d_slice_get() {
let mut a = ArrayBase::from_vec(vec!(0i, 1, 2), -1);
a.wrap(1);
let s = a.slice(1);
assert_eq!(&0, s.get(-1));
assert_eq!(&1, s.get(0));
assert_eq!(&2, s.get(1));
}
#[test]
#[should_fail]
fn test_push_move_wrong_lower_bound() {
let mut a = ArrayBase::from_vec(vec!(1i), -1);
a.push_move(ArrayBase::from_vec(vec!(2), 0));
}
#[test]
#[should_fail]
fn test_push_move_wrong_dims() {
let mut a = ArrayBase::from_vec(vec!(1i), -1);
a.wrap(1);
a.push_move(ArrayBase::from_vec(vec!(1, 2), -1));
}
#[test]
#[should_fail]
fn test_push_move_wrong_dim_count() {
let mut a = ArrayBase::from_vec(vec!(1i), -1);
a.wrap(1);
let mut b = ArrayBase::from_vec(vec!(2), -1);
b.wrap(1);
a.push_move(b);
}
#[test]
fn test_push_move_ok() {
let mut a = ArrayBase::from_vec(vec!(1i, 2), 0);
a.wrap(0);
a.push_move(ArrayBase::from_vec(vec!(3, 4), 0));
let s = a.slice(0);
assert_eq!(&1, s.get(0));
assert_eq!(&2, s.get(1));
let s = a.slice(1);
assert_eq!(&3, s.get(0));
assert_eq!(&4, s.get(1));
}
#[test]
fn test_3d() {
let mut a = ArrayBase::from_vec(vec!(0i, 1), 0);
a.wrap(0);
a.push_move(ArrayBase::from_vec(vec!(2, 3), 0));
a.wrap(0);
let mut b = ArrayBase::from_vec(vec!(4, 5), 0);
b.wrap(0);
b.push_move(ArrayBase::from_vec(vec!(6, 7), 0));
a.push_move(b);
let s1 = a.slice(0);
let s2 = s1.slice(0);
assert_eq!(&0, s2.get(0));
assert_eq!(&1, s2.get(1));
let s2 = s1.slice(1);
assert_eq!(&2, s2.get(0));
assert_eq!(&3, s2.get(1));
let s1 = a.slice(1);
let s2 = s1.slice(0);
assert_eq!(&4, s2.get(0));
assert_eq!(&5, s2.get(1));
let s2 = s1.slice(1);
assert_eq!(&6, s2.get(0));
assert_eq!(&7, s2.get(1));
}
#[test]
fn test_mut() {
let mut a = ArrayBase::from_vec(vec!(1i, 2), 0);
a.wrap(0);
{
let mut s = a.slice_mut(0);
*s.get_mut(0) = 3;
}
let s = a.slice(0);
assert_eq!(&3, s.get(0));
}
#[test]
#[should_fail]
fn test_base_overslice() {
let a = ArrayBase::from_vec(vec!(1i), 0);
a.slice(0);
}
#[test]
#[should_fail]
fn test_slice_overslice() {
let mut a = ArrayBase::from_vec(vec!(1i), 0);
a.wrap(0);
let s = a.slice(0);
s.slice(0);
}

View File

@ -9,24 +9,6 @@ use std::io::net::ip::IpAddr;
use postgres::{Connection, SslMode};
use postgres::types::{ToSql, FromSql};
macro_rules! test_array_params {
($name:expr, $v1:expr, $s1:expr, $v2:expr, $s2:expr, $v3:expr, $s3:expr) => ({
use postgres::types::array::ArrayBase;
use types::test_type;
let tests = &[(Some(ArrayBase::from_vec(vec!(Some($v1), Some($v2), None), 1)),
format!("'{{{},{},NULL}}'", $s1, $s2).into_string()),
(None, "NULL".to_string())];
test_type(format!("{}[]", $name)[], tests);
let mut a = ArrayBase::from_vec(vec!(Some($v1), Some($v2)), 0);
a.wrap(-1);
a.push_move(ArrayBase::from_vec(vec!(None, Some($v3)), 0));
let tests = &[(Some(a), format!("'[-1:0][0:1]={{{{{},{}}},{{NULL,{}}}}}'",
$s1, $s2, $s3).into_string())];
test_type(format!("{}[][]", $name)[], tests);
})
}
macro_rules! test_range {
($name:expr, $t:ty, $low:expr, $low_str:expr, $high:expr, $high_str:expr) => ({
let tests = &[(Some(range!('(', ')')), "'(,)'".to_string()),
@ -48,11 +30,9 @@ macro_rules! test_range {
})
}
mod array;
mod range;
#[cfg(feature = "uuid")]
mod uuid;
#[cfg(feature = "time")]
mod time;
fn test_type<T: PartialEq+FromSql+ToSql, S: fmt::Show>(sql_type: &str, checks: &[(T, S)]) {
@ -214,88 +194,6 @@ fn test_int8range_params() {
test_range!("INT8RANGE", i64, 100i64, "100", 200i64, "200")
}
#[test]
fn test_boolarray_params() {
test_array_params!("BOOL", false, "f", true, "t", true, "t");
}
#[test]
fn test_byteaarray_params() {
test_array_params!("BYTEA", vec!(0u8, 1), r#""\\x0001""#, vec!(254u8, 255u8),
r#""\\xfeff""#, vec!(10u8, 11u8), r#""\\x0a0b""#);
}
#[test]
fn test_chararray_params() {
test_array_params!("\"char\"", 'a' as i8, "a", 'z' as i8, "z",
'0' as i8, "0");
}
#[test]
fn test_namearray_params() {
test_array_params!("NAME", "hello".to_string(), "hello", "world".to_string(),
"world", "!".to_string(), "!");
}
#[test]
fn test_int2array_params() {
test_array_params!("INT2", 0i16, "0", 1i16, "1", 2i16, "2");
}
#[test]
fn test_int4array_params() {
test_array_params!("INT4", 0i32, "0", 1i32, "1", 2i32, "2");
}
#[test]
fn test_textarray_params() {
test_array_params!("TEXT", "hello".to_string(), "hello", "world".to_string(),
"world", "!".to_string(), "!");
}
#[test]
fn test_charnarray_params() {
test_array_params!("CHAR(5)", "hello".to_string(), "hello",
"world".to_string(), "world", "! ".to_string(), "!");
}
#[test]
fn test_varchararray_params() {
test_array_params!("VARCHAR", "hello".to_string(), "hello",
"world".to_string(), "world", "!".to_string(), "!");
}
#[test]
fn test_int8array_params() {
test_array_params!("INT8", 0i64, "0", 1i64, "1", 2i64, "2");
}
#[test]
fn test_float4array_params() {
test_array_params!("FLOAT4", 0f32, "0", 1.5f32, "1.5", 0.009f32, ".009");
}
#[test]
fn test_float8array_params() {
test_array_params!("FLOAT8", 0f64, "0", 1.5f64, "1.5", 0.009f64, ".009");
}
#[test]
fn test_int4rangearray_params() {
test_array_params!("INT4RANGE",
range!('(', ')'), "\"(,)\"",
range!('[' 10i32, ')'), "\"[10,)\"",
range!('(', 10i32 ')'), "\"(,10)\"");
}
#[test]
fn test_int8rangearray_params() {
test_array_params!("INT8RANGE",
range!('(', ')'), "\"(,)\"",
range!('[' 10i64, ')'), "\"[10,)\"",
range!('(', 10i64 ')'), "\"(,10)\"");
}
#[test]
fn test_hstore_params() {
macro_rules! make_map {
@ -338,17 +236,6 @@ fn test_f64_nan_param() {
test_nan_param::<f64>("DOUBLE PRECISION");
}
#[test]
fn test_jsonarray_params() {
test_array_params!("JSON",
json::from_str("[10, 11, 12]").unwrap(),
"\"[10,11,12]\"",
json::from_str(r#"{"a": 10, "b": null}"#).unwrap(),
r#""{\"a\": 10, \"b\": null}""#,
json::from_str(r#"{"a": [10], "b": true}"#).unwrap(),
r#""{\"a\": [10], \"b\": true}""#);
}
#[test]
fn test_pg_database_datname() {
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", &SslMode::None));

View File

@ -38,33 +38,3 @@ fn test_tsrange_params() {
fn test_tstzrange_params() {
test_timespec_range_params("TSTZRANGE");
}
#[test]
fn test_timestamparray_params() {
fn make_check<'a>(time: &'a str) -> (Timespec, &'a str) {
(time::strptime(time, "%Y-%m-%d %H:%M:%S.%f").unwrap().to_timespec(), time)
}
let (v1, s1) = make_check("1970-01-01 00:00:00.01");
let (v2, s2) = make_check("1965-09-25 11:19:33.100314");
let (v3, s3) = make_check("2010-02-09 23:11:45.1202");
test_array_params!("TIMESTAMP", v1, s1, v2, s2, v3, s3);
test_array_params!("TIMESTAMPTZ", v1, s1, v2, s2, v3, s3);
}
#[test]
fn test_tsrangearray_params() {
fn make_check<'a>(time: &'a str) -> (Timespec, &'a str) {
(time::strptime(time, "%Y-%m-%d").unwrap().to_timespec(), time)
}
let (v1, s1) = make_check("1970-10-11");
let (v2, s2) = make_check("1990-01-01");
let r1 = range!('(', ')');
let rs1 = "\"(,)\"";
let r2 = range!('[' v1, ')');
let rs2 = format!("\"[{},)\"", s1);
let r3 = range!('(', v2 ')');
let rs3 = format!("\"(,{})\"", s2);
test_array_params!("TSRANGE", r1, rs1, r2, rs2, r3, rs3);
test_array_params!("TSTZRANGE", r1, rs1, r2, rs2, r3, rs3);
}

View File

@ -8,15 +8,3 @@ fn test_uuid_params() {
"'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'"),
(None, "NULL")])
}
#[test]
fn test_uuidarray_params() {
fn make_check<'a>(uuid: &'a str) -> (uuid::Uuid, &'a str) {
(uuid::Uuid::parse_str(uuid).unwrap(), uuid)
}
let (v1, s1) = make_check("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11");
let (v2, s2) = make_check("00000000-0000-0000-0000-000000000000");
let (v3, s3) = make_check("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11");
test_array_params!("UUID", v1.clone(), s1, v2.clone(), s2, v3.clone(), s3);
}