Fully working prepare

This commit is contained in:
Steven Fackler 2019-07-26 20:11:34 -07:00
parent 4396f38fcc
commit 51f02c89ee
14 changed files with 826 additions and 677 deletions

View File

@ -1,7 +1,7 @@
use crate::codec::BackendMessages; use crate::codec::BackendMessages;
use crate::connection::{Request, RequestMessages}; use crate::connection::{Request, RequestMessages};
use crate::prepare::prepare; use crate::prepare::prepare;
use crate::query::{execute, query, Query}; use crate::query::{self, Query};
use crate::types::{Oid, ToSql, Type}; use crate::types::{Oid, ToSql, Type};
use crate::{Error, Statement}; use crate::{Error, Statement};
use fallible_iterator::FallibleIterator; use fallible_iterator::FallibleIterator;
@ -42,9 +42,9 @@ impl Responses {
} }
struct State { struct State {
has_typeinfo: bool, typeinfo: Option<Statement>,
has_typeinfo_composite: bool, typeinfo_composite: Option<Statement>,
has_typeinfo_enum: bool, typeinfo_enum: Option<Statement>,
types: HashMap<Oid, Type>, types: HashMap<Oid, Type>,
} }
@ -67,36 +67,36 @@ impl InnerClient {
}) })
} }
pub fn has_typeinfo(&self) -> bool { pub fn typeinfo(&self) -> Option<Statement> {
self.state.lock().has_typeinfo self.state.lock().typeinfo.clone()
} }
pub fn set_has_typeinfo(&self) { pub fn set_typeinfo(&self, statement: &Statement) {
self.state.lock().has_typeinfo = true; self.state.lock().typeinfo = Some(statement.clone());
} }
pub fn has_typeinfo_composite(&self) -> bool { pub fn typeinfo_composite(&self) -> Option<Statement> {
self.state.lock().has_typeinfo_composite self.state.lock().typeinfo_composite.clone()
} }
pub fn set_has_typeinfo_composite(&self) { pub fn set_typeinfo_composite(&self, statement: &Statement) {
self.state.lock().has_typeinfo_composite = true; self.state.lock().typeinfo_composite = Some(statement.clone());
} }
pub fn has_typeinfo_enum(&self) -> bool { pub fn typeinfo_enum(&self) -> Option<Statement> {
self.state.lock().has_typeinfo_enum self.state.lock().typeinfo_enum.clone()
} }
pub fn set_has_typeinfo_enum(&self) { pub fn set_typeinfo_enum(&self, statement: &Statement) {
self.state.lock().has_typeinfo_enum = true; self.state.lock().typeinfo_enum = Some(statement.clone());
} }
pub fn type_(&self, oid: Oid) -> Option<Type> { pub fn type_(&self, oid: Oid) -> Option<Type> {
self.state.lock().types.get(&oid).cloned() self.state.lock().types.get(&oid).cloned()
} }
pub fn set_type(&self, oid: Oid, type_: Type) { pub fn set_type(&self, oid: Oid, type_: &Type) {
self.state.lock().types.insert(oid, type_); self.state.lock().types.insert(oid, type_.clone());
} }
} }
@ -116,9 +116,9 @@ impl Client {
inner: Arc::new(InnerClient { inner: Arc::new(InnerClient {
sender, sender,
state: Mutex::new(State { state: Mutex::new(State {
has_typeinfo: false, typeinfo: None,
has_typeinfo_composite: false, typeinfo_composite: None,
has_typeinfo_enum: false, typeinfo_enum: None,
types: HashMap::new(), types: HashMap::new(),
}), }),
}), }),
@ -131,58 +131,59 @@ impl Client {
self.inner.clone() self.inner.clone()
} }
pub fn prepare<'a>( pub fn prepare(&mut self, query: &str) -> impl Future<Output = Result<Statement, Error>> {
&mut self,
query: &'a str,
) -> impl Future<Output = Result<Statement, Error>> + 'a {
self.prepare_typed(query, &[]) self.prepare_typed(query, &[])
} }
pub fn prepare_typed<'a>( pub fn prepare_typed(
&mut self, &mut self,
query: &'a str, query: &str,
parameter_types: &'a [Type], parameter_types: &[Type],
) -> impl Future<Output = Result<Statement, Error>> + 'a { ) -> impl Future<Output = Result<Statement, Error>> {
prepare(self.inner(), query, parameter_types) prepare(self.inner(), query, parameter_types)
} }
pub fn query<'a>( pub fn query(
&mut self, &mut self,
statement: &'a Statement, statement: &Statement,
params: &'a [&dyn ToSql], params: &[&dyn ToSql],
) -> impl Future<Output = Result<Query, Error>> + 'a { ) -> impl Future<Output = Result<Query, Error>> {
self.query_iter(statement, params.iter().cloned()) let buf = query::encode(statement, params.iter().cloned());
query::query(self.inner(), statement.clone(), buf)
} }
pub fn query_iter<'a, I>( pub fn query_iter<'a, I>(
&mut self, &mut self,
statement: &'a Statement, statement: &Statement,
params: I, params: I,
) -> impl Future<Output = Result<Query, Error>> + 'a ) -> impl Future<Output = Result<Query, Error>>
where where
I: IntoIterator<Item = &'a dyn ToSql> + 'a, I: IntoIterator<Item = &'a dyn ToSql>,
I::IntoIter: ExactSizeIterator, I::IntoIter: ExactSizeIterator,
{ {
query(self.inner(), statement, params) let buf = query::encode(statement, params);
query::query(self.inner(), statement.clone(), buf)
} }
pub fn execute<'a>( pub fn execute(
&mut self, &mut self,
statement: &'a Statement, statement: &Statement,
params: &'a [&dyn ToSql], params: &[&dyn ToSql],
) -> impl Future<Output = Result<u64, Error>> + 'a { ) -> impl Future<Output = Result<u64, Error>> {
self.execute_iter(statement, params.iter().cloned()) let buf = query::encode(statement, params.iter().cloned());
query::execute(self.inner(), buf)
} }
pub fn execute_iter<'a, I>( pub fn execute_iter<'a, I>(
&mut self, &mut self,
statement: &'a Statement, statement: &Statement,
params: I, params: I,
) -> impl Future<Output = Result<u64, Error>> + 'a ) -> impl Future<Output = Result<u64, Error>>
where where
I: IntoIterator<Item = &'a dyn ToSql> + 'a, I: IntoIterator<Item = &'a dyn ToSql>,
I::IntoIter: ExactSizeIterator, I::IntoIter: ExactSizeIterator,
{ {
execute(self.inner(), statement, params) let buf = query::encode(statement, params);
query::execute(self.inner(), buf)
} }
} }

View File

