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:
parent
76e6ecf0e7
commit
d0868ed4fd
59
README.md
59
README.md
@ -234,62 +234,6 @@ types. The driver currently supports the following conversions:
|
||||
<td>types::range::Range<Timespec></td>
|
||||
<td>TSRANGE, TSTZRANGE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<bool>></td>
|
||||
<td>BOOL[], BOOL[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<Vec<u8>>></td>
|
||||
<td>BYTEA[], BYTEA[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<i8>></td>
|
||||
<td>"char"[], "char"[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<i16>></td>
|
||||
<td>INT2[], INT2[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<i32>></td>
|
||||
<td>INT4[], INT4[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<String>></td>
|
||||
<td>TEXT[], CHAR(n)[], VARCHAR[], TEXT[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<Json>></td>
|
||||
<td>JSON[], JSON[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<i64>></td>
|
||||
<td>INT8[], INT8[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<Timespec>></td>
|
||||
<td>TIMESTAMP[], TIMESTAMPTZ[], TIMESTAMP[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<f32>></td>
|
||||
<td>FLOAT4[], FLOAT4[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<f64>></td>
|
||||
<td>FLOAT8[], FLOAT8[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<Range<i32>>></td>
|
||||
<td>INT4RANGE[], INT4RANGE[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<Range<Timespec>>></td>
|
||||
<td>TSRANGE[], TSTZRANGE[], TSRANGE[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>types::array::ArrayBase<Option<Range<i64>>></td>
|
||||
<td>INT8RANGE[], INT8RANGE[][], ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>std::collections::HashMap<String, Option<String>></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
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
140
src/types/mod.rs
140
src/types/mod.rs
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user