2018-12-09 01:40:37 +00:00
|
|
|
#![warn(rust_2018_idioms)]
|
2018-06-19 02:34:25 +00:00
|
|
|
|
2019-11-27 00:32:36 +00:00
|
|
|
use bytes::{Bytes, BytesMut};
|
2019-08-01 04:19:56 +00:00
|
|
|
use futures::channel::mpsc;
|
2019-11-30 23:18:50 +00:00
|
|
|
use futures::{
|
|
|
|
future, join, pin_mut, stream, try_join, FutureExt, SinkExt, StreamExt, TryStreamExt,
|
|
|
|
};
|
2019-07-31 04:25:30 +00:00
|
|
|
use std::fmt::Write;
|
2019-11-27 00:32:36 +00:00
|
|
|
use std::time::Duration;
|
2018-11-27 06:45:14 +00:00
|
|
|
use tokio::net::TcpStream;
|
2019-11-27 00:32:36 +00:00
|
|
|
use tokio::time;
|
2018-06-22 02:38:42 +00:00
|
|
|
use tokio_postgres::error::SqlState;
|
2019-07-22 04:42:42 +00:00
|
|
|
use tokio_postgres::tls::{NoTls, NoTlsStream};
|
2019-07-27 03:11:34 +00:00
|
|
|
use tokio_postgres::types::{Kind, Type};
|
2019-08-01 04:19:56 +00:00
|
|
|
use tokio_postgres::{AsyncMessage, Client, Config, Connection, Error, SimpleQueryMessage};
|
2018-11-27 06:45:14 +00:00
|
|
|
|
2019-12-09 02:30:47 +00:00
|
|
|
mod binary_copy;
|
2018-12-17 02:11:52 +00:00
|
|
|
mod parse;
|
2018-12-18 05:25:21 +00:00
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
mod runtime;
|
2018-12-10 00:18:21 +00:00
|
|
|
mod types;
|
|
|
|
|
2019-07-23 04:27:21 +00:00
|
|
|
async fn connect_raw(s: &str) -> Result<(Client, Connection<TcpStream, NoTlsStream>), Error> {
|
2019-08-30 01:10:58 +00:00
|
|
|
let socket = TcpStream::connect("127.0.0.1:5433").await.unwrap();
|
2019-07-22 04:42:42 +00:00
|
|
|
let config = s.parse::<Config>().unwrap();
|
2019-09-25 00:03:37 +00:00
|
|
|
config.connect_raw(socket, NoTls).await
|
2018-06-22 02:38:42 +00:00
|
|
|
}
|
|
|
|
|
2019-07-23 04:27:21 +00:00
|
|
|
async fn connect(s: &str) -> Client {
|
|
|
|
let (client, connection) = connect_raw(s).await.unwrap();
|
|
|
|
let connection = connection.map(|r| r.unwrap());
|
|
|
|
tokio::spawn(connection);
|
|
|
|
client
|
|
|
|
}
|
|
|
|
|
2019-07-22 04:42:42 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn plain_password_missing() {
|
2019-07-23 04:27:21 +00:00
|
|
|
connect_raw("user=pass_user dbname=postgres")
|
2019-07-22 04:42:42 +00:00
|
|
|
.await
|
|
|
|
.err()
|
|
|
|
.unwrap();
|
2018-06-22 02:38:42 +00:00
|
|
|
}
|
|
|
|
|
2019-07-22 04:42:42 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn plain_password_wrong() {
|
2019-07-23 04:27:21 +00:00
|
|
|
match connect_raw("user=pass_user password=foo dbname=postgres").await {
|
2018-06-22 02:38:42 +00:00
|
|
|
Ok(_) => panic!("unexpected success"),
|
|
|
|
Err(ref e) if e.code() == Some(&SqlState::INVALID_PASSWORD) => {}
|
|
|
|
Err(e) => panic!("{}", e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-22 04:42:42 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn plain_password_ok() {
|
2019-07-23 04:27:21 +00:00
|
|
|
connect("user=pass_user password=password dbname=postgres").await;
|
2018-06-22 02:38:42 +00:00
|
|
|
}
|
|
|
|
|
2019-07-22 04:42:42 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn md5_password_missing() {
|
2019-07-23 04:27:21 +00:00
|
|
|
connect_raw("user=md5_user dbname=postgres")
|
2019-07-22 04:42:42 +00:00
|
|
|
.await
|
|
|
|
.err()
|
|
|
|
.unwrap();
|
2018-06-22 02:38:42 +00:00
|
|
|
}
|
|
|
|
|
2019-07-22 04:42:42 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn md5_password_wrong() {
|
2019-07-23 04:27:21 +00:00
|
|
|
match connect_raw("user=md5_user password=foo dbname=postgres").await {
|
2018-06-22 02:38:42 +00:00
|
|
|
Ok(_) => panic!("unexpected success"),
|
|
|
|
Err(ref e) if e.code() == Some(&SqlState::INVALID_PASSWORD) => {}
|
|
|
|
Err(e) => panic!("{}", e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-22 04:42:42 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn md5_password_ok() {
|
2019-07-23 04:27:21 +00:00
|
|
|
connect("user=md5_user password=password dbname=postgres").await;
|
2018-06-22 02:38:42 +00:00
|
|
|
}
|
|
|
|
|
2019-07-22 04:42:42 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn scram_password_missing() {
|
2019-07-23 04:27:21 +00:00
|
|
|
connect_raw("user=scram_user dbname=postgres")
|
2019-07-22 04:42:42 +00:00
|
|
|
.await
|
|
|
|
.err()
|
|
|
|
.unwrap();
|
2018-06-22 02:38:42 +00:00
|
|
|
}
|
|
|
|
|
2019-07-22 04:42:42 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn scram_password_wrong() {
|
2019-07-23 04:27:21 +00:00
|
|
|
match connect_raw("user=scram_user password=foo dbname=postgres").await {
|
2018-06-22 02:38:42 +00:00
|
|
|
Ok(_) => panic!("unexpected success"),
|
|
|
|
Err(ref e) if e.code() == Some(&SqlState::INVALID_PASSWORD) => {}
|
|
|
|
Err(e) => panic!("{}", e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-22 04:42:42 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn scram_password_ok() {
|
2019-07-23 04:27:21 +00:00
|
|
|
connect("user=scram_user password=password dbname=postgres").await;
|
2018-06-22 02:38:42 +00:00
|
|
|
}
|
|
|
|
|
2019-07-24 02:54:22 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn pipelined_prepare() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2019-07-24 02:54:22 +00:00
|
|
|
|
2019-07-27 03:11:34 +00:00
|
|
|
let prepare1 = client.prepare("SELECT $1::HSTORE[]");
|
2019-07-24 02:54:22 +00:00
|
|
|
let prepare2 = client.prepare("SELECT $1::BIGINT");
|
|
|
|
|
|
|
|
let (statement1, statement2) = try_join!(prepare1, prepare2).unwrap();
|
|
|
|
|
2019-07-27 03:11:34 +00:00
|
|
|
assert_eq!(statement1.params()[0].name(), "_hstore");
|
|
|
|
assert_eq!(statement1.columns()[0].type_().name(), "_hstore");
|
2019-07-24 02:54:22 +00:00
|
|
|
|
|
|
|
assert_eq!(statement2.params()[0], Type::INT8);
|
|
|
|
assert_eq!(statement2.columns()[0].type_(), &Type::INT8);
|
|
|
|
}
|
|
|
|
|
2019-07-25 02:18:15 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn insert_select() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2019-07-25 02:18:15 +00:00
|
|
|
|
2019-07-28 23:34:07 +00:00
|
|
|
client
|
|
|
|
.batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL, name TEXT)")
|
2019-07-25 02:18:15 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let insert = client.prepare("INSERT INTO foo (name) VALUES ($1), ($2)");
|
|
|
|
let select = client.prepare("SELECT id, name FROM foo ORDER BY id");
|
|
|
|
let (insert, select) = try_join!(insert, select).unwrap();
|
|
|
|
|
|
|
|
let insert = client.execute(&insert, &[&"alice", &"bob"]);
|
2019-10-09 00:15:41 +00:00
|
|
|
let select = client.query(&select, &[]);
|
2019-07-25 02:18:15 +00:00
|
|
|
let (_, rows) = try_join!(insert, select).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(rows.len(), 2);
|
|
|
|
assert_eq!(rows[0].get::<_, i32>(0), 1);
|
|
|
|
assert_eq!(rows[0].get::<_, &str>(1), "alice");
|
|
|
|
assert_eq!(rows[1].get::<_, i32>(0), 2);
|
|
|
|
assert_eq!(rows[1].get::<_, &str>(1), "bob");
|
|
|
|
}
|
|
|
|
|
2019-07-27 03:11:34 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn custom_enum() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2018-07-05 04:02:08 +00:00
|
|
|
|
2019-07-28 23:34:07 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
2019-07-27 03:11:34 +00:00
|
|
|
"CREATE TYPE pg_temp.mood AS ENUM (
|
|
|
|
'sad',
|
|
|
|
'ok',
|
|
|
|
'happy'
|
|
|
|
)",
|
2019-02-01 04:34:49 +00:00
|
|
|
)
|
2019-07-27 03:11:34 +00:00
|
|
|
.await
|
2018-12-09 01:40:37 +00:00
|
|
|
.unwrap();
|
2018-07-05 04:02:08 +00:00
|
|
|
|
2019-07-27 03:11:34 +00:00
|
|
|
let select = client.prepare("SELECT $1::mood").await.unwrap();
|
2018-07-05 04:02:08 +00:00
|
|
|
|
|
|
|
let ty = &select.params()[0];
|
|
|
|
assert_eq!("mood", ty.name());
|
|
|
|
assert_eq!(
|
|
|
|
&Kind::Enum(vec![
|
|
|
|
"sad".to_string(),
|
|
|
|
"ok".to_string(),
|
|
|
|
"happy".to_string(),
|
|
|
|
]),
|
2019-07-27 03:11:34 +00:00
|
|
|
ty.kind(),
|
2018-07-05 04:02:08 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-07-27 03:11:34 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn custom_domain() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2018-07-05 04:02:08 +00:00
|
|
|
|
2019-07-28 23:34:07 +00:00
|
|
|
client
|
|
|
|
.batch_execute("CREATE DOMAIN pg_temp.session_id AS bytea CHECK(octet_length(VALUE) = 16)")
|
2019-07-27 03:11:34 +00:00
|
|
|
.await
|
2018-12-09 01:40:37 +00:00
|
|
|
.unwrap();
|
2018-07-05 04:02:08 +00:00
|
|
|
|
2019-07-27 03:11:34 +00:00
|
|
|
let select = client.prepare("SELECT $1::session_id").await.unwrap();
|
2018-07-05 04:02:08 +00:00
|
|
|
|
|
|
|
let ty = &select.params()[0];
|
|
|
|
assert_eq!("session_id", ty.name());
|
|
|
|
assert_eq!(&Kind::Domain(Type::BYTEA), ty.kind());
|
|
|
|
}
|
|
|
|
|
2019-07-27 03:11:34 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn custom_array() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2018-07-05 04:02:08 +00:00
|
|
|
|
2019-07-27 03:11:34 +00:00
|
|
|
let select = client.prepare("SELECT $1::HSTORE[]").await.unwrap();
|
2018-07-05 04:02:08 +00:00
|
|
|
|
|
|
|
let ty = &select.params()[0];
|
|
|
|
assert_eq!("_hstore", ty.name());
|
2019-07-27 03:11:34 +00:00
|
|
|
match ty.kind() {
|
|
|
|
Kind::Array(ty) => {
|
2018-07-05 04:02:08 +00:00
|
|
|
assert_eq!("hstore", ty.name());
|
|
|
|
assert_eq!(&Kind::Simple, ty.kind());
|
|
|
|
}
|
|
|
|
_ => panic!("unexpected kind"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-27 03:11:34 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn custom_composite() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2018-07-05 04:02:08 +00:00
|
|
|
|
2019-07-28 23:34:07 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
2019-07-27 03:11:34 +00:00
|
|
|
"CREATE TYPE pg_temp.inventory_item AS (
|
2019-07-28 23:34:07 +00:00
|
|
|
name TEXT,
|
|
|
|
supplier INTEGER,
|
|
|
|
price NUMERIC
|
|
|
|
)",
|
2019-02-01 04:34:49 +00:00
|
|
|
)
|
2019-07-27 03:11:34 +00:00
|
|
|
.await
|
2018-12-09 01:40:37 +00:00
|
|
|
.unwrap();
|
2018-07-05 04:02:08 +00:00
|
|
|
|
2019-07-27 03:11:34 +00:00
|
|
|
let select = client.prepare("SELECT $1::inventory_item").await.unwrap();
|
2018-07-05 04:02:08 +00:00
|
|
|
|
|
|
|
let ty = &select.params()[0];
|
|
|
|
assert_eq!(ty.name(), "inventory_item");
|
2019-07-27 03:11:34 +00:00
|
|
|
match ty.kind() {
|
|
|
|
Kind::Composite(fields) => {
|
2018-07-05 04:02:08 +00:00
|
|
|
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);
|
|
|
|
}
|
2019-07-27 03:11:34 +00:00
|
|
|
_ => panic!("unexpected kind"),
|
2018-07-05 04:02:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-27 03:11:34 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn custom_range() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2019-07-27 03:11:34 +00:00
|
|
|
|
2019-07-28 23:34:07 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
2019-07-27 03:11:34 +00:00
|
|
|
"CREATE TYPE pg_temp.floatrange AS RANGE (
|
2019-07-28 23:34:07 +00:00
|
|
|
subtype = float8,
|
|
|
|
subtype_diff = float8mi
|
|
|
|
)",
|
2019-07-27 03:11:34 +00:00
|
|
|
)
|
|
|
|
.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());
|
|
|
|
}
|
|
|
|
|
2019-07-28 23:34:07 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn simple_query() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2019-07-28 23:34:07 +00:00
|
|
|
|
|
|
|
let messages = client
|
|
|
|
.simple_query(
|
|
|
|
"CREATE TEMPORARY TABLE foo (
|
|
|
|
id SERIAL,
|
|
|
|
name TEXT
|
|
|
|
);
|
|
|
|
INSERT INTO foo (name) VALUES ('steven'), ('joe');
|
|
|
|
SELECT * FROM foo ORDER BY id;",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
match messages[0] {
|
|
|
|
SimpleQueryMessage::CommandComplete(0) => {}
|
|
|
|
_ => panic!("unexpected message"),
|
|
|
|
}
|
|
|
|
match messages[1] {
|
|
|
|
SimpleQueryMessage::CommandComplete(2) => {}
|
|
|
|
_ => panic!("unexpected message"),
|
|
|
|
}
|
|
|
|
match &messages[2] {
|
|
|
|
SimpleQueryMessage::Row(row) => {
|
|
|
|
assert_eq!(row.get(0), Some("1"));
|
|
|
|
assert_eq!(row.get(1), Some("steven"));
|
|
|
|
}
|
|
|
|
_ => panic!("unexpected message"),
|
|
|
|
}
|
|
|
|
match &messages[3] {
|
|
|
|
SimpleQueryMessage::Row(row) => {
|
|
|
|
assert_eq!(row.get(0), Some("2"));
|
|
|
|
assert_eq!(row.get(1), Some("joe"));
|
|
|
|
}
|
|
|
|
_ => panic!("unexpected message"),
|
|
|
|
}
|
|
|
|
match messages[4] {
|
|
|
|
SimpleQueryMessage::CommandComplete(2) => {}
|
|
|
|
_ => panic!("unexpected message"),
|
|
|
|
}
|
|
|
|
assert_eq!(messages.len(), 5);
|
|
|
|
}
|
|
|
|
|
2019-07-30 04:36:07 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn cancel_query_raw() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2019-07-30 04:36:07 +00:00
|
|
|
|
2019-08-30 01:10:58 +00:00
|
|
|
let socket = TcpStream::connect("127.0.0.1:5433").await.unwrap();
|
2019-07-30 04:36:07 +00:00
|
|
|
let cancel = client.cancel_query_raw(socket, NoTls);
|
2019-11-27 00:32:36 +00:00
|
|
|
let cancel = time::delay_for(Duration::from_millis(100)).then(|()| cancel);
|
2019-07-30 04:36:07 +00:00
|
|
|
|
|
|
|
let sleep = client.batch_execute("SELECT pg_sleep(100)");
|
|
|
|
|
|
|
|
match join!(sleep, cancel) {
|
|
|
|
(Err(ref e), Ok(())) if e.code() == Some(&SqlState::QUERY_CANCELED) => {}
|
|
|
|
t => panic!("unexpected return: {:?}", t),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-31 02:54:30 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn transaction_commit() {
|
|
|
|
let mut client = connect("user=postgres").await;
|
|
|
|
|
2019-07-31 04:25:30 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"CREATE TEMPORARY TABLE foo(
|
2019-07-31 04:29:18 +00:00
|
|
|
id SERIAL,
|
|
|
|
name TEXT
|
|
|
|
)",
|
2019-07-31 04:25:30 +00:00
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-07-31 02:54:30 +00:00
|
|
|
|
2019-09-28 18:42:00 +00:00
|
|
|
let transaction = client.transaction().await.unwrap();
|
2019-07-31 04:25:30 +00:00
|
|
|
transaction
|
|
|
|
.batch_execute("INSERT INTO foo (name) VALUES ('steven')")
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-07-31 02:54:30 +00:00
|
|
|
transaction.commit().await.unwrap();
|
|
|
|
|
|
|
|
let stmt = client.prepare("SELECT name FROM foo").await.unwrap();
|
2019-10-09 00:22:56 +00:00
|
|
|
let rows = client.query(&stmt, &[]).await.unwrap();
|
2019-07-31 02:54:30 +00:00
|
|
|
|
|
|
|
assert_eq!(rows.len(), 1);
|
|
|
|
assert_eq!(rows[0].get::<_, &str>(0), "steven");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn transaction_rollback() {
|
|
|
|
let mut client = connect("user=postgres").await;
|
|
|
|
|
2019-07-31 04:25:30 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"CREATE TEMPORARY TABLE foo(
|
2019-07-31 04:29:18 +00:00
|
|
|
id SERIAL,
|
|
|
|
name TEXT
|
|
|
|
)",
|
2019-07-31 04:25:30 +00:00
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-07-31 02:54:30 +00:00
|
|
|
|
2019-09-28 18:42:00 +00:00
|
|
|
let transaction = client.transaction().await.unwrap();
|
2019-07-31 04:25:30 +00:00
|
|
|
transaction
|
|
|
|
.batch_execute("INSERT INTO foo (name) VALUES ('steven')")
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-07-31 02:54:30 +00:00
|
|
|
transaction.rollback().await.unwrap();
|
|
|
|
|
|
|
|
let stmt = client.prepare("SELECT name FROM foo").await.unwrap();
|
2019-10-09 00:22:56 +00:00
|
|
|
let rows = client.query(&stmt, &[]).await.unwrap();
|
2019-07-31 02:54:30 +00:00
|
|
|
|
|
|
|
assert_eq!(rows.len(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn transaction_rollback_drop() {
|
|
|
|
let mut client = connect("user=postgres").await;
|
|
|
|
|
2019-07-31 04:25:30 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"CREATE TEMPORARY TABLE foo(
|
2019-07-31 04:29:18 +00:00
|
|
|
id SERIAL,
|
|
|
|
name TEXT
|
|
|
|
)",
|
2019-07-31 04:25:30 +00:00
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-07-31 02:54:30 +00:00
|
|
|
|
2019-09-28 18:42:00 +00:00
|
|
|
let transaction = client.transaction().await.unwrap();
|
2019-07-31 04:25:30 +00:00
|
|
|
transaction
|
|
|
|
.batch_execute("INSERT INTO foo (name) VALUES ('steven')")
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-07-31 02:54:30 +00:00
|
|
|
drop(transaction);
|
|
|
|
|
|
|
|
let stmt = client.prepare("SELECT name FROM foo").await.unwrap();
|
2019-10-09 00:22:56 +00:00
|
|
|
let rows = client.query(&stmt, &[]).await.unwrap();
|
2019-07-31 04:25:30 +00:00
|
|
|
|
|
|
|
assert_eq!(rows.len(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn copy_in() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2019-07-31 04:25:30 +00:00
|
|
|
|
2019-07-31 04:29:18 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"CREATE TEMPORARY TABLE foo (
|
|
|
|
id INTEGER,
|
|
|
|
name TEXT
|
|
|
|
)",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-07-31 04:25:30 +00:00
|
|
|
|
2019-11-30 16:04:59 +00:00
|
|
|
let mut stream = stream::iter(
|
2019-11-27 00:32:36 +00:00
|
|
|
vec![
|
|
|
|
Bytes::from_static(b"1\tjim\n"),
|
|
|
|
Bytes::from_static(b"2\tjoe\n"),
|
|
|
|
]
|
|
|
|
.into_iter()
|
2019-11-30 16:04:59 +00:00
|
|
|
.map(Ok::<_, Error>),
|
2019-07-31 04:29:18 +00:00
|
|
|
);
|
2019-12-05 02:51:59 +00:00
|
|
|
let sink = client.copy_in("COPY foo FROM STDIN").await.unwrap();
|
2019-11-30 16:04:59 +00:00
|
|
|
pin_mut!(sink);
|
|
|
|
sink.send_all(&mut stream).await.unwrap();
|
|
|
|
let rows = sink.finish().await.unwrap();
|
2019-07-31 04:25:30 +00:00
|
|
|
assert_eq!(rows, 2);
|
|
|
|
|
2019-11-30 23:18:50 +00:00
|
|
|
let rows = client
|
|
|
|
.query("SELECT id, name FROM foo ORDER BY id", &[])
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-07-31 02:54:30 +00:00
|
|
|
|
2019-07-31 04:25:30 +00:00
|
|
|
assert_eq!(rows.len(), 2);
|
|
|
|
assert_eq!(rows[0].get::<_, i32>(0), 1);
|
|
|
|
assert_eq!(rows[0].get::<_, &str>(1), "jim");
|
|
|
|
assert_eq!(rows[1].get::<_, i32>(0), 2);
|
|
|
|
assert_eq!(rows[1].get::<_, &str>(1), "joe");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn copy_in_large() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2019-07-31 04:25:30 +00:00
|
|
|
|
2019-07-31 04:29:18 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"CREATE TEMPORARY TABLE foo (
|
|
|
|
id INTEGER,
|
|
|
|
name TEXT
|
|
|
|
)",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-07-31 04:25:30 +00:00
|
|
|
|
2019-11-27 00:32:36 +00:00
|
|
|
let a = Bytes::from_static(b"0\tname0\n");
|
|
|
|
let mut b = BytesMut::new();
|
2019-07-31 04:25:30 +00:00
|
|
|
for i in 1..5_000 {
|
|
|
|
writeln!(b, "{0}\tname{0}", i).unwrap();
|
|
|
|
}
|
2019-11-27 00:32:36 +00:00
|
|
|
let mut c = BytesMut::new();
|
2019-07-31 04:25:30 +00:00
|
|
|
for i in 5_000..10_000 {
|
|
|
|
writeln!(c, "{0}\tname{0}", i).unwrap();
|
|
|
|
}
|
2019-11-30 16:04:59 +00:00
|
|
|
let mut stream = stream::iter(
|
2019-11-27 00:32:36 +00:00
|
|
|
vec![a, b.freeze(), c.freeze()]
|
|
|
|
.into_iter()
|
2019-11-30 16:04:59 +00:00
|
|
|
.map(Ok::<_, Error>),
|
2019-11-27 00:32:36 +00:00
|
|
|
);
|
2019-07-31 04:25:30 +00:00
|
|
|
|
2019-12-05 02:51:59 +00:00
|
|
|
let sink = client.copy_in("COPY foo FROM STDIN").await.unwrap();
|
2019-11-30 16:04:59 +00:00
|
|
|
pin_mut!(sink);
|
|
|
|
sink.send_all(&mut stream).await.unwrap();
|
|
|
|
let rows = sink.finish().await.unwrap();
|
2019-07-31 04:25:30 +00:00
|
|
|
assert_eq!(rows, 10_000);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn copy_in_error() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2019-07-31 04:25:30 +00:00
|
|
|
|
2019-07-31 04:29:18 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"CREATE TEMPORARY TABLE foo (
|
|
|
|
id INTEGER,
|
|
|
|
name TEXT
|
|
|
|
)",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-07-31 04:25:30 +00:00
|
|
|
|
2019-11-30 16:04:59 +00:00
|
|
|
{
|
2019-12-05 02:51:59 +00:00
|
|
|
let sink = client.copy_in("COPY foo FROM STDIN").await.unwrap();
|
2019-11-30 16:04:59 +00:00
|
|
|
pin_mut!(sink);
|
|
|
|
sink.send(Bytes::from_static(b"1\tsteven")).await.unwrap();
|
|
|
|
}
|
2019-07-31 04:25:30 +00:00
|
|
|
|
2019-11-30 23:18:50 +00:00
|
|
|
let rows = client
|
|
|
|
.query("SELECT id, name FROM foo ORDER BY id", &[])
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-07-31 02:54:30 +00:00
|
|
|
assert_eq!(rows.len(), 0);
|
|
|
|
}
|
|
|
|
|
2019-08-01 03:15:17 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn copy_out() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let client = connect("user=postgres").await;
|
2019-08-01 03:15:17 +00:00
|
|
|
|
2019-08-01 04:19:56 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"CREATE TEMPORARY TABLE foo (
|
2019-08-01 03:15:17 +00:00
|
|
|
id SERIAL,
|
|
|
|
name TEXT
|
|
|
|
);
|
|
|
|
|
2019-08-01 04:19:56 +00:00
|
|
|
INSERT INTO foo (name) VALUES ('jim'), ('joe');",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-08-01 03:15:17 +00:00
|
|
|
|
|
|
|
let stmt = client.prepare("COPY foo TO STDOUT").await.unwrap();
|
2019-10-09 02:01:34 +00:00
|
|
|
let data = client
|
2019-12-05 02:51:59 +00:00
|
|
|
.copy_out(&stmt)
|
2019-10-09 02:01:34 +00:00
|
|
|
.await
|
|
|
|
.unwrap()
|
2019-11-27 00:32:36 +00:00
|
|
|
.try_fold(BytesMut::new(), |mut buf, chunk| {
|
|
|
|
async move {
|
|
|
|
buf.extend_from_slice(&chunk);
|
|
|
|
Ok(buf)
|
|
|
|
}
|
|
|
|
})
|
2019-10-09 02:01:34 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-08-01 03:15:17 +00:00
|
|
|
assert_eq!(&data[..], b"1\tjim\n2\tjoe\n");
|
|
|
|
}
|
|
|
|
|
2019-08-01 04:19:56 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn notifications() {
|
2019-09-28 18:42:00 +00:00
|
|
|
let (client, mut connection) = connect_raw("user=postgres").await.unwrap();
|
2019-08-01 04:19:56 +00:00
|
|
|
|
|
|
|
let (tx, rx) = mpsc::unbounded();
|
|
|
|
let stream = stream::poll_fn(move |cx| connection.poll_message(cx)).map_err(|e| panic!(e));
|
|
|
|
let connection = stream.forward(tx).map(|r| r.unwrap());
|
|
|
|
tokio::spawn(connection);
|
|
|
|
|
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"LISTEN test_notifications;
|
|
|
|
NOTIFY test_notifications, 'hello';
|
|
|
|
NOTIFY test_notifications, 'world';",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
drop(client);
|
|
|
|
|
|
|
|
let notifications = rx
|
|
|
|
.filter_map(|m| match m {
|
|
|
|
AsyncMessage::Notification(n) => future::ready(Some(n)),
|
|
|
|
_ => future::ready(None),
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.await;
|
|
|
|
assert_eq!(notifications.len(), 2);
|
|
|
|
assert_eq!(notifications[0].channel(), "test_notifications");
|
|
|
|
assert_eq!(notifications[0].payload(), "hello");
|
|
|
|
assert_eq!(notifications[1].channel(), "test_notifications");
|
|
|
|
assert_eq!(notifications[1].payload(), "world");
|
|
|
|
}
|
|
|
|
|
2019-08-02 03:43:13 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn query_portal() {
|
|
|
|
let mut client = connect("user=postgres").await;
|
|
|
|
|
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"CREATE TEMPORARY TABLE foo (
|
|
|
|
id SERIAL,
|
|
|
|
name TEXT
|
|
|
|
);
|
|
|
|
|
|
|
|
INSERT INTO foo (name) VALUES ('alice'), ('bob'), ('charlie');",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let stmt = client
|
|
|
|
.prepare("SELECT id, name FROM foo ORDER BY id")
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2019-09-28 18:42:00 +00:00
|
|
|
let transaction = client.transaction().await.unwrap();
|
2019-08-02 03:43:13 +00:00
|
|
|
|
|
|
|
let portal = transaction.bind(&stmt, &[]).await.unwrap();
|
2019-10-09 00:22:56 +00:00
|
|
|
let f1 = transaction.query_portal(&portal, 2);
|
|
|
|
let f2 = transaction.query_portal(&portal, 2);
|
|
|
|
let f3 = transaction.query_portal(&portal, 2);
|
2019-08-02 03:43:13 +00:00
|
|
|
|
|
|
|
let (r1, r2, r3) = try_join!(f1, f2, f3).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(r1.len(), 2);
|
|
|
|
assert_eq!(r1[0].get::<_, i32>(0), 1);
|
|
|
|
assert_eq!(r1[0].get::<_, &str>(1), "alice");
|
|
|
|
assert_eq!(r1[1].get::<_, i32>(0), 2);
|
|
|
|
assert_eq!(r1[1].get::<_, &str>(1), "bob");
|
|
|
|
|
|
|
|
assert_eq!(r2.len(), 1);
|
|
|
|
assert_eq!(r2[0].get::<_, i32>(0), 3);
|
|
|
|
assert_eq!(r2[0].get::<_, &str>(1), "charlie");
|
|
|
|
|
|
|
|
assert_eq!(r3.len(), 0);
|
|
|
|
}
|
|
|
|
|
2019-09-25 00:03:37 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn require_channel_binding() {
|
|
|
|
connect_raw("user=postgres channel_binding=require")
|
|
|
|
.await
|
|
|
|
.err()
|
|
|
|
.unwrap();
|
2018-12-24 18:02:48 +00:00
|
|
|
}
|
|
|
|
|
2019-09-25 00:03:37 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn prefer_channel_binding() {
|
|
|
|
connect("user=postgres channel_binding=prefer").await;
|
|
|
|
}
|
2018-12-23 01:02:48 +00:00
|
|
|
|
2019-09-25 00:03:37 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn disable_channel_binding() {
|
|
|
|
connect("user=postgres channel_binding=disable").await;
|
2018-12-23 01:02:48 +00:00
|
|
|
}
|
2019-09-28 18:42:00 +00:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn check_send() {
|
|
|
|
fn is_send<T: Send>(_: &T) {}
|
|
|
|
|
|
|
|
let f = connect("user=postgres");
|
|
|
|
is_send(&f);
|
|
|
|
let mut client = f.await;
|
|
|
|
|
|
|
|
let f = client.prepare("SELECT $1::TEXT");
|
|
|
|
is_send(&f);
|
|
|
|
let stmt = f.await.unwrap();
|
|
|
|
|
|
|
|
let f = client.query(&stmt, &[&"hello"]);
|
|
|
|
is_send(&f);
|
|
|
|
drop(f);
|
|
|
|
|
|
|
|
let f = client.execute(&stmt, &[&"hello"]);
|
|
|
|
is_send(&f);
|
|
|
|
drop(f);
|
|
|
|
|
|
|
|
let f = client.transaction();
|
|
|
|
is_send(&f);
|
|
|
|
let trans = f.await.unwrap();
|
|
|
|
|
|
|
|
let f = trans.query(&stmt, &[&"hello"]);
|
|
|
|
is_send(&f);
|
|
|
|
drop(f);
|
|
|
|
|
|
|
|
let f = trans.execute(&stmt, &[&"hello"]);
|
|
|
|
is_send(&f);
|
|
|
|
drop(f);
|
|
|
|
}
|
2019-10-10 00:45:53 +00:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn query_one() {
|
|
|
|
let client = connect("user=postgres").await;
|
|
|
|
|
2019-10-10 23:03:48 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"
|
2019-12-11 00:34:04 +00:00
|
|
|
CREATE TEMPORARY TABLE foo (
|
|
|
|
name TEXT
|
|
|
|
);
|
|
|
|
INSERT INTO foo (name) VALUES ('alice'), ('bob'), ('carol');
|
|
|
|
",
|
2019-10-10 23:03:48 +00:00
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-10-10 00:45:53 +00:00
|
|
|
|
2019-10-10 23:03:48 +00:00
|
|
|
client
|
|
|
|
.query_one("SELECT * FROM foo WHERE name = 'dave'", &[])
|
|
|
|
.await
|
|
|
|
.err()
|
|
|
|
.unwrap();
|
|
|
|
client
|
|
|
|
.query_one("SELECT * FROM foo WHERE name = 'alice'", &[])
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
client
|
|
|
|
.query_one("SELECT * FROM foo", &[])
|
|
|
|
.await
|
|
|
|
.err()
|
|
|
|
.unwrap();
|
2019-10-10 00:45:53 +00:00
|
|
|
}
|
2019-12-11 00:34:04 +00:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn query_opt() {
|
|
|
|
let client = connect("user=postgres").await;
|
|
|
|
|
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"
|
|
|
|
CREATE TEMPORARY TABLE foo (
|
|
|
|
name TEXT
|
|
|
|
);
|
|
|
|
INSERT INTO foo (name) VALUES ('alice'), ('bob'), ('carol');
|
|
|
|
",
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(client
|
|
|
|
.query_opt("SELECT * FROM foo WHERE name = 'dave'", &[])
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.is_none());
|
|
|
|
client
|
|
|
|
.query_opt("SELECT * FROM foo WHERE name = 'alice'", &[])
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.unwrap();
|
|
|
|
client
|
|
|
|
.query_one("SELECT * FROM foo", &[])
|
|
|
|
.await
|
|
|
|
.err()
|
|
|
|
.unwrap();
|
|
|
|
}
|