@ -1,64 +1,119 @@
use crate::client::InnerClient; use crate::client::InnerClient;
use crate::codec::FrontendMessage; use crate::codec::FrontendMessage;
use crate::connection::RequestMessages; use crate::connection::RequestMessages;
use crate::types::{Oid, Type}; use crate::error::SqlState;
use crate::query;
use crate::types::{Field, Kind, Oid, ToSql, Type};
use crate::{Column, Error, Statement}; use crate::{Column, Error, Statement};
use fallible_iterator::FallibleIterator; use fallible_iterator::FallibleIterator;
use futures::{future, StreamExt, TryStreamExt};
use postgres_protocol::message::backend::Message; use postgres_protocol::message::backend::Message;
use postgres_protocol::message::frontend; use postgres_protocol::message::frontend;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
const TYPEINFO_STMT: &str = "typeinfo";
const TYPEINFO_QUERY: &str = "\
SELECT t.typname, t.typtype, t.typelem, r.rngsubtype, t.typbasetype, n.nspname, t.typrelid
FROM pg_catalog.pg_type t
LEFT OUTER JOIN pg_catalog.pg_range r ON r.rngtypid = t.oid
INNER JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid
WHERE t.oid = $1
";
// Range types weren't added until Postgres 9.2, so pg_range may not exist
const TYPEINFO_FALLBACK_QUERY: &str = "\
SELECT t.typname, t.typtype, t.typelem, NULL::OID, t.typbasetype, n.nspname, t.typrelid
FROM pg_catalog.pg_type t
INNER JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid
WHERE t.oid = $1
";
const TYPEINFO_ENUM_QUERY: &str = "\
SELECT enumlabel
FROM pg_catalog.pg_enum
WHERE enumtypid = $1
ORDER BY enumsortorder
";
// Postgres 9.0 didn't have enumsortorder
const TYPEINFO_ENUM_FALLBACK_QUERY: &str = "\
SELECT enumlabel
FROM pg_catalog.pg_enum
WHERE enumtypid = $1
ORDER BY oid
";
const TYPEINFO_COMPOSITE_QUERY: &str = "\
SELECT attname, atttypid
FROM pg_catalog.pg_attribute
WHERE attrelid = $1
AND NOT attisdropped
AND attnum > 0
ORDER BY attnum
";
static NEXT_ID: AtomicUsize = AtomicUsize::new(0); static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
pub async fn prepare( pub fn prepare(
client: Arc<InnerClient>, client: Arc<InnerClient>,
query: &str, query: &str,
types: &[Type], types: &[Type],
) -> Result<Statement, Error> { ) -> impl Future<Output = Result<Statement, Error>> {
let name = format!("s{}", NEXT_ID.fetch_add(1, Ordering::SeqCst)); let name = format!("s{}", NEXT_ID.fetch_add(1, Ordering::SeqCst));
let buf = encode(&name, query, types);
async move {
let buf = buf?;
let mut responses = client.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?;
match responses.next().await? {
Message::ParseComplete => {}
_ => return Err(Error::unexpected_message()),
}
let parameter_description = match responses.next().await? {
Message::ParameterDescription(body) => body,
_ => return Err(Error::unexpected_message()),
};
let row_description = match responses.next().await? {
Message::RowDescription(body) => Some(body),
Message::NoData => None,
_ => return Err(Error::unexpected_message()),
};
let mut parameters = vec![];
let mut it = parameter_description.parameters();
while let Some(oid) = it.next().map_err(Error::parse)? {
let type_ = get_type(&client, oid).await?;
parameters.push(type_);
}
let mut columns = vec![];
if let Some(row_description) = row_description {
let mut it = row_description.fields();
while let Some(field) = it.next().map_err(Error::parse)? {
let type_ = get_type(&client, field.type_oid()).await?;
let column = Column::new(field.name().to_string(), type_);
columns.push(column);
}
}
Ok(Statement::new(&client, name, parameters, columns))
}
}
fn encode(name: &str, query: &str, types: &[Type]) -> Result<Vec<u8>, Error> {
let mut buf = vec![]; let mut buf = vec![];
frontend::parse(&name, query, types.iter().map(Type::oid), &mut buf).map_err(Error::encode)?; frontend::parse(name, query, types.iter().map(Type::oid), &mut buf).map_err(Error::encode)?;
frontend::describe(b'S', &name, &mut buf).map_err(Error::encode)?; frontend::describe(b'S', &name, &mut buf).map_err(Error::encode)?;
frontend::sync(&mut buf); frontend::sync(&mut buf);
let mut responses = client.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?; Ok(buf)
match responses.next().await? {
Message::ParseComplete => {}
_ => return Err(Error::unexpected_message()),
}
let parameter_description = match responses.next().await? {
Message::ParameterDescription(body) => body,
_ => return Err(Error::unexpected_message()),
};
let row_description = match responses.next().await? {
Message::RowDescription(body) => Some(body),
Message::NoData => None,
_ => return Err(Error::unexpected_message()),
};
let mut parameters = vec![];
let mut it = parameter_description.parameters();
while let Some(oid) = it.next().map_err(Error::parse)? {
let type_ = get_type(&client, oid).await?;
parameters.push(type_);
}
let mut columns = vec![];
if let Some(row_description) = row_description {
let mut it = row_description.fields();
while let Some(field) = it.next().map_err(Error::parse)? {
let type_ = get_type(&client, field.type_oid()).await?;
let column = Column::new(field.name().to_string(), type_);
columns.push(column);
}
}
Ok(Statement::new(&client, name, parameters, columns))
} }
async fn get_type(client: &Arc<InnerClient>, oid: Oid) -> Result<Type, Error> { async fn get_type(client: &Arc<InnerClient>, oid: Oid) -> Result<Type, Error> {
@ -70,5 +125,133 @@ async fn get_type(client: &Arc<InnerClient>, oid: Oid) -> Result<Type, Error> {
return Ok(type_); return Ok(type_);
} }
unimplemented!() let stmt = typeinfo_statement(client).await?;
let params: &[&dyn ToSql] = &[&oid];
let buf = query::encode(&stmt, params.iter().cloned());
let mut rows = query::query(client.clone(), stmt, buf).await?;
let row = match rows.try_next().await? {
Some(row) => row,
None => return Err(Error::unexpected_message()),
};
let name: String = row.try_get(0)?;
let type_: i8 = row.try_get(1)?;
let elem_oid: Oid = row.try_get(2)?;
let rngsubtype: Option<Oid> = row.try_get(3)?;
let basetype: Oid = row.try_get(4)?;
let schema: String = row.try_get(5)?;
let relid: Oid = row.try_get(6)?;
let kind = if type_ == b'e' as i8 {
let variants = get_enum_variants(client, oid).await?;
Kind::Enum(variants)
} else if type_ == b'p' as i8 {
Kind::Pseudo
} else if basetype != 0 {
let type_ = get_type_rec(client, basetype).await?;
Kind::Domain(type_)
} else if elem_oid != 0 {
let type_ = get_type_rec(client, elem_oid).await?;
Kind::Array(type_)
} else if relid != 0 {
let fields = get_composite_fields(client, relid).await?;
Kind::Composite(fields)
} else if let Some(rngsubtype) = rngsubtype {
let type_ = get_type_rec(client, rngsubtype).await?;
Kind::Range(type_)
} else {
Kind::Simple
};
let type_ = Type::new(name, oid, kind, schema);
client.set_type(oid, &type_);
Ok(type_)
}
fn get_type_rec<'a>(
client: &'a Arc<InnerClient>,
oid: Oid,
) -> Pin<Box<dyn Future<Output = Result<Type, Error>> + 'a>> {
Box::pin(get_type(client, oid))
}
async fn typeinfo_statement(client: &Arc<InnerClient>) -> Result<Statement, Error> {
if let Some(stmt) = client.typeinfo() {
return Ok(stmt);
}
let stmt = match Box::pin(prepare(client.clone(), TYPEINFO_QUERY, &[])).await {
Ok(stmt) => stmt,
Err(ref e) if e.code() == Some(&SqlState::UNDEFINED_TABLE) => {
Box::pin(prepare(client.clone(), TYPEINFO_FALLBACK_QUERY, &[])).await?
}
Err(e) => return Err(e),
};
client.set_typeinfo(&stmt);
Ok(stmt)
}
async fn get_enum_variants(client: &Arc<InnerClient>, oid: Oid) -> Result<Vec<String>, Error> {
let stmt = typeinfo_enum_statement(client).await?;
let params: &[&dyn ToSql] = &[&oid];
let buf = query::encode(&stmt, params.iter().cloned());
query::query(client.clone(), stmt, buf)
.await?
.and_then(|row| future::ready(row.try_get(0)))
.try_collect()
.await
}
async fn typeinfo_enum_statement(client: &Arc<InnerClient>) -> Result<Statement, Error> {
if let Some(stmt) = client.typeinfo_enum() {
return Ok(stmt);
}
let stmt = match Box::pin(prepare(client.clone(), TYPEINFO_ENUM_QUERY, &[])).await {
Ok(stmt) => stmt,
Err(ref e) if e.code() == Some(&SqlState::UNDEFINED_COLUMN) => {
Box::pin(prepare(client.clone(), TYPEINFO_ENUM_FALLBACK_QUERY, &[])).await?
}
Err(e) => return Err(e),
};
client.set_typeinfo_enum(&stmt);
Ok(stmt)
}
async fn get_composite_fields(client: &Arc<InnerClient>, oid: Oid) -> Result<Vec<Field>, Error> {
let stmt = typeinfo_composite_statement(client).await?;
let params: &[&dyn ToSql] = &[&oid];
let buf = query::encode(&stmt, params.iter().cloned());
let rows = query::query(client.clone(), stmt, buf)
.await?
.try_collect::<Vec<_>>()
.await?;
let mut fields = vec![];
for row in rows {
let name = row.try_get(0)?;
let oid = row.try_get(1)?;
let type_ = Box::pin(get_type(client, oid)).await?;
fields.push(Field::new(name, type_));
}
Ok(fields)
}
async fn typeinfo_composite_statement(client: &Arc<InnerClient>) -> Result<Statement, Error> {
if let Some(stmt) = client.typeinfo_composite() {
return Ok(stmt);
}
let stmt = Box::pin(prepare(client.clone(), TYPEINFO_COMPOSITE_QUERY, &[])).await?;
client.set_typeinfo_composite(&stmt);
Ok(stmt)
} }

View File

@ -11,33 +11,21 @@ use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
pub async fn query<'a, I>( pub async fn query(
client: Arc<InnerClient>, client: Arc<InnerClient>,
statement: &Statement, statement: Statement,
params: I, buf: Result<Vec<u8>, Error>,
) -> Result<Query, Error> ) -> Result<Query, Error> {
where let responses = start(client, buf).await?;
I: IntoIterator<Item = &'a dyn ToSql>,
I::IntoIter: ExactSizeIterator,
{
let responses = start(&client, &statement, params).await?;
Ok(Query { Ok(Query {
statement: statement.clone(), statement,
responses, responses,
}) })
} }
pub async fn execute<'a, I>( pub async fn execute(client: Arc<InnerClient>, buf: Result<Vec<u8>, Error>) -> Result<u64, Error> {
client: Arc<InnerClient>, let mut responses = start(client, buf).await?;
statement: &Statement,
params: I,
) -> Result<u64, Error>
where
I: IntoIterator<Item = &'a dyn ToSql>,
I::IntoIter: ExactSizeIterator,
{
let mut responses = start(&client, &statement, params).await?;
loop { loop {
match responses.next().await? { match responses.next().await? {
@ -59,11 +47,19 @@ where
} }
} }
async fn start<'a, I>( async fn start(client: Arc<InnerClient>, buf: Result<Vec<u8>, Error>) -> Result<Responses, Error> {
client: &Arc<InnerClient>, let buf = buf?;
statement: &Statement, let mut responses = client.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?;
params: I,
) -> Result<Responses, Error> match responses.next().await? {
Message::BindComplete => {}
_ => return Err(Error::unexpected_message()),
}
Ok(responses)
}
pub fn encode<'a, I>(statement: &Statement, params: I) -> Result<Vec<u8>, Error>
where where
I: IntoIterator<Item = &'a dyn ToSql>, I: IntoIterator<Item = &'a dyn ToSql>,
I::IntoIter: ExactSizeIterator, I::IntoIter: ExactSizeIterator,
@ -105,14 +101,7 @@ where
frontend::execute("", 0, &mut buf).map_err(Error::encode)?; frontend::execute("", 0, &mut buf).map_err(Error::encode)?;
frontend::sync(&mut buf); frontend::sync(&mut buf);
let mut responses = client.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?; Ok(buf)
match responses.next().await? {
Message::BindComplete => {}
_ => return Err(Error::unexpected_message()),
}
Ok(responses)
} }
pub struct Query { pub struct Query {

View File

@ -107,7 +107,7 @@ impl fmt::Display for Type {
} }
impl Type { impl Type {
pub(crate) fn _new(name: String, oid: Oid, kind: Kind, schema: String) -> Type { pub(crate) fn new(name: String, oid: Oid, kind: Kind, schema: String) -> Type {
Type(Inner::Other(Arc::new(Other { Type(Inner::Other(Arc::new(Other {
name, name,
oid, oid,
@ -175,6 +175,10 @@ pub struct Field {
} }
impl Field { impl Field {
pub(crate) fn new(name: String, type_: Type) -> Field {
Field { name, type_ }
}
/// Returns the name of the field. /// Returns the name of the field.
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
&self.name &self.name
@ -186,12 +190,6 @@ impl Field {
} }
} }
impl Field {
pub(crate) fn new(name: String, type_: Type) -> Field {
Field { name, type_ }
}
}
/// An error indicating that a `NULL` Postgres value was passed to a `FromSql` /// An error indicating that a `NULL` Postgres value was passed to a `FromSql`
/// implementation that does not support `NULL` values. /// implementation that does not support `NULL` values.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -543,7 +541,7 @@ pub enum IsNull {
/// ///
/// `ToSql` is implemented for `Vec<T>` and `&[T]` where `T` implements `ToSql`, /// `ToSql` is implemented for `Vec<T>` and `&[T]` where `T` implements `ToSql`,
/// and corresponds to one-dimensional Postgres arrays with an index offset of 1. /// and corresponds to one-dimensional Postgres arrays with an index offset of 1.
pub trait ToSql: fmt::Debug + Sync + Send { pub trait ToSql: fmt::Debug {
/// Converts the value of `self` into the binary format of the specified /// Converts the value of `self` into the binary format of the specified
/// Postgres `Type`, appending it to `out`. /// Postgres `Type`, appending it to `out`.
/// ///
@ -744,7 +742,7 @@ simple_to!(f64, float8_to_sql, FLOAT8);
impl<H> ToSql for HashMap<String, Option<String>, H> impl<H> ToSql for HashMap<String, Option<String>, H>
where where
H: BuildHasher + Sync + Send, H: BuildHasher,
{ {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<dyn Error + Sync + Send>> { fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
types::hstore_to_sql( types::hstore_to_sql(

View File

@ -33,7 +33,7 @@ where
impl<T> ToSql for Json<T> impl<T> ToSql for Json<T>
where where
T: Serialize + Debug + Sync + Send, T: Serialize + Debug,
{ {
fn to_sql(&self, ty: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<dyn Error + Sync + Send>> { fn to_sql(&self, ty: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
if *ty == Type::JSONB { if *ty == Type::JSONB {

View File

@ -1,19 +1,17 @@
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
#![feature(async_await)] #![feature(async_await)]
use futures::{try_join, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; use futures::{try_join, FutureExt, TryFutureExt, TryStreamExt};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio_postgres::error::SqlState; use tokio_postgres::error::SqlState;
use tokio_postgres::tls::{NoTls, NoTlsStream}; use tokio_postgres::tls::{NoTls, NoTlsStream};
use tokio_postgres::types::Type; use tokio_postgres::types::{Kind, Type};
use tokio_postgres::{Client, Config, Connection, Error}; use tokio_postgres::{Client, Config, Connection, Error};
mod parse; mod parse;
#[cfg(feature = "runtime")] #[cfg(feature = "runtime")]
mod runtime; mod runtime;
/*
mod types; mod types;
*/
async fn connect_raw(s: &str) -> Result<(Client, Connection<TcpStream, NoTlsStream>), Error> { async fn connect_raw(s: &str) -> Result<(Client, Connection<TcpStream, NoTlsStream>), Error> {
let socket = TcpStream::connect(&"127.0.0.1:5433".parse().unwrap()) let socket = TcpStream::connect(&"127.0.0.1:5433".parse().unwrap())
@ -100,13 +98,13 @@ async fn scram_password_ok() {
async fn pipelined_prepare() { async fn pipelined_prepare() {
let mut client = connect("user=postgres").await; let mut client = connect("user=postgres").await;
let prepare1 = client.prepare("SELECT $1::TEXT"); let prepare1 = client.prepare("SELECT $1::HSTORE[]");
let prepare2 = client.prepare("SELECT $1::BIGINT"); let prepare2 = client.prepare("SELECT $1::BIGINT");
let (statement1, statement2) = try_join!(prepare1, prepare2).unwrap(); let (statement1, statement2) = try_join!(prepare1, prepare2).unwrap();
assert_eq!(statement1.params()[0], Type::TEXT); assert_eq!(statement1.params()[0].name(), "_hstore");
assert_eq!(statement1.columns()[0].type_(), &Type::TEXT); assert_eq!(statement1.columns()[0].type_().name(), "_hstore");
assert_eq!(statement2.params()[0], Type::INT8); assert_eq!(statement2.params()[0], Type::INT8);
assert_eq!(statement2.columns()[0].type_(), &Type::INT8); assert_eq!(statement2.columns()[0].type_(), &Type::INT8);
@ -140,6 +138,125 @@ async fn insert_select() {
assert_eq!(rows[1].get::<_, &str>(1), "bob"); assert_eq!(rows[1].get::<_, &str>(1), "bob");
} }
#[tokio::test]
async fn custom_enum() {
let mut client = connect("user=postgres").await;
let create = client
.prepare(
"CREATE TYPE pg_temp.mood AS ENUM (
'sad',
'ok',
'happy'
)",
)
.await
.unwrap();
client.execute(&create, &[]).await.unwrap();
let select = client.prepare("SELECT $1::mood").await.unwrap();
let ty = &select.params()[0];
assert_eq!("mood", ty.name());
assert_eq!(
&Kind::Enum(vec![
"sad".to_string(),
"ok".to_string(),
"happy".to_string(),
]),
ty.kind(),
);
}
#[tokio::test]
async fn custom_domain() {
let mut client = connect("user=postgres").await;
let create = client
.prepare("CREATE DOMAIN pg_temp.session_id AS bytea CHECK(octet_length(VALUE) = 16)")
.await
.unwrap();
client.execute(&create, &[]).await.unwrap();
let select = client.prepare("SELECT $1::session_id").await.unwrap();
let ty = &select.params()[0];
assert_eq!("session_id", ty.name());
assert_eq!(&Kind::Domain(Type::BYTEA), ty.kind());
}
#[tokio::test]
async fn custom_array() {
let mut client = connect("user=postgres").await;
let select = client.prepare("SELECT $1::HSTORE[]").await.unwrap();
let ty = &select.params()[0];
assert_eq!("_hstore", ty.name());
match ty.kind() {
Kind::Array(ty) => {
assert_eq!("hstore", ty.name());
assert_eq!(&Kind::Simple, ty.kind());
}
_ => panic!("unexpected kind"),
}
}
#[tokio::test]
async fn custom_composite() {
let mut client = connect("user=postgres").await;
let create = client
.prepare(
"CREATE TYPE pg_temp.inventory_item AS (
name TEXT,
supplier INTEGER,
price NUMERIC
)",
)
.await
.unwrap();
client.execute(&create, &[]).await.unwrap();
let select = client.prepare("SELECT $1::inventory_item").await.unwrap();
let ty = &select.params()[0];
assert_eq!(ty.name(), "inventory_item");
match ty.kind() {
Kind::Composite(fields) => {
assert_eq!(fields[0].name(), "name");
assert_eq!(fields[0].type_(), &Type::TEXT);
assert_eq!(fields[1].name(), "supplier");
assert_eq!(fields[1].type_(), &Type::INT4);
assert_eq!(fields[2].name(), "price");
assert_eq!(fields[2].type_(), &Type::NUMERIC);
}
_ => panic!("unexpected kind"),
}
}
#[tokio::test]
async fn custom_range() {
let mut client = connect("user=postgres").await;
let create = client
.prepare(
"CREATE TYPE pg_temp.floatrange AS RANGE (
subtype = float8,
subtype_diff = float8mi
)",
)
.await
.unwrap();
client.execute(&create, &[]).await.unwrap();
let select = client.prepare("SELECT $1::floatrange").await.unwrap();
let ty = &select.params()[0];
assert_eq!("floatrange", ty.name());
assert_eq!(&Kind::Range(Type::FLOAT8), ty.kind());
}
/* /*
#[test] #[test]
fn query_portal() { fn query_portal() {
@ -219,182 +336,6 @@ fn cancel_query_raw() {
let ((), ()) = runtime.block_on(sleep.join(cancel)).unwrap(); let ((), ()) = runtime.block_on(sleep.join(cancel)).unwrap();
} }
#[test]
fn custom_enum() {
let _ = env_logger::try_init();
let mut runtime = Runtime::new().unwrap();
let (mut client, connection) = runtime.block_on(connect("user=postgres")).unwrap();
let connection = connection.map_err(|e| panic!("{}", e));
runtime.handle().spawn(connection).unwrap();
runtime
.block_on(
client
.simple_query(
"CREATE TYPE pg_temp.mood AS ENUM (
'sad',
'ok',
'happy'
)",
)
.for_each(|_| Ok(())),
)
.unwrap();
let select = client.prepare("SELECT $1::mood");
let select = runtime.block_on(select).unwrap();
let ty = &select.params()[0];
assert_eq!("mood", ty.name());
assert_eq!(
&Kind::Enum(vec![
"sad".to_string(),
"ok".to_string(),
"happy".to_string(),
]),
ty.kind()
);
}
#[test]
fn custom_domain() {
let _ = env_logger::try_init();
let mut runtime = Runtime::new().unwrap();
let (mut client, connection) = runtime.block_on(connect("user=postgres")).unwrap();
let connection = connection.map_err(|e| panic!("{}", e));
runtime.handle().spawn(connection).unwrap();
runtime
.block_on(
client
.simple_query(
"CREATE DOMAIN pg_temp.session_id AS bytea CHECK(octet_length(VALUE) = 16)",
)
.for_each(|_| Ok(())),
)
.unwrap();
let select = client.prepare("SELECT $1::session_id");
let select = runtime.block_on(select).unwrap();
let ty = &select.params()[0];
assert_eq!("session_id", ty.name());
assert_eq!(&Kind::Domain(Type::BYTEA), ty.kind());
}
#[test]
fn custom_array() {
let _ = env_logger::try_init();
let mut runtime = Runtime::new().unwrap();
let (mut client, connection) = runtime.block_on(connect("user=postgres")).unwrap();
let connection = connection.map_err(|e| panic!("{}", e));
runtime.handle().spawn(connection).unwrap();
let select = client.prepare("SELECT $1::HSTORE[]");
let select = runtime.block_on(select).unwrap();
let ty = &select.params()[0];
assert_eq!("_hstore", ty.name());
match *ty.kind() {
Kind::Array(ref ty) => {
assert_eq!("hstore", ty.name());
assert_eq!(&Kind::Simple, ty.kind());
}
_ => panic!("unexpected kind"),
}
}
#[test]
fn custom_composite() {
let _ = env_logger::try_init();
let mut runtime = Runtime::new().unwrap();
let (mut client, connection) = runtime.block_on(connect("user=postgres")).unwrap();
let connection = connection.map_err(|e| panic!("{}", e));
runtime.handle().spawn(connection).unwrap();
runtime
.block_on(
client
.simple_query(
"CREATE TYPE pg_temp.inventory_item AS (
name TEXT,
supplier INTEGER,
price NUMERIC
)",
)
.for_each(|_| Ok(())),
)
.unwrap();
let select = client.prepare("SELECT $1::inventory_item");
let select = runtime.block_on(select).unwrap();
let ty = &select.params()[0];
assert_eq!(ty.name(), "inventory_item");
match *ty.kind() {
Kind::Composite(ref fields) => {
assert_eq!(fields[0].name(), "name");
assert_eq!(fields[0].type_(), &Type::TEXT);
assert_eq!(fields[1].name(), "supplier");
assert_eq!(fields[1].type_(), &Type::INT4);
assert_eq!(fields[2].name(), "price");
assert_eq!(fields[2].type_(), &Type::NUMERIC);
}
ref t => panic!("bad type {:?}", t),
}
}
#[test]
fn custom_range() {
let _ = env_logger::try_init();
let mut runtime = Runtime::new().unwrap();
let (mut client, connection) = runtime.block_on(connect("user=postgres")).unwrap();
let connection = connection.map_err(|e| panic!("{}", e));
runtime.handle().spawn(connection).unwrap();
runtime
.block_on(
client
.simple_query(
"CREATE TYPE pg_temp.floatrange AS RANGE (
subtype = float8,
subtype_diff = float8mi
)",
)
.for_each(|_| Ok(())),
)
.unwrap();
let select = client.prepare("SELECT $1::floatrange");
let select = runtime.block_on(select).unwrap();
let ty = &select.params()[0];
assert_eq!("floatrange", ty.name());
assert_eq!(&Kind::Range(Type::FLOAT8), ty.kind());
}
#[test]
fn custom_simple() {
let _ = env_logger::try_init();
let mut runtime = Runtime::new().unwrap();
let (mut client, connection) = runtime.block_on(connect("user=postgres")).unwrap();
let connection = connection.map_err(|e| panic!("{}", e));
runtime.handle().spawn(connection).unwrap();
let select = client.prepare("SELECT $1::HSTORE");
let select = runtime.block_on(select).unwrap();
let ty = &select.params()[0];
assert_eq!("hstore", ty.name());
assert_eq!(&Kind::Simple, ty.kind());
}
#[test] #[test]
fn notifications() { fn notifications() {
let _ = env_logger::try_init(); let _ = env_logger::try_init();

View File

@ -1,6 +1,5 @@
use futures::{Future, FutureExt, Stream}; use futures::{FutureExt, TryStreamExt};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use tokio::runtime::current_thread::Runtime;
use tokio::timer::Delay; use tokio::timer::Delay;
use tokio_postgres::error::SqlState; use tokio_postgres::error::SqlState;
use tokio_postgres::NoTls; use tokio_postgres::NoTls;
@ -10,10 +9,9 @@ async fn smoke_test(s: &str) {
let connection = connection.map(|e| e.unwrap()); let connection = connection.map(|e| e.unwrap());
tokio::spawn(connection); tokio::spawn(connection);
/* let stmt = client.prepare("SELECT $1::INT").await.unwrap();
let execute = client.simple_query("SELECT 1").for_each(|_| Ok(())); let rows = client.query(&stmt, &[&1i32]).await.unwrap().try_collect::<Vec<_>>().await.unwrap();
runtime.block_on(execute).unwrap(); assert_eq!(rows[0].get::<_, i32>(0), 1i32);
*/
} }
#[tokio::test] #[tokio::test]

View File

@ -2,28 +2,30 @@ use bit_vec_07::BitVec;
use crate::types::test_type; use crate::types::test_type;
#[test] #[tokio::test]
fn test_bit_params() { async fn test_bit_params() {
let mut bv = BitVec::from_bytes(&[0b0110_1001, 0b0000_0111]); let mut bv = BitVec::from_bytes(&[0b0110_1001, 0b0000_0111]);
bv.pop(); bv.pop();
bv.pop(); bv.pop();
test_type( test_type(
"BIT(14)", "BIT(14)",
&[(Some(bv), "B'01101001000001'"), (None, "NULL")], vec![(Some(bv), "B'01101001000001'"), (None, "NULL")],
) )
.await
} }
#[test] #[tokio::test]
fn test_varbit_params() { async fn test_varbit_params() {
let mut bv = BitVec::from_bytes(&[0b0110_1001, 0b0000_0111]); let mut bv = BitVec::from_bytes(&[0b0110_1001, 0b0000_0111]);
bv.pop(); bv.pop();
bv.pop(); bv.pop();
test_type( test_type(
"VARBIT", "VARBIT",
&[ vec![
(Some(bv), "B'01101001000001'"), (Some(bv), "B'01101001000001'"),
(Some(BitVec::from_bytes(&[])), "B''"), (Some(BitVec::from_bytes(&[])), "B''"),
(None, "NULL"), (None, "NULL"),
], ],
) )
.await
} }

View File

@ -3,8 +3,8 @@ use tokio_postgres::types::{Date, Timestamp};
use crate::types::test_type; use crate::types::test_type;
#[test] #[tokio::test]
fn test_naive_date_time_params() { async fn test_naive_date_time_params() {
fn make_check(time: &str) -> (Option<NaiveDateTime>, &str) { fn make_check(time: &str) -> (Option<NaiveDateTime>, &str) {
( (
Some(NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()), Some(NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()),
@ -13,17 +13,17 @@ fn test_naive_date_time_params() {
} }
test_type( test_type(
"TIMESTAMP", "TIMESTAMP",
&[ vec![
make_check("'1970-01-01 00:00:00.010000000'"), make_check("'1970-01-01 00:00:00.010000000'"),
make_check("'1965-09-25 11:19:33.100314000'"), make_check("'1965-09-25 11:19:33.100314000'"),
make_check("'2010-02-09 23:11:45.120200000'"), make_check("'2010-02-09 23:11:45.120200000'"),
(None, "NULL"), (None, "NULL"),
], ],
); ).await;
} }
#[test] #[tokio::test]
fn test_with_special_naive_date_time_params() { async fn test_with_special_naive_date_time_params() {
fn make_check(time: &str) -> (Timestamp<NaiveDateTime>, &str) { fn make_check(time: &str) -> (Timestamp<NaiveDateTime>, &str) {
( (
Timestamp::Value( Timestamp::Value(
@ -34,18 +34,19 @@ fn test_with_special_naive_date_time_params() {
} }
test_type( test_type(
"TIMESTAMP", "TIMESTAMP",
&[ vec![
make_check("'1970-01-01 00:00:00.010000000'"), make_check("'1970-01-01 00:00:00.010000000'"),
make_check("'1965-09-25 11:19:33.100314000'"), make_check("'1965-09-25 11:19:33.100314000'"),
make_check("'2010-02-09 23:11:45.120200000'"), make_check("'2010-02-09 23:11:45.120200000'"),
(Timestamp::PosInfinity, "'infinity'"), (Timestamp::PosInfinity, "'infinity'"),
(Timestamp::NegInfinity, "'-infinity'"), (Timestamp::NegInfinity, "'-infinity'"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_date_time_params() { async fn test_date_time_params() {
fn make_check(time: &str) -> (Option<DateTime<Utc>>, &str) { fn make_check(time: &str) -> (Option<DateTime<Utc>>, &str) {
( (
Some( Some(
@ -57,17 +58,18 @@ fn test_date_time_params() {
} }
test_type( test_type(
"TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH TIME ZONE",
&[ vec![
make_check("'1970-01-01 00:00:00.010000000'"), make_check("'1970-01-01 00:00:00.010000000'"),
make_check("'1965-09-25 11:19:33.100314000'"), make_check("'1965-09-25 11:19:33.100314000'"),
make_check("'2010-02-09 23:11:45.120200000'"), make_check("'2010-02-09 23:11:45.120200000'"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_with_special_date_time_params() { async fn test_with_special_date_time_params() {
fn make_check(time: &str) -> (Timestamp<DateTime<Utc>>, &str) { fn make_check(time: &str) -> (Timestamp<DateTime<Utc>>, &str) {
( (
Timestamp::Value( Timestamp::Value(
@ -79,18 +81,19 @@ fn test_with_special_date_time_params() {
} }
test_type( test_type(
"TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH TIME ZONE",
&[ vec![
make_check("'1970-01-01 00:00:00.010000000'"), make_check("'1970-01-01 00:00:00.010000000'"),
make_check("'1965-09-25 11:19:33.100314000'"), make_check("'1965-09-25 11:19:33.100314000'"),
make_check("'2010-02-09 23:11:45.120200000'"), make_check("'2010-02-09 23:11:45.120200000'"),
(Timestamp::PosInfinity, "'infinity'"), (Timestamp::PosInfinity, "'infinity'"),
(Timestamp::NegInfinity, "'-infinity'"), (Timestamp::NegInfinity, "'-infinity'"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_date_params() { async fn test_date_params() {
fn make_check(time: &str) -> (Option<NaiveDate>, &str) { fn make_check(time: &str) -> (Option<NaiveDate>, &str) {
( (
Some(NaiveDate::parse_from_str(time, "'%Y-%m-%d'").unwrap()), Some(NaiveDate::parse_from_str(time, "'%Y-%m-%d'").unwrap()),
@ -99,17 +102,18 @@ fn test_date_params() {
} }
test_type( test_type(
"DATE", "DATE",
&[ vec![
make_check("'1970-01-01'"), make_check("'1970-01-01'"),
make_check("'1965-09-25'"), make_check("'1965-09-25'"),
make_check("'2010-02-09'"), make_check("'2010-02-09'"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_with_special_date_params() { async fn test_with_special_date_params() {
fn make_check(date: &str) -> (Date<NaiveDate>, &str) { fn make_check(date: &str) -> (Date<NaiveDate>, &str) {
( (
Date::Value(NaiveDate::parse_from_str(date, "'%Y-%m-%d'").unwrap()), Date::Value(NaiveDate::parse_from_str(date, "'%Y-%m-%d'").unwrap()),
@ -118,18 +122,19 @@ fn test_with_special_date_params() {
} }
test_type( test_type(
"DATE", "DATE",
&[ vec![
make_check("'1970-01-01'"), make_check("'1970-01-01'"),
make_check("'1965-09-25'"), make_check("'1965-09-25'"),
make_check("'2010-02-09'"), make_check("'2010-02-09'"),
(Date::PosInfinity, "'infinity'"), (Date::PosInfinity, "'infinity'"),
(Date::NegInfinity, "'-infinity'"), (Date::NegInfinity, "'-infinity'"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_time_params() { async fn test_time_params() {
fn make_check(time: &str) -> (Option<NaiveTime>, &str) { fn make_check(time: &str) -> (Option<NaiveTime>, &str) {
( (
Some(NaiveTime::parse_from_str(time, "'%H:%M:%S.%f'").unwrap()), Some(NaiveTime::parse_from_str(time, "'%H:%M:%S.%f'").unwrap()),
@ -138,11 +143,12 @@ fn test_time_params() {
} }
test_type( test_type(
"TIME", "TIME",
&[ vec![
make_check("'00:00:00.010000000'"), make_check("'00:00:00.010000000'"),
make_check("'11:19:33.100314000'"), make_check("'11:19:33.100314000'"),
make_check("'23:11:45.120200000'"), make_check("'23:11:45.120200000'"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }

View File

@ -2,11 +2,11 @@ use eui48_04::MacAddress;
use crate::types::test_type; use crate::types::test_type;
#[test] #[tokio::test]
fn test_eui48_params() { async fn test_eui48_params() {
test_type( test_type(
"MACADDR", "MACADDR",
&[ vec![
( (
Some(MacAddress::parse_str("12-34-56-AB-CD-EF").unwrap()), Some(MacAddress::parse_str("12-34-56-AB-CD-EF").unwrap()),
"'12-34-56-ab-cd-ef'", "'12-34-56-ab-cd-ef'",
@ -14,4 +14,5 @@ fn test_eui48_params() {
(None, "NULL"), (None, "NULL"),
], ],
) )
.await
} }

View File

@ -2,23 +2,24 @@ use geo_010::{Coordinate, LineString, Point, Rect};
use crate::types::test_type; use crate::types::test_type;
#[test] #[tokio::test]
fn test_point_params() { async fn test_point_params() {
test_type( test_type(
"POINT", "POINT",
&[ vec![
(Some(Point::new(0.0, 0.0)), "POINT(0, 0)"), (Some(Point::new(0.0, 0.0)), "POINT(0, 0)"),
(Some(Point::new(-3.14, 1.618)), "POINT(-3.14, 1.618)"), (Some(Point::new(-3.14, 1.618)), "POINT(-3.14, 1.618)"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_box_params() { async fn test_box_params() {
test_type( test_type(
"BOX", "BOX",
&[ vec![
( (
Some(Rect { Some(Rect {
min: Coordinate { x: -3.14, y: 1.618 }, min: Coordinate { x: -3.14, y: 1.618 },
@ -31,11 +32,12 @@ fn test_box_params() {
), ),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_path_params() { async fn test_path_params() {
let points = vec![ let points = vec![
Coordinate { x: 0., y: 0. }, Coordinate { x: 0., y: 0. },
Coordinate { x: -3.14, y: 1.618 }, Coordinate { x: -3.14, y: 1.618 },
@ -46,12 +48,13 @@ fn test_path_params() {
]; ];
test_type( test_type(
"PATH", "PATH",
&[ vec![
( (
Some(LineString(points)), Some(LineString(points)),
"path '((0, 0), (-3.14, 1.618), (160.0, 69701.5615))'", "path '((0, 0), (-3.14, 1.618), (160.0, 69701.5615))'",
), ),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }

View File

@ -1,4 +1,4 @@
use futures::{Future, Stream}; use futures::{TryStreamExt};
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::f32; use std::f32;
@ -7,7 +7,6 @@ use std::fmt;
use std::net::IpAddr; use std::net::IpAddr;
use std::result; use std::result;
use std::time::{Duration, UNIX_EPOCH}; use std::time::{Duration, UNIX_EPOCH};
use tokio::runtime::current_thread::Runtime;
use tokio_postgres::to_sql_checked; use tokio_postgres::to_sql_checked;
use tokio_postgres::types::{FromSql, FromSqlOwned, IsNull, Kind, ToSql, Type, WrongType}; use tokio_postgres::types::{FromSql, FromSqlOwned, IsNull, Kind, ToSql, Type, WrongType};
@ -26,53 +25,63 @@ mod serde_json_1;
#[cfg(feature = "with-uuid-0_7")] #[cfg(feature = "with-uuid-0_7")]
mod uuid_07; mod uuid_07;
fn test_type<T, S>(sql_type: &str, checks: &[(T, S)]) async fn test_type<T, S>(sql_type: &str, checks: Vec<(T, S)>)
where where
T: PartialEq + for<'a> FromSqlOwned + ToSql, T: PartialEq + for<'a> FromSqlOwned + ToSql,
S: fmt::Display, S: fmt::Display,
{ {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let handshake = connect("user=postgres"); for (val, repr) in checks {
let (mut client, connection) = runtime.block_on(handshake).unwrap(); let stmt = client
let connection = connection.map_err(|e| panic!("{}", e)); .prepare(&format!("SELECT {}::{}", repr, sql_type))
runtime.spawn(connection); .await
.unwrap();
for &(ref val, ref repr) in checks.iter() { let rows = client
let prepare = client.prepare(&format!("SELECT {}::{}", repr, sql_type)); .query(&stmt, &[])
let stmt = runtime.block_on(prepare).unwrap(); .await
let query = client.query(&stmt, &[]).collect(); .unwrap()
let rows = runtime.block_on(query).unwrap(); .try_collect::<Vec<_>>()
.await
.unwrap();
let result = rows[0].get(0); let result = rows[0].get(0);
assert_eq!(val, &result); assert_eq!(val, result);
let prepare = client.prepare(&format!("SELECT $1::{}", sql_type)); let stmt = client
let stmt = runtime.block_on(prepare).unwrap(); .prepare(&format!("SELECT $1::{}", sql_type))
let query = client.query(&stmt, &[val]).collect(); .await
let rows = runtime.block_on(query).unwrap(); .unwrap();
let rows = client
.query(&stmt, &[&val])
.await
.unwrap()
.try_collect::<Vec<_>>()
.await
.unwrap();
let result = rows[0].get(0); let result = rows[0].get(0);
assert_eq!(val, &result); assert_eq!(val, result);
} }
} }
#[test] #[tokio::test]
fn test_bool_params() { async fn test_bool_params() {
test_type( test_type(
"BOOL", "BOOL",
&[(Some(true), "'t'"), (Some(false), "'f'"), (None, "NULL")], vec![(Some(true), "'t'"), (Some(false), "'f'"), (None, "NULL")],
); )
.await;
} }
#[test] #[tokio::test]
fn test_i8_params() { async fn test_i8_params() {
test_type("\"char\"", &[(Some('a' as i8), "'a'"), (None, "NULL")]); test_type("\"char\"", vec![(Some('a' as i8), "'a'"), (None, "NULL")]).await;
} }
#[test] #[tokio::test]
fn test_name_params() { async fn test_name_params() {
test_type( test_type(
"NAME", "NAME",
&[ vec![
(Some("hello world".to_owned()), "'hello world'"), (Some("hello world".to_owned()), "'hello world'"),
( (
Some("イロハニホヘト チリヌルヲ".to_owned()), Some("イロハニホヘト チリヌルヲ".to_owned()),
@ -80,88 +89,95 @@ fn test_name_params() {
), ),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_i16_params() { async fn test_i16_params() {
test_type( test_type(
"SMALLINT", "SMALLINT",
&[ vec![
(Some(15001i16), "15001"), (Some(15001i16), "15001"),
(Some(-15001i16), "-15001"), (Some(-15001i16), "-15001"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_i32_params() { async fn test_i32_params() {
test_type( test_type(
"INT", "INT",
&[ vec![
(Some(2_147_483_548i32), "2147483548"), (Some(2_147_483_548i32), "2147483548"),
(Some(-2_147_483_548i32), "-2147483548"), (Some(-2_147_483_548i32), "-2147483548"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_oid_params() { async fn test_oid_params() {
test_type( test_type(
"OID", "OID",
&[ vec![
(Some(2_147_483_548u32), "2147483548"), (Some(2_147_483_548u32), "2147483548"),
(Some(4_000_000_000), "4000000000"), (Some(4_000_000_000), "4000000000"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_i64_params() { async fn test_i64_params() {
test_type( test_type(
"BIGINT", "BIGINT",
&[ vec![
(Some(9_223_372_036_854_775_708i64), "9223372036854775708"), (Some(9_223_372_036_854_775_708i64), "9223372036854775708"),
(Some(-9_223_372_036_854_775_708i64), "-9223372036854775708"), (Some(-9_223_372_036_854_775_708i64), "-9223372036854775708"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_f32_params() { async fn test_f32_params() {
test_type( test_type(
"REAL", "REAL",
&[ vec![
(Some(f32::INFINITY), "'infinity'"), (Some(f32::INFINITY), "'infinity'"),
(Some(f32::NEG_INFINITY), "'-infinity'"), (Some(f32::NEG_INFINITY), "'-infinity'"),
(Some(1000.55), "1000.55"), (Some(1000.55), "1000.55"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_f64_params() { async fn test_f64_params() {
test_type( test_type(
"DOUBLE PRECISION", "DOUBLE PRECISION",
&[ vec![
(Some(f64::INFINITY), "'infinity'"), (Some(f64::INFINITY), "'infinity'"),
(Some(f64::NEG_INFINITY), "'-infinity'"), (Some(f64::NEG_INFINITY), "'-infinity'"),
(Some(10000.55), "10000.55"), (Some(10000.55), "10000.55"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_varchar_params() { async fn test_varchar_params() {
test_type( test_type(
"VARCHAR", "VARCHAR",
&[ vec![
(Some("hello world".to_owned()), "'hello world'"), (Some("hello world".to_owned()), "'hello world'"),
( (
Some("イロハニホヘト チリヌルヲ".to_owned()), Some("イロハニホヘト チリヌルヲ".to_owned()),
@ -169,14 +185,15 @@ fn test_varchar_params() {
), ),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_text_params() { async fn test_text_params() {
test_type( test_type(
"TEXT", "TEXT",
&[ vec![
(Some("hello world".to_owned()), "'hello world'"), (Some("hello world".to_owned()), "'hello world'"),
( (
Some("イロハニホヘト チリヌルヲ".to_owned()), Some("イロハニホヘト チリヌルヲ".to_owned()),
@ -184,138 +201,149 @@ fn test_text_params() {
), ),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_borrowed_text() { async fn test_borrowed_text() {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let handshake = connect("user=postgres"); let stmt = client.prepare("SELECT 'foo'").await.unwrap();
let (mut client, connection) = runtime.block_on(handshake).unwrap(); let rows = client
let connection = connection.map_err(|e| panic!("{}", e)); .query(&stmt, &[])
runtime.spawn(connection); .await
.unwrap()
let prepare = client.prepare("SELECT 'foo'"); .try_collect::<Vec<_>>()
let stmt = runtime.block_on(prepare).unwrap(); .await
let query = client.query(&stmt, &[]).collect(); .unwrap();
let rows = runtime.block_on(query).unwrap();
let s: &str = rows[0].get(0); let s: &str = rows[0].get(0);
assert_eq!(s, "foo"); assert_eq!(s, "foo");
} }
#[test] #[tokio::test]
fn test_bpchar_params() { async fn test_bpchar_params() {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let handshake = connect("user=postgres"); let stmt = client
let (mut client, connection) = runtime.block_on(handshake).unwrap(); .prepare(
let connection = connection.map_err(|e| panic!("{}", e));
runtime.spawn(connection);
let batch = client
.simple_query(
"CREATE TEMPORARY TABLE foo ( "CREATE TEMPORARY TABLE foo (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
b CHAR(5) b CHAR(5)
)", )",
) )
.for_each(|_| Ok(())); .await
runtime.block_on(batch).unwrap(); .unwrap();
client.execute(&stmt, &[]).await.unwrap();
let prepare = client.prepare("INSERT INTO foo (b) VALUES ($1), ($2), ($3)"); let stmt = client
let stmt = runtime.block_on(prepare).unwrap(); .prepare("INSERT INTO foo (b) VALUES ($1), ($2), ($3)")
let execute = client.execute(&stmt, &[&"12345", &"123", &None::<&'static str>]); .await
runtime.block_on(execute).unwrap(); .unwrap();
client
.execute(&stmt, &[&"12345", &"123", &None::<&'static str>])
.await
.unwrap();
let prepare = client.prepare("SELECT b FROM foo ORDER BY id"); let stmt = client
let stmt = runtime.block_on(prepare).unwrap(); .prepare("SELECT b FROM foo ORDER BY id")
let query = client.query(&stmt, &[]).collect(); .await
let res = runtime.block_on(query).unwrap(); .unwrap();
let rows = client
.query(&stmt, &[])
.await
.unwrap()
.map_ok(|row| row.get(0))
.try_collect::<Vec<Option<String>>>()
.await
.unwrap();
assert_eq!( assert_eq!(
vec![Some("12345".to_owned()), Some("123 ".to_owned()), None], vec![Some("12345".to_owned()), Some("123 ".to_owned()), None],
res.iter().map(|row| row.get(0)).collect::<Vec<_>>() rows,
); );
} }
#[test] #[tokio::test]
fn test_citext_params() { async fn test_citext_params() {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let handshake = connect("user=postgres"); let stmt = client
let (mut client, connection) = runtime.block_on(handshake).unwrap(); .prepare(
let connection = connection.map_err(|e| panic!("{}", e));
runtime.spawn(connection);
let batch = client
.simple_query(
"CREATE TEMPORARY TABLE foo ( "CREATE TEMPORARY TABLE foo (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
b CITEXT b CITEXT
)", )",
) )
.for_each(|_| Ok(())); .await
runtime.block_on(batch).unwrap(); .unwrap();
client.execute(&stmt, &[]).await.unwrap();
let prepare = client.prepare("INSERT INTO foo (b) VALUES ($1), ($2), ($3)"); let stmt = client
let stmt = runtime.block_on(prepare).unwrap(); .prepare("INSERT INTO foo (b) VALUES ($1), ($2), ($3)")
let execute = client.execute(&stmt, &[&"foobar", &"FooBar", &None::<&'static str>]); .await
runtime.block_on(execute).unwrap(); .unwrap();
client
.execute(&stmt, &[&"foobar", &"FooBar", &None::<&'static str>])
.await
.unwrap();
let prepare = client.prepare("SELECT b FROM foo WHERE b = 'FOOBAR' ORDER BY id"); let stmt = client
let stmt = runtime.block_on(prepare).unwrap(); .prepare("SELECT b FROM foo WHERE b = 'FOOBAR' ORDER BY id")
let query = client.query(&stmt, &[]).collect(); .await
let res = runtime.block_on(query).unwrap(); .unwrap();
let rows = client
.query(&stmt, &[])
.await
.unwrap()
.map_ok(|row| row.get(0))
.try_collect::<Vec<String>>()
.await
.unwrap();
assert_eq!( assert_eq!(vec!["foobar".to_string(), "FooBar".to_string()], rows,);
vec!["foobar".to_string(), "FooBar".to_string()],
res.iter()
.map(|row| row.get::<_, String>(0))
.collect::<Vec<_>>()
);
} }
#[test] #[tokio::test]
fn test_bytea_params() { async fn test_bytea_params() {
test_type( test_type(
"BYTEA", "BYTEA",
&[ vec![
(Some(vec![0u8, 1, 2, 3, 254, 255]), "'\\x00010203feff'"), (Some(vec![0u8, 1, 2, 3, 254, 255]), "'\\x00010203feff'"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_borrowed_bytea() { async fn test_borrowed_bytea() {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let stmt = client.prepare("SELECT 'foo'::BYTEA").await.unwrap();
let handshake = connect("user=postgres"); let rows = client
let (mut client, connection) = runtime.block_on(handshake).unwrap(); .query(&stmt, &[])
let connection = connection.map_err(|e| panic!("{}", e)); .await
runtime.spawn(connection); .unwrap()
.try_collect::<Vec<_>>()
let prepare = client.prepare("SELECT 'foo'::BYTEA"); .await
let stmt = runtime.block_on(prepare).unwrap(); .unwrap();
let query = client.query(&stmt, &[]).collect();
let rows = runtime.block_on(query).unwrap();
let s: &[u8] = rows[0].get(0); let s: &[u8] = rows[0].get(0);
assert_eq!(s, b"foo"); assert_eq!(s, b"foo");
} }
#[test] macro_rules! make_map {
fn test_hstore_params() { ($($k:expr => $v:expr),+) => ({
macro_rules! make_map { let mut map = HashMap::new();
($($k:expr => $v:expr),+) => ({ $(map.insert($k, $v);)+
let mut map = HashMap::new(); map
$(map.insert($k, $v);)+ })
map }
})
} #[tokio::test]
async fn test_hstore_params() {
test_type( test_type(
"hstore", "hstore",
&[ vec![
( (
Some(make_map!("a".to_owned() => Some("1".to_owned()))), Some(make_map!("a".to_owned() => Some("1".to_owned()))),
"'a=>1'", "'a=>1'",
@ -328,149 +356,149 @@ fn test_hstore_params() {
), ),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn test_array_params() { async fn test_array_params() {
test_type( test_type(
"integer[]", "integer[]",
&[ vec![
(Some(vec![1i32, 2i32]), "ARRAY[1,2]"), (Some(vec![1i32, 2i32]), "ARRAY[1,2]"),
(Some(vec![1i32]), "ARRAY[1]"), (Some(vec![1i32]), "ARRAY[1]"),
(Some(vec![]), "ARRAY[]"), (Some(vec![]), "ARRAY[]"),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[allow(clippy::eq_op)] #[allow(clippy::eq_op)]
fn test_nan_param<T>(sql_type: &str) async fn test_nan_param<T>(sql_type: &str)
where where
T: PartialEq + ToSql + FromSqlOwned, T: PartialEq + ToSql + FromSqlOwned,
{ {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let handshake = connect("user=postgres"); let stmt = client
let (mut client, connection) = runtime.block_on(handshake).unwrap(); .prepare(&format!("SELECT 'NaN'::{}", sql_type))
let connection = connection.map_err(|e| panic!("{}", e)); .await
runtime.spawn(connection); .unwrap();
let rows = client
let prepare = client.prepare(&format!("SELECT 'NaN'::{}", sql_type)); .query(&stmt, &[])
let stmt = runtime.block_on(prepare).unwrap(); .await
let query = client.query(&stmt, &[]).collect(); .unwrap()
let rows = runtime.block_on(query).unwrap(); .try_collect::<Vec<_>>()
.await
.unwrap();
let val: T = rows[0].get(0); let val: T = rows[0].get(0);
assert!(val != val); assert!(val != val);
} }
#[test] #[tokio::test]
fn test_f32_nan_param() { async fn test_f32_nan_param() {
test_nan_param::<f32>("REAL"); test_nan_param::<f32>("REAL").await;
} }
#[test] #[tokio::test]
fn test_f64_nan_param() { async fn test_f64_nan_param() {
test_nan_param::<f64>("DOUBLE PRECISION"); test_nan_param::<f64>("DOUBLE PRECISION").await;
} }
#[test] #[tokio::test]
fn test_pg_database_datname() { async fn test_pg_database_datname() {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let stmt = client
let handshake = connect("user=postgres"); .prepare("SELECT datname FROM pg_database")
let (mut client, connection) = runtime.block_on(handshake).unwrap(); .await
let connection = connection.map_err(|e| panic!("{}", e)); .unwrap();
runtime.spawn(connection); let rows = client
.query(&stmt, &[])
let prepare = client.prepare("SELECT datname FROM pg_database"); .await
let stmt = runtime.block_on(prepare).unwrap(); .unwrap()
let query = client.query(&stmt, &[]).collect(); .try_collect::<Vec<_>>()
let rows = runtime.block_on(query).unwrap(); .await
.unwrap();
assert_eq!(rows[0].get::<_, &str>(0), "postgres"); assert_eq!(rows[0].get::<_, &str>(0), "postgres");
} }
#[test] #[tokio::test]
fn test_slice() { async fn test_slice() {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let handshake = connect("user=postgres"); let stmt = client
let (mut client, connection) = runtime.block_on(handshake).unwrap(); .prepare(
let connection = connection.map_err(|e| panic!("{}", e));
runtime.spawn(connection);
let batch = client
.simple_query(
"CREATE TEMPORARY TABLE foo ( "CREATE TEMPORARY TABLE foo (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
f TEXT f TEXT
); )",
INSERT INTO foo(f) VALUES ('a'), ('b'), ('c'), ('d');",
) )
.for_each(|_| Ok(())); .await
runtime.block_on(batch).unwrap(); .unwrap();
client.execute(&stmt, &[]).await.unwrap();
let prepare = client.prepare("SELECT f FROM foo WHERE id = ANY($1)"); let stmt = client
let stmt = runtime.block_on(prepare).unwrap(); .prepare("INSERT INTO foo (f) VALUES ('a'), ('b'), ('c'), ('d')")
let query = client .await
.unwrap();
client.execute(&stmt, &[]).await.unwrap();
let stmt = client
.prepare("SELECT f FROM foo WHERE id = ANY($1)")
.await
.unwrap();
let rows = client
.query(&stmt, &[&&[1i32, 3, 4][..]]) .query(&stmt, &[&&[1i32, 3, 4][..]])
.map(|r| r.get::<_, String>(0)) .await
.collect(); .unwrap()
let rows = runtime.block_on(query).unwrap(); .map_ok(|r| r.get(0))
.try_collect::<Vec<String>>()
.await
.unwrap();
assert_eq!(vec!["a".to_owned(), "c".to_owned(), "d".to_owned()], rows); assert_eq!(vec!["a".to_owned(), "c".to_owned(), "d".to_owned()], rows);
} }
#[test] #[tokio::test]
fn test_slice_wrong_type() { async fn test_slice_wrong_type() {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let handshake = connect("user=postgres"); let stmt = client
let (mut client, connection) = runtime.block_on(handshake).unwrap(); .prepare(
let connection = connection.map_err(|e| panic!("{}", e));
runtime.spawn(connection);
let batch = client
.simple_query(
"CREATE TEMPORARY TABLE foo ( "CREATE TEMPORARY TABLE foo (
id SERIAL PRIMARY KEY id SERIAL PRIMARY KEY
)", )",
) )
.for_each(|_| Ok(())); .await
runtime.block_on(batch).unwrap(); .unwrap();
client.execute(&stmt, &[]).await.unwrap();
let prepare = client.prepare("SELECT * FROM foo WHERE id = ANY($1)"); let stmt = client
let stmt = runtime.block_on(prepare).unwrap(); .prepare("SELECT * FROM foo WHERE id = ANY($1)")
let query = client.query(&stmt, &[&&[&"hi"][..]]).collect(); .await
let err = runtime.block_on(query).err().unwrap(); .unwrap();
let err = client.query(&stmt, &[&&[&"hi"][..]]).await.err().unwrap();
match err.source() { match err.source() {
Some(e) if e.is::<WrongType>() => {} Some(e) if e.is::<WrongType>() => {}
_ => panic!("Unexpected error {:?}", err), _ => panic!("Unexpected error {:?}", err),
}; };
} }
#[test] #[tokio::test]
fn test_slice_range() { async fn test_slice_range() {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let handshake = connect("user=postgres"); let stmt = client.prepare("SELECT $1::INT8RANGE").await.unwrap();
let (mut client, connection) = runtime.block_on(handshake).unwrap(); let err = client.query(&stmt, &[&&[&1i64][..]]).await.err().unwrap();
let connection = connection.map_err(|e| panic!("{}", e));
runtime.spawn(connection);
let prepare = client.prepare("SELECT $1::INT8RANGE");
let stmt = runtime.block_on(prepare).unwrap();
let query = client.query(&stmt, &[&&[&1i64][..]]).collect();
let err = runtime.block_on(query).err().unwrap();
match err.source() { match err.source() {
Some(e) if e.is::<WrongType>() => {} Some(e) if e.is::<WrongType>() => {}
_ => panic!("Unexpected error {:?}", err), _ => panic!("Unexpected error {:?}", err),
}; };
} }
#[test] #[tokio::test]
fn domain() { async fn domain() {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
struct SessionId(Vec<u8>); struct SessionId(Vec<u8>);
@ -509,57 +537,56 @@ fn domain() {
} }
} }
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let handshake = connect("user=postgres"); let stmt = client
let (mut client, connection) = runtime.block_on(handshake).unwrap(); .prepare("CREATE DOMAIN pg_temp.session_id AS bytea CHECK(octet_length(VALUE) = 16)")
let connection = connection.map_err(|e| panic!("{}", e)); .await
runtime.spawn(connection); .unwrap();
client.execute(&stmt, &[]).await.unwrap();
let batch = client let stmt = client
.simple_query( .prepare("CREATE TABLE pg_temp.foo (id pg_temp.session_id)")
"CREATE DOMAIN pg_temp.session_id AS bytea CHECK(octet_length(VALUE) = 16); .await
CREATE TABLE pg_temp.foo (id pg_temp.session_id);", .unwrap();
) client.execute(&stmt, &[]).await.unwrap();
.for_each(|_| Ok(()));
runtime.block_on(batch).unwrap();
let id = SessionId(b"0123456789abcdef".to_vec()); let id = SessionId(b"0123456789abcdef".to_vec());
let prepare = client.prepare("INSERT INTO pg_temp.foo (id) VALUES ($1)"); let stmt = client
let stmt = runtime.block_on(prepare).unwrap(); .prepare("INSERT INTO pg_temp.foo (id) VALUES ($1)")
let execute = client.execute(&stmt, &[&id]); .await
runtime.block_on(execute).unwrap(); .unwrap();
client.execute(&stmt, &[&id]).await.unwrap();
let prepare = client.prepare("SELECT id FROM pg_temp.foo"); let stmt = client.prepare("SELECT id FROM pg_temp.foo").await.unwrap();
let stmt = runtime.block_on(prepare).unwrap(); let rows = client
let query = client.query(&stmt, &[]).collect(); .query(&stmt, &[])
let rows = runtime.block_on(query).unwrap(); .await
.unwrap()
.try_collect::<Vec<_>>()
.await
.unwrap();
assert_eq!(id, rows[0].get(0)); assert_eq!(id, rows[0].get(0));
} }
#[test] #[tokio::test]
fn composite() { async fn composite() {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let handshake = connect("user=postgres"); let stmt = client
let (mut client, connection) = runtime.block_on(handshake).unwrap(); .prepare(
let connection = connection.map_err(|e| panic!("{}", e));
runtime.spawn(connection);
let batch = client
.simple_query(
"CREATE TYPE pg_temp.inventory_item AS ( "CREATE TYPE pg_temp.inventory_item AS (
name TEXT, name TEXT,
supplier INTEGER, supplier INTEGER,
price NUMERIC price NUMERIC
)", )",
) )
.for_each(|_| Ok(())); .await
runtime.block_on(batch).unwrap(); .unwrap();
client.execute(&stmt, &[]).await.unwrap();
let prepare = client.prepare("SELECT $1::inventory_item"); let stmt = client.prepare("SELECT $1::inventory_item").await.unwrap();
let stmt = runtime.block_on(prepare).unwrap();
let type_ = &stmt.params()[0]; let type_ = &stmt.params()[0];
assert_eq!(type_.name(), "inventory_item"); assert_eq!(type_.name(), "inventory_item");
match *type_.kind() { match *type_.kind() {
@ -575,22 +602,17 @@ fn composite() {
} }
} }
#[test] #[tokio::test]
fn enum_() { async fn enum_() {
let mut runtime = Runtime::new().unwrap(); let mut client = connect("user=postgres").await;
let handshake = connect("user=postgres"); let stmt = client
let (mut client, connection) = runtime.block_on(handshake).unwrap(); .prepare("CREATE TYPE pg_temp.mood AS ENUM ('sad', 'ok', 'happy')")
let connection = connection.map_err(|e| panic!("{}", e)); .await
runtime.spawn(connection); .unwrap();
client.execute(&stmt, &[]).await.unwrap();
let batch = client let stmt = client.prepare("SELECT $1::mood").await.unwrap();
.simple_query("CREATE TYPE pg_temp.mood AS ENUM ('sad', 'ok', 'happy');")
.for_each(|_| Ok(()));
runtime.block_on(batch).unwrap();
let prepare = client.prepare("SELECT $1::mood");
let stmt = runtime.block_on(prepare).unwrap();
let type_ = &stmt.params()[0]; let type_ = &stmt.params()[0];
assert_eq!(type_.name(), "mood"); assert_eq!(type_.name(), "mood");
match *type_.kind() { match *type_.kind() {
@ -604,11 +626,11 @@ fn enum_() {
} }
} }
#[test] #[tokio::test]
fn system_time() { async fn system_time() {
test_type( test_type(
"TIMESTAMP", "TIMESTAMP",
&[ vec![
( (
Some(UNIX_EPOCH + Duration::from_millis(1_010)), Some(UNIX_EPOCH + Duration::from_millis(1_010)),
"'1970-01-01 00:00:01.01'", "'1970-01-01 00:00:01.01'",
@ -623,14 +645,15 @@ fn system_time() {
), ),
(None, "NULL"), (None, "NULL"),
], ],
); )
.await;
} }
#[test] #[tokio::test]
fn inet() { async fn inet() {
test_type( test_type(
"INET", "INET",
&[ vec![
(Some("127.0.0.1".parse::<IpAddr>().unwrap()), "'127.0.0.1'"), (Some("127.0.0.1".parse::<IpAddr>().unwrap()), "'127.0.0.1'"),
( (
Some("127.0.0.1".parse::<IpAddr>().unwrap()), Some("127.0.0.1".parse::<IpAddr>().unwrap()),
@ -653,5 +676,6 @@ fn inet() {
"'2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128'", "'2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128'",
), ),
], ],
); )
.await;
} }

View File

@ -2,11 +2,11 @@ use serde_json_1::Value;
use crate::types::test_type; use crate::types::test_type;
#[test] #[tokio::test]
fn test_json_params() { async fn test_json_params() {
test_type( test_type(
"JSON", "JSON",
&[ vec![
( (
Some(serde_json_1::from_str::<Value>("[10, 11, 12]").unwrap()), Some(serde_json_1::from_str::<Value>("[10, 11, 12]").unwrap()),
"'[10, 11, 12]'", "'[10, 11, 12]'",
@ -18,13 +18,14 @@ fn test_json_params() {
(None, "NULL"), (None, "NULL"),
], ],
) )
.await
} }
#[test] #[tokio::test]
fn test_jsonb_params() { async fn test_jsonb_params() {
test_type( test_type(
"JSONB", "JSONB",
&[ vec![
( (
Some(serde_json_1::from_str::<Value>("[10, 11, 12]").unwrap()), Some(serde_json_1::from_str::<Value>("[10, 11, 12]").unwrap()),
"'[10, 11, 12]'", "'[10, 11, 12]'",
@ -36,4 +37,5 @@ fn test_jsonb_params() {
(None, "NULL"), (None, "NULL"),
], ],
) )
.await
} }

View File

@ -2,11 +2,11 @@ use uuid_07::Uuid;
use crate::types::test_type; use crate::types::test_type;
#[test] #[tokio::test]
fn test_uuid_params() { async fn test_uuid_params() {
test_type( test_type(
"UUID", "UUID",
&[ vec![
( (
Some(Uuid::parse_str("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11").unwrap()), Some(Uuid::parse_str("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11").unwrap()),
"'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'", "'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'",
@ -14,4 +14,5 @@ fn test_uuid_params() {
(None, "NULL"), (None, "NULL"),
], ],
) )
.await
} }