This commit is contained in:
Steven Fackler 2017-06-30 17:35:17 -10:00
parent 3809972907
commit 6a86f8dd85
52 changed files with 2638 additions and 1605 deletions

View File

@ -71,8 +71,9 @@ fn variant_name(code: &str) -> Option<String> {
}
fn make_header(file: &mut BufWriter<File>) {
write!(file,
"// Autogenerated file - DO NOT EDIT
write!(
file,
"// Autogenerated file - DO NOT EDIT
use phf;
"
@ -80,8 +81,9 @@ use phf;
}
fn make_enum(codes: &[Code], file: &mut BufWriter<File>) {
write!(file,
r#"/// SQLSTATE error codes
write!(
file,
r#"/// SQLSTATE error codes
#[derive(PartialEq, Eq, Clone, Debug)]
#[allow(enum_variant_names)]
pub enum SqlState {{
@ -89,14 +91,18 @@ pub enum SqlState {{
).unwrap();
for code in codes {
write!(file,
" /// `{}`
write!(
file,
" /// `{}`
{},\n",
code.code, code.variant).unwrap();
code.code,
code.variant
).unwrap();
}
write!(file,
" /// An unknown code
write!(
file,
" /// An unknown code
Other(String),
}}
@ -105,9 +111,11 @@ pub enum SqlState {{
}
fn make_map(codes: &[Code], file: &mut BufWriter<File>) {
write!(file,
"#[cfg_attr(rustfmt, rustfmt_skip)]
static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = ").unwrap();
write!(
file,
"#[cfg_attr(rustfmt, rustfmt_skip)]
static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = "
).unwrap();
let mut builder = phf_codegen::Map::new();
for code in codes {
builder.entry(&*code.code, &format!("SqlState::{}", code.variant));
@ -117,7 +125,9 @@ static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = ").unwrap();
}
fn make_impl(codes: &[Code], file: &mut BufWriter<File>) {
write!(file, r#"
write!(
file,
r#"
impl SqlState {{
/// Creates a `SqlState` from its error code.
pub fn from_code(s: &str) -> SqlState {{
@ -133,12 +143,18 @@ impl SqlState {{
).unwrap();
for code in codes {
write!(file, r#"
write!(
file,
r#"
SqlState::{} => "{}","#,
code.variant, code.code).unwrap();
code.variant,
code.code
).unwrap();
}
write!(file, r#"
write!(
file,
r#"
SqlState::Other(ref s) => s,
}}
}}
@ -146,4 +162,3 @@ impl SqlState {{
"#
).unwrap();
}

View File

@ -94,7 +94,8 @@ fn parse_types(ranges: &BTreeMap<u32, u32>) -> BTreeMap<u32, Type> {
let doc = array_re.replace(name, "$1[]");
let mut doc = doc.to_ascii_uppercase();
let descr = lines.peek()
let descr = lines
.peek()
.and_then(|line| doc_re.captures(line))
.and_then(|captures| captures.at(1));
if let Some(descr) = descr {
@ -119,8 +120,9 @@ fn parse_types(ranges: &BTreeMap<u32, u32>) -> BTreeMap<u32, Type> {
}
fn make_header(w: &mut BufWriter<File>) {
write!(w,
"// Autogenerated file - DO NOT EDIT
write!(
w,
"// Autogenerated file - DO NOT EDIT
use std::fmt;
use types::{{Oid, Kind, Other}};
@ -130,27 +132,33 @@ use types::{{Oid, Kind, Other}};
}
fn make_enum(w: &mut BufWriter<File>, types: &BTreeMap<u32, Type>) {
write!(w,
"/// A Postgres type.
write!(
w,
"/// A Postgres type.
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Type {{
"
).unwrap();
for type_ in types.values() {
write!(w,
" /// {}
write!(
w,
" /// {}
{},
"
, type_.doc, type_.variant).unwrap();
",
type_.doc,
type_.variant
).unwrap();
}
write!(w,
r" /// An unknown type.
write!(
w,
r" /// An unknown type.
Other(Other),
}}
" ).unwrap();
"
).unwrap();
}
fn make_display_impl(w: &mut BufWriter<File>) {
@ -180,10 +188,13 @@ fn make_impl(w: &mut BufWriter<File>, types: &BTreeMap<u32, Type>) {
).unwrap();
for (oid, type_) in types {
write!(w,
" {} => Some(Type::{}),
write!(
w,
" {} => Some(Type::{}),
",
oid, type_.variant).unwrap();
oid,
type_.variant
).unwrap();
}
write!(w,
@ -199,10 +210,13 @@ fn make_impl(w: &mut BufWriter<File>, types: &BTreeMap<u32, Type>) {
for (oid, type_) in types {
write!(w,
" Type::{} => {},
write!(
w,
" Type::{} => {},
",
type_.variant, oid).unwrap();
type_.variant,
oid
).unwrap();
}
write!(w,
@ -224,13 +238,16 @@ fn make_impl(w: &mut BufWriter<File>, types: &BTreeMap<u32, Type>) {
_ => "Simple".to_owned(),
};
write!(w,
" Type::{} => {{
write!(
w,
" Type::{} => {{
const V: &'static Kind = &Kind::{};
V
}}
",
type_.variant, kind).unwrap();
type_.variant,
kind
).unwrap();
}
write!(w,
@ -253,14 +270,18 @@ r#" Type::Other(ref u) => u.kind(),
).unwrap();
for type_ in types.values() {
write!(w,
r#" Type::{} => "{}",
write!(
w,
r#" Type::{} => "{}",
"#,
type_.variant, type_.name).unwrap();
type_.variant,
type_.name
).unwrap();
}
write!(w,
" Type::Other(ref u) => u.name(),
write!(
w,
" Type::Other(ref u) => u.name(),
}}
}}
}}

View File

@ -30,7 +30,9 @@ mod test {
let password = b"password";
let salt = [0x2a, 0x3d, 0x8f, 0xe0];
assert_eq!(md5_hash(username, password, salt),
"md562af4dd09bbb41884907a838a3233294");
assert_eq!(
md5_hash(username, password, salt),
"md562af4dd09bbb41884907a838a3233294"
);
}
}

View File

@ -133,8 +133,9 @@ impl ScramSha256 {
_ => return Err(io::Error::new(io::ErrorKind::Other, "invalid SCRAM state")),
};
let message = str::from_utf8(message)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
let message = str::from_utf8(message).map_err(|e| {
io::Error::new(io::ErrorKind::InvalidInput, e)
})?;
let parsed = Parser::new(message).server_first_message()?;
@ -193,14 +194,18 @@ impl ScramSha256 {
_ => return Err(io::Error::new(io::ErrorKind::Other, "invalid SCRAM state")),
};
let message = str::from_utf8(message)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
let message = str::from_utf8(message).map_err(|e| {
io::Error::new(io::ErrorKind::InvalidInput, e)
})?;
let parsed = Parser::new(message).server_final_message()?;
let verifier = match parsed {
ServerFinalMessage::Error(e) => {
return Err(io::Error::new(io::ErrorKind::Other, format!("SCRAM error: {}", e)))
return Err(io::Error::new(
io::ErrorKind::Other,
format!("SCRAM error: {}", e),
))
}
ServerFinalMessage::Verifier(verifier) => verifier,
};
@ -219,7 +224,10 @@ impl ScramSha256 {
if hmac.verify(&verifier) {
Ok(())
} else {
Err(io::Error::new(io::ErrorKind::InvalidInput, "SCRAM verification error"))
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"SCRAM verification error",
))
}
}
}
@ -241,18 +249,24 @@ impl<'a> Parser<'a> {
match self.it.next() {
Some((_, c)) if c == target => Ok(()),
Some((i, c)) => {
let m = format!("unexpected character at byte {}: expected `{}` but got `{}",
let m = format!(
"unexpected character at byte {}: expected `{}` but got `{}",
i,
target,
c);
c
);
Err(io::Error::new(io::ErrorKind::InvalidInput, m))
}
None => Err(io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected EOF")),
None => Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"unexpected EOF",
)),
}
}
fn take_while<F>(&mut self, f: F) -> io::Result<&'a str>
where F: Fn(char) -> bool
where
F: Fn(char) -> bool,
{
let start = match self.it.peek() {
Some(&(i, _)) => i,
@ -301,8 +315,9 @@ impl<'a> Parser<'a> {
'0'...'9' => true,
_ => false,
})?;
n.parse()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
n.parse().map_err(
|e| io::Error::new(io::ErrorKind::InvalidInput, e),
)
}
fn iteration_count(&mut self) -> io::Result<u32> {
@ -314,8 +329,10 @@ impl<'a> Parser<'a> {
fn eof(&mut self) -> io::Result<()> {
match self.it.peek() {
Some(&(i, _)) => {
Err(io::Error::new(io::ErrorKind::InvalidInput,
format!("unexpected trailing data at byte {}", i)))
Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("unexpected trailing data at byte {}", i),
))
}
None => Ok(()),
}

View File

@ -43,8 +43,9 @@ pub enum IsNull {
#[inline]
fn write_nullable<F, E>(serializer: F, buf: &mut Vec<u8>) -> Result<(), E>
where F: FnOnce(&mut Vec<u8>) -> Result<IsNull, E>,
E: From<io::Error>
where
F: FnOnce(&mut Vec<u8>) -> Result<IsNull, E>,
E: From<io::Error>,
{
let base = buf.len();
buf.extend_from_slice(&[0; 4]);

View File

@ -61,7 +61,10 @@ impl Message {
let len = (&buf[1..5]).read_u32::<BigEndian>().unwrap();
if len < 4 {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid message length"));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid message length",
));
}
let total_len = len as usize + 1;
@ -153,9 +156,9 @@ impl Message {
5 => {
let mut salt = [0; 4];
buf.read_exact(&mut salt)?;
Message::AuthenticationMd5Password(AuthenticationMd5PasswordBody {
salt: salt,
})
Message::AuthenticationMd5Password(
AuthenticationMd5PasswordBody { salt: salt },
)
}
6 => Message::AuthenticationScmCredential,
7 => Message::AuthenticationGss,
@ -177,8 +180,10 @@ impl Message {
Message::AuthenticationSaslFinal(AuthenticationSaslFinalBody(storage))
}
tag => {
return Err(io::Error::new(io::ErrorKind::InvalidInput,
format!("unknown authentication tag `{}`", tag)));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("unknown authentication tag `{}`", tag),
));
}
}
}
@ -212,13 +217,18 @@ impl Message {
Message::ReadyForQuery(ReadyForQueryBody { status: status })
}
tag => {
return Err(io::Error::new(io::ErrorKind::InvalidInput,
format!("unknown message tag `{}`", tag)));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("unknown message tag `{}`", tag),
));
}
};
if !buf.is_empty() {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid message length"));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid message length",
));
}
Ok(Some(message))
@ -248,7 +258,10 @@ impl Buffer {
self.idx = end + 1;
Ok(cstr)
}
None => Err(io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected EOF")),
None => Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"unexpected EOF",
)),
}
}
@ -312,7 +325,10 @@ impl<'a> FallibleIterator for SaslMechanisms<'a> {
let value_end = find_null(self.0, 0)?;
if value_end == 0 {
if self.0.len() != 1 {
return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid message length"));
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid message length",
));
}
Ok(None)
} else {
@ -416,7 +432,10 @@ impl<'a> FallibleIterator for ColumnFormats<'a> {
if self.buf.is_empty() {
return Ok(None);
} else {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid message length"));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid message length",
));
}
}
@ -489,7 +508,10 @@ impl<'a> FallibleIterator for DataRowRanges<'a> {
if self.buf.is_empty() {
return Ok(None);
} else {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid message length"));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid message length",
));
}
}
@ -500,7 +522,10 @@ impl<'a> FallibleIterator for DataRowRanges<'a> {
} else {
let len = len as usize;
if self.buf.len() < len {
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected EOF"));
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"unexpected EOF",
));
}
let base = self.len - self.buf.len();
self.buf = &self.buf[len as usize..];
@ -541,7 +566,10 @@ impl<'a> FallibleIterator for ErrorFields<'a> {
if self.buf.is_empty() {
return Ok(None);
} else {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid message length"));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid message length",
));
}
}
@ -637,7 +665,10 @@ impl<'a> FallibleIterator for Parameters<'a> {
if self.buf.is_empty() {
return Ok(None);
} else {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid message length"));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid message length",
));
}
}
@ -710,7 +741,10 @@ impl<'a> FallibleIterator for Fields<'a> {
if self.buf.is_empty() {
return Ok(None);
} else {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid message length"));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid message length",
));
}
}
@ -788,7 +822,10 @@ impl<'a> Field<'a> {
fn find_null(buf: &[u8], start: usize) -> io::Result<usize> {
match memchr(0, &buf[start..]) {
Some(pos) => Ok(pos + start),
None => Err(io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected EOF")),
None => Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"unexpected EOF",
)),
}
}

View File

@ -51,7 +51,8 @@ impl<'a> Message<'a> {
values,
result_formats,
} => {
let r = bind(portal,
let r = bind(
portal,
statement,
formats.iter().cloned(),
values,
@ -63,7 +64,8 @@ impl<'a> Message<'a> {
None => Ok(IsNull::Yes),
},
result_formats.iter().cloned(),
buf);
buf,
);
match r {
Ok(()) => Ok(()),
Err(BindError::Conversion(_)) => unreachable!(),
@ -104,8 +106,9 @@ impl<'a> Message<'a> {
#[inline]
fn write_body<F, E>(buf: &mut Vec<u8>, f: F) -> Result<(), E>
where F: FnOnce(&mut Vec<u8>) -> Result<(), E>,
E: From<io::Error>
where
F: FnOnce(&mut Vec<u8>) -> Result<(), E>,
E: From<io::Error>,
{
let base = buf.len();
buf.extend_from_slice(&[0; 4]);
@ -137,18 +140,20 @@ impl From<io::Error> for BindError {
}
#[inline]
pub fn bind<I, J, F, T, K>(portal: &str,
pub fn bind<I, J, F, T, K>(
portal: &str,
statement: &str,
formats: I,
values: J,
mut serializer: F,
result_formats: K,
buf: &mut Vec<u8>)
-> Result<(), BindError>
where I: IntoIterator<Item = i16>,
buf: &mut Vec<u8>,
) -> Result<(), BindError>
where
I: IntoIterator<Item = i16>,
J: IntoIterator<Item = T>,
F: FnMut(T, &mut Vec<u8>) -> Result<IsNull, Box<Error + marker::Sync + Send>>,
K: IntoIterator<Item = i16>
K: IntoIterator<Item = i16>,
{
buf.push(b'B');
@ -156,9 +161,11 @@ pub fn bind<I, J, F, T, K>(portal: &str,
buf.write_cstr(portal)?;
buf.write_cstr(statement)?;
write_counted(formats, |f, buf| buf.write_i16::<BigEndian>(f), buf)?;
write_counted(values,
write_counted(
values,
|v, buf| write_nullable(|buf| serializer(v, buf), buf),
buf)?;
buf,
)?;
write_counted(result_formats, |f, buf| buf.write_i16::<BigEndian>(f), buf)?;
Ok(())
@ -167,9 +174,10 @@ pub fn bind<I, J, F, T, K>(portal: &str,
#[inline]
fn write_counted<I, T, F, E>(items: I, mut serializer: F, buf: &mut Vec<u8>) -> Result<(), E>
where I: IntoIterator<Item = T>,
where
I: IntoIterator<Item = T>,
F: FnMut(T, &mut Vec<u8>) -> Result<(), E>,
E: From<io::Error>
E: From<io::Error>,
{
let base = buf.len();
buf.extend_from_slice(&[0; 2]);
@ -190,8 +198,7 @@ pub fn cancel_request(process_id: i32, secret_key: i32, buf: &mut Vec<u8>) {
buf.write_i32::<BigEndian>(80877102).unwrap();
buf.write_i32::<BigEndian>(process_id).unwrap();
buf.write_i32::<BigEndian>(secret_key)
})
.unwrap();
}).unwrap();
}
#[inline]
@ -246,7 +253,8 @@ pub fn execute(portal: &str, max_rows: i32, buf: &mut Vec<u8>) -> io::Result<()>
#[inline]
pub fn parse<I>(name: &str, query: &str, param_types: I, buf: &mut Vec<u8>) -> io::Result<()>
where I: IntoIterator<Item = Oid>
where
I: IntoIterator<Item = Oid>,
{
buf.push(b'P');
write_body(buf, |buf| {
@ -294,7 +302,8 @@ pub fn ssl_request(buf: &mut Vec<u8>) {
#[inline]
pub fn startup_message<'a, I>(parameters: I, buf: &mut Vec<u8>) -> io::Result<()>
where I: IntoIterator<Item = (&'a str, &'a str)>
where
I: IntoIterator<Item = (&'a str, &'a str)>,
{
write_body(buf, |buf| {
buf.write_i32::<BigEndian>(196608).unwrap();
@ -327,8 +336,10 @@ impl WriteCStr for Vec<u8> {
#[inline]
fn write_cstr(&mut self, s: &str) -> Result<(), io::Error> {
if s.as_bytes().contains(&0) {
return Err(io::Error::new(io::ErrorKind::InvalidInput,
"string contains embedded null"));
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"string contains embedded null",
));
}
self.extend_from_slice(s.as_bytes());
self.push(0);

View File

@ -168,7 +168,8 @@ pub fn float8_from_sql(mut buf: &[u8]) -> Result<f64, StdBox<Error + Sync + Send
/// Serializes an `HSTORE` value.
#[inline]
pub fn hstore_to_sql<'a, I>(values: I, buf: &mut Vec<u8>) -> Result<(), StdBox<Error + Sync + Send>>
where I: IntoIterator<Item = (&'a str, Option<&'a str>)>
where
I: IntoIterator<Item = (&'a str, Option<&'a str>)>,
{
let base = buf.len();
buf.extend_from_slice(&[0; 4]);
@ -204,8 +205,9 @@ fn write_pascal_string(s: &str, buf: &mut Vec<u8>) -> Result<(), StdBox<Error +
/// Deserializes an `HSTORE` value.
#[inline]
pub fn hstore_from_sql<'a>(mut buf: &'a [u8])
-> Result<HstoreEntries<'a>, StdBox<Error + Sync + Send>> {
pub fn hstore_from_sql<'a>(
mut buf: &'a [u8],
) -> Result<HstoreEntries<'a>, StdBox<Error + Sync + Send>> {
let count = buf.read_i32::<BigEndian>()?;
if count < 0 {
return Err("invalid entry count".into());
@ -268,11 +270,13 @@ impl<'a> FallibleIterator for HstoreEntries<'a> {
/// Serializes a `VARBIT` or `BIT` value.
#[inline]
pub fn varbit_to_sql<I>(len: usize,
pub fn varbit_to_sql<I>(
len: usize,
v: I,
buf: &mut Vec<u8>)
-> Result<(), StdBox<Error + Sync + Send>>
where I: Iterator<Item = u8>
buf: &mut Vec<u8>,
) -> Result<(), StdBox<Error + Sync + Send>>
where
I: Iterator<Item = u8>,
{
let len = i32::from_usize(len)?;
buf.write_i32::<BigEndian>(len).unwrap();
@ -418,16 +422,18 @@ pub fn uuid_from_sql(buf: &[u8]) -> Result<[u8; 16], StdBox<Error + Sync + Send>
/// Serializes an array value.
#[inline]
pub fn array_to_sql<T, I, J, F>(dimensions: I,
pub fn array_to_sql<T, I, J, F>(
dimensions: I,
has_nulls: bool,
element_type: Oid,
elements: J,
mut serializer: F,
buf: &mut Vec<u8>)
-> Result<(), StdBox<Error + Sync + Send>>
where I: IntoIterator<Item = ArrayDimension>,
buf: &mut Vec<u8>,
) -> Result<(), StdBox<Error + Sync + Send>>
where
I: IntoIterator<Item = ArrayDimension>,
J: IntoIterator<Item = T>,
F: FnMut(T, &mut Vec<u8>) -> Result<IsNull, StdBox<Error + Sync + Send>>
F: FnMut(T, &mut Vec<u8>) -> Result<IsNull, StdBox<Error + Sync + Send>>,
{
let dimensions_idx = buf.len();
buf.extend_from_slice(&[0; 4]);
@ -616,12 +622,14 @@ pub fn empty_range_to_sql(buf: &mut Vec<u8>) {
}
/// Serializes a range value.
pub fn range_to_sql<F, G>(lower: F,
pub fn range_to_sql<F, G>(
lower: F,
upper: G,
buf: &mut Vec<u8>)
-> Result<(), StdBox<Error + Sync + Send>>
where F: FnOnce(&mut Vec<u8>) -> Result<RangeBound<IsNull>, StdBox<Error + Sync + Send>>,
G: FnOnce(&mut Vec<u8>) -> Result<RangeBound<IsNull>, StdBox<Error + Sync + Send>>
buf: &mut Vec<u8>,
) -> Result<(), StdBox<Error + Sync + Send>>
where
F: FnOnce(&mut Vec<u8>) -> Result<RangeBound<IsNull>, StdBox<Error + Sync + Send>>,
G: FnOnce(&mut Vec<u8>) -> Result<RangeBound<IsNull>, StdBox<Error + Sync + Send>>,
{
let tag_idx = buf.len();
buf.push(0);
@ -644,10 +652,12 @@ pub fn range_to_sql<F, G>(lower: F,
Ok(())
}
fn write_bound<F>(bound: F,
buf: &mut Vec<u8>)
-> Result<RangeBound<()>, StdBox<Error + Sync + Send>>
where F: FnOnce(&mut Vec<u8>) -> Result<RangeBound<IsNull>, StdBox<Error + Sync + Send>>
fn write_bound<F>(
bound: F,
buf: &mut Vec<u8>,
) -> Result<RangeBound<()>, StdBox<Error + Sync + Send>>
where
F: FnOnce(&mut Vec<u8>) -> Result<RangeBound<IsNull>, StdBox<Error + Sync + Send>>,
{
let base = buf.len();
buf.extend_from_slice(&[0; 4]);
@ -707,11 +717,12 @@ pub fn range_from_sql<'a>(mut buf: &'a [u8]) -> Result<Range<'a>, StdBox<Error +
}
#[inline]
fn read_bound<'a>(buf: &mut &'a [u8],
fn read_bound<'a>(
buf: &mut &'a [u8],
tag: u8,
unbounded: u8,
inclusive: u8)
-> Result<RangeBound<Option<&'a [u8]>>, StdBox<Error + Sync + Send>> {
inclusive: u8,
) -> Result<RangeBound<Option<&'a [u8]>>, StdBox<Error + Sync + Send>> {
if tag & unbounded != 0 {
Ok(RangeBound::Unbounded)
} else {
@ -831,11 +842,13 @@ impl Box {
/// Serializes a Postgres path.
#[inline]
pub fn path_to_sql<I>(closed: bool,
pub fn path_to_sql<I>(
closed: bool,
points: I,
buf: &mut Vec<u8>)
-> Result<(), StdBox<Error + Sync + Send>>
where I: IntoIterator<Item = (f64, f64)>
buf: &mut Vec<u8>,
) -> Result<(), StdBox<Error + Sync + Send>>
where
I: IntoIterator<Item = (f64, f64)>,
{
buf.push(closed as u8);
let points_idx = buf.len();
@ -988,11 +1001,13 @@ mod test {
let mut buf = vec![];
hstore_to_sql(map.iter().map(|(&k, &v)| (k, v)), &mut buf).unwrap();
assert_eq!(hstore_from_sql(&buf)
assert_eq!(
hstore_from_sql(&buf)
.unwrap()
.collect::<HashMap<_, _>>()
.unwrap(),
map);
map
);
}
#[test]
@ -1009,18 +1024,21 @@ mod test {
#[test]
fn array() {
let dimensions = [ArrayDimension {
let dimensions = [
ArrayDimension {
len: 1,
lower_bound: 10,
},
ArrayDimension {
len: 2,
lower_bound: 0,
}];
},
];
let values = [None, Some(&b"hello"[..])];
let mut buf = vec![];
array_to_sql(dimensions.iter().cloned(),
array_to_sql(
dimensions.iter().cloned(),
true,
10,
values.iter().cloned(),
@ -1031,8 +1049,8 @@ mod test {
}
None => Ok(IsNull::Yes),
},
&mut buf)
.unwrap();
&mut buf,
).unwrap();
let array = array_from_sql(&buf).unwrap();
assert_eq!(array.has_nulls(), true);

View File

@ -168,14 +168,18 @@ impl DbError {
b'H' => hint = Some(field.value().to_owned()),
b'P' => {
normal_position = Some(field.value().parse::<u32>().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput,
"`P` field did not contain an integer")
io::Error::new(
io::ErrorKind::InvalidInput,
"`P` field did not contain an integer",
)
})?);
}
b'p' => {
internal_position = Some(field.value().parse::<u32>().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput,
"`p` field did not contain an integer")
io::Error::new(
io::ErrorKind::InvalidInput,
"`p` field did not contain an integer",
)
})?);
}
b'q' => internal_query = Some(field.value().to_owned()),
@ -188,18 +192,22 @@ impl DbError {
b'F' => file = Some(field.value().to_owned()),
b'L' => {
line = Some(field.value().parse::<u32>().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput,
"`L` field did not contain an integer")
io::Error::new(
io::ErrorKind::InvalidInput,
"`L` field did not contain an integer",
)
})?);
}
b'R' => routine = Some(field.value().to_owned()),
b'V' => {
parsed_severity = Some(Severity::from_str(field.value()).ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput,
"`V` field contained an invalid value")
io::Error::new(
io::ErrorKind::InvalidInput,
"`V` field contained an invalid value",
)
})?);
}
_ => {},
_ => {}
}
}
@ -208,10 +216,12 @@ impl DbError {
io::Error::new(io::ErrorKind::InvalidInput, "`S` field missing")
})?,
parsed_severity: parsed_severity,
code: code.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput,
"`C` field missing"))?,
message: message.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput,
"`M` field missing"))?,
code: code.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "`C` field missing")
})?,
message: message.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "`M` field missing")
})?,
detail: detail,
hint: hint,
position: match normal_position {
@ -222,8 +232,10 @@ impl DbError {
Some(ErrorPosition::Internal {
position: position,
query: internal_query.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput,
"`q` field missing but `p` field present")
io::Error::new(
io::ErrorKind::InvalidInput,
"`q` field missing but `p` field present",
)
})?,
})
}

View File

@ -168,7 +168,17 @@ impl IntoConnectParams for String {
impl IntoConnectParams for Url {
fn into_connect_params(self) -> Result<ConnectParams, Box<Error + Sync + Send>> {
let Url { host, port, user, path: url::Path { path, query: options, .. }, .. } = self;
let Url {
host,
port,
user,
path: url::Path {
path,
query: options,
..
},
..
} = self;
let mut builder = ConnectParams::builder();
@ -199,4 +209,3 @@ impl IntoConnectParams for Url {
Ok(builder.build(host))
}
}

View File

@ -32,14 +32,15 @@ pub struct UserInfo {
pub type Query = Vec<(String, String)>;
impl Url {
pub fn new(scheme: String,
pub fn new(
scheme: String,
user: Option<UserInfo>,
host: String,
port: Option<u16>,
path: String,
query: Query,
fragment: Option<String>)
-> Url {
fragment: Option<String>,
) -> Url {
Url {
scheme: scheme,
user: user,
@ -63,13 +64,15 @@ impl Url {
// query and fragment
let (query, fragment) = get_query_fragment(rest)?;
let url = Url::new(scheme.to_owned(),
let url = Url::new(
scheme.to_owned(),
userinfo,
host.to_owned(),
port,
path,
query,
fragment);
fragment,
);
Ok(url)
}
}
@ -125,19 +128,23 @@ fn decode_inner(c: &str, full_url: bool) -> DecodeResult<String> {
let bytes = match (iter.next(), iter.next()) {
(Some(one), Some(two)) => [one, two],
_ => {
return Err("Malformed input: found '%' without two \
return Err(
"Malformed input: found '%' without two \
trailing bytes"
.to_owned())
.to_owned(),
)
}
};
let bytes_from_hex = match Vec::<u8>::from_hex(&bytes) {
Ok(b) => b,
_ => {
return Err("Malformed input: found '%' followed by \
return Err(
"Malformed input: found '%' followed by \
invalid hex values. Character '%' must \
escaped."
.to_owned())
.to_owned(),
)
}
};
@ -247,9 +254,7 @@ fn get_authority(rawurl: &str) -> DecodeResult<(Option<UserInfo>, &str, Option<u
let mut begin = 2;
let mut end = len;
for (i, c) in rawurl.chars()
.enumerate()
.skip(2) {
for (i, c) in rawurl.chars().enumerate().skip(2) {
// deal with input class first
match c {
'0'...'9' => (),
@ -390,7 +395,9 @@ fn get_path(rawurl: &str, is_authority: bool) -> DecodeResult<(String, &str)> {
}
if is_authority && end != 0 && !rawurl.starts_with('/') {
Err("Non-empty path must begin with '/' in presence of authority.".to_owned())
Err(
"Non-empty path must begin with '/' in presence of authority.".to_owned(),
)
} else {
Ok((decode_component(&rawurl[0..end])?, &rawurl[end..len]))
}
@ -409,7 +416,10 @@ fn get_query_fragment(rawurl: &str) -> DecodeResult<(Query, Option<String>)> {
match before_fragment.chars().next() {
Some('?') => Ok((query_from_str(&before_fragment[1..])?, fragment)),
None => Ok((vec![], fragment)),
_ => Err(format!("Query didn't start with '?': '{}..'", before_fragment)),
_ => Err(format!(
"Query didn't start with '?': '{}..'",
before_fragment
)),
}
}

View File

@ -1,4 +1,4 @@
use fallible_iterator::{FallibleIterator};
use fallible_iterator::FallibleIterator;
use postgres_protocol::message::backend::DataRowBody;
use std::ascii::AsciiExt;
use std::io;
@ -34,13 +34,15 @@ impl<'a> RowIndex for str {
// FIXME ASCII-only case insensitivity isn't really the right thing to
// do. Postgres itself uses a dubious wrapper around tolower and JDBC
// uses the US locale.
stmt.iter()
.position(|d| d.name().eq_ignore_ascii_case(self))
stmt.iter().position(
|d| d.name().eq_ignore_ascii_case(self),
)
}
}
impl<'a, T: ?Sized> RowIndex for &'a T
where T: RowIndex
where
T: RowIndex,
{
#[inline]
fn idx(&self, columns: &[Column]) -> Option<usize> {

View File

@ -21,10 +21,7 @@ impl FromSql for BitVec {
}
impl ToSql for BitVec {
fn to_sql(&self,
_: &Type,
mut out: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, _: &Type, mut out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
types::varbit_to_sql(self.len(), self.to_bytes().into_iter(), out)?;
Ok(IsNull::No)
}

View File

@ -12,9 +12,7 @@ fn base() -> NaiveDateTime {
}
impl FromSql for NaiveDateTime {
fn from_sql(_: &Type,
raw: &[u8])
-> Result<NaiveDateTime, Box<Error + Sync + Send>> {
fn from_sql(_: &Type, raw: &[u8]) -> Result<NaiveDateTime, Box<Error + Sync + Send>> {
let t = types::timestamp_from_sql(raw)?;
Ok(base() + Duration::microseconds(t))
}
@ -23,10 +21,7 @@ impl FromSql for NaiveDateTime {
}
impl ToSql for NaiveDateTime {
fn to_sql(&self,
_: &Type,
w: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
let time = match self.signed_duration_since(base()).num_microseconds() {
Some(time) => time,
None => return Err("value too large to transmit".into()),
@ -40,9 +35,7 @@ impl ToSql for NaiveDateTime {
}
impl FromSql for DateTime<Utc> {
fn from_sql(type_: &Type,
raw: &[u8])
-> Result<DateTime<Utc>, Box<Error + Sync + Send>> {
fn from_sql(type_: &Type, raw: &[u8]) -> Result<DateTime<Utc>, Box<Error + Sync + Send>> {
let naive = NaiveDateTime::from_sql(type_, raw)?;
Ok(DateTime::from_utc(naive, Utc))
}
@ -51,10 +44,7 @@ impl FromSql for DateTime<Utc> {
}
impl ToSql for DateTime<Utc> {
fn to_sql(&self,
type_: &Type,
w: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, type_: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
self.naive_utc().to_sql(type_, w)
}
@ -63,9 +53,7 @@ impl ToSql for DateTime<Utc> {
}
impl FromSql for DateTime<Local> {
fn from_sql(type_: &Type,
raw: &[u8])
-> Result<DateTime<Local>, Box<Error + Sync + Send>> {
fn from_sql(type_: &Type, raw: &[u8]) -> Result<DateTime<Local>, Box<Error + Sync + Send>> {
let utc = DateTime::<Utc>::from_sql(type_, raw)?;
Ok(utc.with_timezone(&Local))
}
@ -74,10 +62,11 @@ impl FromSql for DateTime<Local> {
}
impl ToSql for DateTime<Local> {
fn to_sql(&self,
fn to_sql(
&self,
type_: &Type,
mut w: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
mut w: &mut Vec<u8>,
) -> Result<IsNull, Box<Error + Sync + Send>> {
self.with_timezone(&Utc).to_sql(type_, w)
}
@ -86,9 +75,10 @@ impl ToSql for DateTime<Local> {
}
impl FromSql for DateTime<FixedOffset> {
fn from_sql(type_: &Type,
raw: &[u8])
-> Result<DateTime<FixedOffset>, Box<Error + Sync + Send>> {
fn from_sql(
type_: &Type,
raw: &[u8],
) -> Result<DateTime<FixedOffset>, Box<Error + Sync + Send>> {
let utc = DateTime::<Utc>::from_sql(type_, raw)?;
Ok(utc.with_timezone(&FixedOffset::east(0)))
}
@ -97,10 +87,7 @@ impl FromSql for DateTime<FixedOffset> {
}
impl ToSql for DateTime<FixedOffset> {
fn to_sql(&self,
type_: &Type,
w: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, type_: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
self.with_timezone(&Utc).to_sql(type_, w)
}
@ -109,9 +96,7 @@ impl ToSql for DateTime<FixedOffset> {
}
impl FromSql for NaiveDate {
fn from_sql(_: &Type,
raw: &[u8])
-> Result<NaiveDate, Box<Error + Sync + Send>> {
fn from_sql(_: &Type, raw: &[u8]) -> Result<NaiveDate, Box<Error + Sync + Send>> {
let jd = types::date_from_sql(raw)?;
Ok(base().date() + Duration::days(jd as i64))
}
@ -120,10 +105,7 @@ impl FromSql for NaiveDate {
}
impl ToSql for NaiveDate {
fn to_sql(&self,
_: &Type,
w: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
let jd = self.signed_duration_since(base().date()).num_days();
if jd > i32::max_value() as i64 || jd < i32::min_value() as i64 {
return Err("value too large to transmit".into());
@ -138,9 +120,7 @@ impl ToSql for NaiveDate {
}
impl FromSql for NaiveTime {
fn from_sql(_: &Type,
raw: &[u8])
-> Result<NaiveTime, Box<Error + Sync + Send>> {
fn from_sql(_: &Type, raw: &[u8]) -> Result<NaiveTime, Box<Error + Sync + Send>> {
let usec = types::time_from_sql(raw)?;
Ok(NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(usec))
}
@ -149,10 +129,7 @@ impl FromSql for NaiveTime {
}
impl ToSql for NaiveTime {
fn to_sql(&self,
_: &Type,
w: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
let delta = self.signed_duration_since(NaiveTime::from_hms(0, 0, 0));
let time = match delta.num_microseconds() {
Some(time) => time,

View File

@ -7,9 +7,7 @@ use postgres_protocol::types;
use types::{FromSql, ToSql, Type, IsNull};
impl FromSql for MacAddress {
fn from_sql(_: &Type,
raw: &[u8])
-> Result<MacAddress, Box<Error + Sync + Send>> {
fn from_sql(_: &Type, raw: &[u8]) -> Result<MacAddress, Box<Error + Sync + Send>> {
let bytes = types::macaddr_from_sql(raw)?;
Ok(MacAddress::new(bytes))
}
@ -18,10 +16,7 @@ impl FromSql for MacAddress {
}
impl ToSql for MacAddress {
fn to_sql(&self,
_: &Type,
w: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
let mut bytes = [0; 6];
bytes.copy_from_slice(self.as_bytes());
types::macaddr_to_sql(bytes, w);

View File

@ -47,11 +47,13 @@ macro_rules! to_sql_checked {
// WARNING: this function is not considered part of this crate's public API.
// It is subject to change at any time.
#[doc(hidden)]
pub fn __to_sql_checked<T>(v: &T,
pub fn __to_sql_checked<T>(
v: &T,
ty: &Type,
out: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>>
where T: ToSql
out: &mut Vec<u8>,
) -> Result<IsNull, Box<Error + Sync + Send>>
where
T: ToSql,
{
if !T::accepts(ty) {
return Err(Box::new(WrongType(ty.clone())));
@ -210,9 +212,11 @@ pub struct WrongType(Type);
impl fmt::Display for WrongType {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt,
write!(
fmt,
"cannot convert to or from a Postgres value of type `{}`",
self.0)
self.0
)
}
}
@ -402,9 +406,10 @@ simple_from!(f32, float4_from_sql, Type::Float4);
simple_from!(f64, float8_from_sql, Type::Float8);
impl FromSql for HashMap<String, Option<String>> {
fn from_sql(_: &Type,
raw: &[u8])
-> Result<HashMap<String, Option<String>>, Box<Error + Sync + Send>> {
fn from_sql(
_: &Type,
raw: &[u8],
) -> Result<HashMap<String, Option<String>>, Box<Error + Sync + Send>> {
types::hstore_from_sql(raw)?
.map(|(k, v)| (k.to_owned(), v.map(str::to_owned)))
.collect()
@ -492,24 +497,29 @@ pub trait ToSql: fmt::Debug {
/// `NULL`. If this is the case, implementations **must not** write
/// anything to `out`.
fn to_sql(&self, ty: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>>
where Self: Sized;
where
Self: Sized;
/// Determines if a value of this type can be converted to the specified
/// Postgres `Type`.
fn accepts(ty: &Type) -> bool where Self: Sized;
fn accepts(ty: &Type) -> bool
where
Self: Sized;
/// An adaptor method used internally by Rust-Postgres.
///
/// *All* implementations of this method should be generated by the
/// `to_sql_checked!()` macro.
fn to_sql_checked(&self,
fn to_sql_checked(
&self,
ty: &Type,
out: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>>;
out: &mut Vec<u8>,
) -> Result<IsNull, Box<Error + Sync + Send>>;
}
impl<'a, T> ToSql for &'a T
where T: ToSql
where
T: ToSql,
{
fn to_sql(&self, ty: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
(*self).to_sql(ty, out)
@ -549,7 +559,8 @@ impl<'a, T: ToSql> ToSql for &'a [T] {
lower_bound: 1,
};
types::array_to_sql(Some(dimension),
types::array_to_sql(
Some(dimension),
true,
member_type.oid(),
self.iter(),
@ -557,7 +568,8 @@ impl<'a, T: ToSql> ToSql for &'a [T] {
IsNull::No => Ok(postgres_protocol::IsNull::No),
IsNull::Yes => Ok(postgres_protocol::IsNull::Yes),
},
w)?;
w,
)?;
Ok(IsNull::No)
}
@ -664,8 +676,10 @@ simple_to!(f64, float8_to_sql, Type::Float8);
impl ToSql for HashMap<String, Option<String>> {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
types::hstore_to_sql(self.iter().map(|(k, v)| (&**k, v.as_ref().map(|v| &**v))),
w)?;
types::hstore_to_sql(
self.iter().map(|(k, v)| (&**k, v.as_ref().map(|v| &**v))),
w,
)?;
Ok(IsNull::No)
}

View File

@ -7,9 +7,7 @@ use std::error::Error;
use types::{FromSql, ToSql, IsNull, Type};
impl FromSql for json::Json {
fn from_sql(ty: &Type,
mut raw: &[u8])
-> Result<json::Json, Box<Error + Sync + Send>> {
fn from_sql(ty: &Type, mut raw: &[u8]) -> Result<json::Json, Box<Error + Sync + Send>> {
if let Type::Jsonb = *ty {
let mut b = [0; 1];
raw.read_exact(&mut b)?;
@ -25,10 +23,7 @@ impl FromSql for json::Json {
}
impl ToSql for json::Json {
fn to_sql(&self,
ty: &Type,
mut out: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, ty: &Type, mut out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
if let Type::Jsonb = *ty {
out.push(1);
}

View File

@ -7,9 +7,7 @@ use std::io::{Read, Write};
use types::{FromSql, ToSql, IsNull, Type};
impl FromSql for Value {
fn from_sql(ty: &Type,
mut raw: &[u8])
-> Result<Value, Box<Error + Sync + Send>> {
fn from_sql(ty: &Type, mut raw: &[u8]) -> Result<Value, Box<Error + Sync + Send>> {
if let Type::Jsonb = *ty {
let mut b = [0; 1];
raw.read_exact(&mut b)?;
@ -25,10 +23,7 @@ impl FromSql for Value {
}
impl ToSql for Value {
fn to_sql(&self,
ty: &Type,
mut out: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, ty: &Type, mut out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
if let Type::Jsonb = *ty {
out.push(1);
}

View File

@ -16,9 +16,7 @@ pub enum Date<T> {
}
impl<T: FromSql> FromSql for Date<T> {
fn from_sql(ty: &Type,
raw: &[u8])
-> Result<Self, Box<Error + Sync + Send>> {
fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
match types::date_from_sql(raw)? {
i32::MAX => Ok(Date::PosInfinity),
i32::MIN => Ok(Date::NegInfinity),
@ -31,10 +29,7 @@ impl<T: FromSql> FromSql for Date<T> {
}
}
impl<T: ToSql> ToSql for Date<T> {
fn to_sql(&self,
ty: &Type,
out: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, ty: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
let value = match *self {
Date::PosInfinity => i32::MAX,
Date::NegInfinity => i32::MIN,
@ -65,9 +60,7 @@ pub enum Timestamp<T> {
}
impl<T: FromSql> FromSql for Timestamp<T> {
fn from_sql(ty: &Type,
raw: &[u8])
-> Result<Self, Box<Error + Sync + Send>> {
fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<Error + Sync + Send>> {
match types::timestamp_from_sql(raw)? {
i64::MAX => Ok(Timestamp::PosInfinity),
i64::MIN => Ok(Timestamp::NegInfinity),
@ -81,10 +74,7 @@ impl<T: FromSql> FromSql for Timestamp<T> {
}
impl<T: ToSql> ToSql for Timestamp<T> {
fn to_sql(&self,
ty: &Type,
out: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, ty: &Type, out: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
let value = match *self {
Timestamp::PosInfinity => i64::MAX,
Timestamp::NegInfinity => i64::MIN,

View File

@ -13,9 +13,7 @@ const NSEC_PER_USEC: i64 = 1_000;
const TIME_SEC_CONVERSION: i64 = 946684800;
impl FromSql for Timespec {
fn from_sql(_: &Type,
raw: &[u8])
-> Result<Timespec, Box<Error + Sync + Send>> {
fn from_sql(_: &Type, raw: &[u8]) -> Result<Timespec, Box<Error + Sync + Send>> {
let t = types::timestamp_from_sql(raw)?;
let mut sec = t / USEC_PER_SEC + TIME_SEC_CONVERSION;
let mut usec = t % USEC_PER_SEC;
@ -32,10 +30,7 @@ impl FromSql for Timespec {
}
impl ToSql for Timespec {
fn to_sql(&self,
_: &Type,
w: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
let t = (self.sec - TIME_SEC_CONVERSION) * USEC_PER_SEC + self.nsec as i64 / NSEC_PER_USEC;
types::timestamp_to_sql(t, w);
Ok(IsNull::No)

View File

@ -16,10 +16,7 @@ impl FromSql for Uuid {
}
impl ToSql for Uuid {
fn to_sql(&self,
_: &Type,
w: &mut Vec<u8>)
-> Result<IsNull, Box<Error + Sync + Send>> {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
types::uuid_to_sql(*self.as_bytes(), w);
Ok(IsNull::No)
}

View File

@ -7,7 +7,8 @@ use postgres::{Connection, TlsMode};
#[bench]
fn bench_naiive_execute(b: &mut test::Bencher) {
let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();
conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[]).unwrap();
conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[])
.unwrap();
b.iter(|| {
let stmt = conn.prepare("UPDATE foo SET id = 1").unwrap();
@ -20,9 +21,8 @@ fn bench_naiive_execute(b: &mut test::Bencher) {
#[bench]
fn bench_execute(b: &mut test::Bencher) {
let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();
conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[]).unwrap();
conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[])
.unwrap();
b.iter(|| {
conn.execute("UPDATE foo SET id = 1", &[]).unwrap()
});
b.iter(|| conn.execute("UPDATE foo SET id = 1", &[]).unwrap());
}

View File

@ -178,15 +178,17 @@ impl HandleNotice for LoggingNoticeHandler {
/// });
/// postgres::cancel_query(url, TlsMode::None, &cancel_data).unwrap();
/// ```
pub fn cancel_query<T>(params: T,
pub fn cancel_query<T>(
params: T,
tls: TlsMode,
data: &CancelData)
-> result::Result<(), ConnectError>
where T: IntoConnectParams
data: &CancelData,
) -> result::Result<(), ConnectError>
where
T: IntoConnectParams,
{
let params = params
.into_connect_params()
.map_err(ConnectError::ConnectParams)?;
let params = params.into_connect_params().map_err(
ConnectError::ConnectParams,
)?;
let mut socket = priv_io::initialize_stream(&params, tls)?;
let mut buf = vec![];
@ -198,13 +200,17 @@ pub fn cancel_query<T>(params: T,
}
fn bad_response() -> io::Error {
io::Error::new(io::ErrorKind::InvalidInput,
"the server returned an unexpected response")
io::Error::new(
io::ErrorKind::InvalidInput,
"the server returned an unexpected response",
)
}
fn desynchronized() -> io::Error {
io::Error::new(io::ErrorKind::Other,
"communication with the server has desynchronized due to an earlier IO error")
io::Error::new(
io::ErrorKind::Other,
"communication with the server has desynchronized due to an earlier IO error",
)
}
/// Specifies the TLS support requested for a new connection.
@ -252,18 +258,20 @@ impl Drop for InnerConnection {
impl InnerConnection {
fn connect<T>(params: T, tls: TlsMode) -> result::Result<InnerConnection, ConnectError>
where T: IntoConnectParams
where
T: IntoConnectParams,
{
let params = params
.into_connect_params()
.map_err(ConnectError::ConnectParams)?;
let params = params.into_connect_params().map_err(
ConnectError::ConnectParams,
)?;
let stream = priv_io::initialize_stream(&params, tls)?;
let user = match params.user() {
Some(user) => user,
None => {
return Err(ConnectError::ConnectParams("User missing from connection parameters"
.into()));
return Err(ConnectError::ConnectParams(
"User missing from connection parameters".into(),
));
}
};
@ -299,8 +307,9 @@ impl InnerConnection {
}
let options = options.iter().map(|&(ref a, ref b)| (&**a, &**b));
conn.stream
.write_message(|buf| frontend::startup_message(options, buf))?;
conn.stream.write_message(
|buf| frontend::startup_message(options, buf),
)?;
conn.stream.flush()?;
conn.handle_auth(user)?;
@ -332,17 +341,20 @@ impl InnerConnection {
}
}
backend::Message::ParameterStatus(body) => {
self.parameters
.insert(body.name()?.to_owned(), body.value()?.to_owned());
self.parameters.insert(
body.name()?.to_owned(),
body.value()?.to_owned(),
);
}
val => return Ok(val),
}
}
}
fn read_message_with_notification_timeout(&mut self,
timeout: Duration)
-> io::Result<Option<backend::Message>> {
fn read_message_with_notification_timeout(
&mut self,
timeout: Duration,
) -> io::Result<Option<backend::Message>> {
debug_assert!(!self.desynchronized);
loop {
match try_desync!(self, self.stream.read_message_timeout(timeout)) {
@ -352,16 +364,19 @@ impl InnerConnection {
}
}
Some(backend::Message::ParameterStatus(body)) => {
self.parameters
.insert(body.name()?.to_owned(), body.value()?.to_owned());
self.parameters.insert(
body.name()?.to_owned(),
body.value()?.to_owned(),
);
}
val => return Ok(val),
}
}
}
fn read_message_with_notification_nonblocking(&mut self)
-> io::Result<Option<backend::Message>> {
fn read_message_with_notification_nonblocking(
&mut self,
) -> io::Result<Option<backend::Message>> {
debug_assert!(!self.desynchronized);
loop {
match try_desync!(self, self.stream.read_message_nonblocking()) {
@ -371,8 +386,10 @@ impl InnerConnection {
}
}
Some(backend::Message::ParameterStatus(body)) => {
self.parameters
.insert(body.name()?.to_owned(), body.value()?.to_owned());
self.parameters.insert(
body.name()?.to_owned(),
body.value()?.to_owned(),
);
}
val => return Ok(val),
}
@ -383,8 +400,7 @@ impl InnerConnection {
loop {
match self.read_message_with_notification()? {
backend::Message::NotificationResponse(body) => {
self.notifications
.push_back(Notification {
self.notifications.push_back(Notification {
process_id: body.process_id(),
channel: body.channel()?.to_owned(),
payload: body.message()?.to_owned(),
@ -399,49 +415,45 @@ impl InnerConnection {
match self.read_message()? {
backend::Message::AuthenticationOk => return Ok(()),
backend::Message::AuthenticationCleartextPassword => {
let pass = user.password()
.ok_or_else(|| {
ConnectError::ConnectParams("a password was requested but not provided"
.into())
let pass = user.password().ok_or_else(|| {
ConnectError::ConnectParams("a password was requested but not provided".into())
})?;
self.stream
.write_message(|buf| frontend::password_message(pass, buf))?;
self.stream.write_message(
|buf| frontend::password_message(pass, buf),
)?;
self.stream.flush()?;
}
backend::Message::AuthenticationMd5Password(body) => {
let pass = user.password()
.ok_or_else(|| {
ConnectError::ConnectParams("a password was requested but not provided"
.into())
let pass = user.password().ok_or_else(|| {
ConnectError::ConnectParams("a password was requested but not provided".into())
})?;
let output =
authentication::md5_hash(user.name().as_bytes(), pass.as_bytes(), body.salt());
self.stream
.write_message(|buf| frontend::password_message(&output, buf))?;
self.stream.write_message(
|buf| frontend::password_message(&output, buf),
)?;
self.stream.flush()?;
}
backend::Message::AuthenticationSasl(body) => {
// count to validate the entire message body.
if body.mechanisms()
.filter(|m| *m == sasl::SCRAM_SHA_256)
.count()? == 0 {
return Err(ConnectError::Io(io::Error::new(io::ErrorKind::Other,
"unsupported authentication")));
.count()? == 0
{
return Err(ConnectError::Io(io::Error::new(
io::ErrorKind::Other,
"unsupported authentication",
)));
}
let pass = user.password()
.ok_or_else(|| {
ConnectError::ConnectParams("a password was requested but not provided"
.into())
let pass = user.password().ok_or_else(|| {
ConnectError::ConnectParams("a password was requested but not provided".into())
})?;
let mut scram = ScramSha256::new(pass.as_bytes())?;
self.stream
.write_message(|buf| {
frontend::sasl_initial_response(sasl::SCRAM_SHA_256,
scram.message(),
buf)
self.stream.write_message(|buf| {
frontend::sasl_initial_response(sasl::SCRAM_SHA_256, scram.message(), buf)
})?;
self.stream.flush()?;
@ -455,8 +467,9 @@ impl InnerConnection {
scram.update(body.data())?;
self.stream
.write_message(|buf| frontend::sasl_response(scram.message(), buf))?;
self.stream.write_message(|buf| {
frontend::sasl_response(scram.message(), buf)
})?;
self.stream.flush()?;
let body = match self.read_message()? {
@ -473,8 +486,10 @@ impl InnerConnection {
backend::Message::AuthenticationScmCredential |
backend::Message::AuthenticationGss |
backend::Message::AuthenticationSspi => {
return Err(ConnectError::Io(io::Error::new(io::ErrorKind::Other,
"unsupported authentication")))
return Err(ConnectError::Io(io::Error::new(
io::ErrorKind::Other,
"unsupported authentication",
)))
}
backend::Message::ErrorResponse(body) => return Err(connect_err(&mut body.fields())),
_ => return Err(ConnectError::Io(bad_response())),
@ -494,12 +509,15 @@ impl InnerConnection {
fn raw_prepare(&mut self, stmt_name: &str, query: &str) -> Result<(Vec<Type>, Vec<Column>)> {
debug!("preparing query with name `{}`: {}", stmt_name, query);
self.stream
.write_message(|buf| frontend::parse(stmt_name, query, None, buf))?;
self.stream
.write_message(|buf| frontend::describe(b'S', stmt_name, buf))?;
self.stream
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?;
self.stream.write_message(|buf| {
frontend::parse(stmt_name, query, None, buf)
})?;
self.stream.write_message(
|buf| frontend::describe(b'S', stmt_name, buf),
)?;
self.stream.write_message(
|buf| Ok::<(), io::Error>(frontend::sync(buf)),
)?;
self.stream.flush()?;
match self.read_message()? {
@ -534,8 +552,10 @@ impl InnerConnection {
Some(body) => {
body.fields()
.and_then(|field| {
Ok(Column::new(field.name().to_owned(),
self.get_type(field.type_oid())?))
Ok(Column::new(
field.name().to_owned(),
self.get_type(field.type_oid())?,
))
})
.collect()?
}
@ -546,7 +566,8 @@ impl InnerConnection {
}
fn read_rows<F>(&mut self, mut consumer: F) -> Result<bool>
where F: FnMut(RowData)
where
F: FnMut(RowData),
{
let more_rows;
loop {
@ -566,12 +587,12 @@ impl InnerConnection {
return Err(err(&mut body.fields()));
}
backend::Message::CopyInResponse(_) => {
self.stream
.write_message(|buf| {
self.stream.write_message(|buf| {
frontend::copy_fail("COPY queries cannot be directly executed", buf)
})?;
self.stream
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?;
self.stream.write_message(
|buf| Ok::<(), io::Error>(frontend::sync(buf)),
)?;
self.stream.flush()?;
}
backend::Message::CopyOutResponse(_) => {
@ -580,9 +601,11 @@ impl InnerConnection {
break;
}
}
return Err(Error::Io(io::Error::new(io::ErrorKind::InvalidInput,
return Err(Error::Io(io::Error::new(
io::ErrorKind::InvalidInput,
"COPY queries cannot be directly \
executed")));
executed",
)));
}
_ => {
self.desynchronized = true;
@ -594,25 +617,30 @@ impl InnerConnection {
Ok(more_rows)
}
fn raw_execute(&mut self,
fn raw_execute(
&mut self,
stmt_name: &str,
portal_name: &str,
row_limit: i32,
param_types: &[Type],
params: &[&ToSql])
-> Result<()> {
assert!(param_types.len() == params.len(),
params: &[&ToSql],
) -> Result<()> {
assert!(
param_types.len() == params.len(),
"expected {} parameters but got {}",
param_types.len(),
params.len());
debug!("executing statement {} with parameters: {:?}",
params.len()
);
debug!(
"executing statement {} with parameters: {:?}",
stmt_name,
params);
params
);
{
let r = self.stream
.write_message(|buf| {
frontend::bind(portal_name,
let r = self.stream.write_message(|buf| {
frontend::bind(
portal_name,
stmt_name,
Some(1),
params.iter().zip(param_types),
@ -622,7 +650,8 @@ impl InnerConnection {
Err(e) => Err(e),
},
Some(1),
buf)
buf,
)
});
match r {
Ok(()) => {}
@ -631,10 +660,12 @@ impl InnerConnection {
}
}
self.stream
.write_message(|buf| frontend::execute(portal_name, row_limit, buf))?;
self.stream
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?;
self.stream.write_message(|buf| {
frontend::execute(portal_name, row_limit, buf)
})?;
self.stream.write_message(
|buf| Ok::<(), io::Error>(frontend::sync(buf)),
)?;
self.stream.flush()?;
match self.read_message()? {
@ -680,8 +711,10 @@ impl InnerConnection {
param_types: param_types,
columns: columns,
});
self.cached_statements
.insert(query.to_owned(), info.clone());
self.cached_statements.insert(
query.to_owned(),
info.clone(),
);
info
}
};
@ -690,10 +723,12 @@ impl InnerConnection {
}
fn close_statement(&mut self, name: &str, type_: u8) -> Result<()> {
self.stream
.write_message(|buf| frontend::close(type_, name, buf))?;
self.stream
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?;
self.stream.write_message(
|buf| frontend::close(type_, name, buf),
)?;
self.stream.write_message(
|buf| Ok::<(), io::Error>(frontend::sync(buf)),
)?;
self.stream.flush()?;
let resp = match self.read_message()? {
backend::Message::CloseComplete => Ok(()),
@ -723,7 +758,8 @@ impl InnerConnection {
return Ok(());
}
match self.raw_prepare(TYPEINFO_QUERY,
match self.raw_prepare(
TYPEINFO_QUERY,
"SELECT t.typname, t.typtype, t.typelem, r.rngsubtype, \
t.typbasetype, n.nspname, t.typrelid \
FROM pg_catalog.pg_type t \
@ -731,17 +767,20 @@ impl InnerConnection {
r.rngtypid = t.oid \
INNER JOIN pg_catalog.pg_namespace n ON \
t.typnamespace = n.oid \
WHERE t.oid = $1") {
WHERE t.oid = $1",
) {
Ok(..) => {}
// Range types weren't added until Postgres 9.2, so pg_range may not exist
Err(Error::Db(ref e)) if e.code == SqlState::UndefinedTable => {
self.raw_prepare(TYPEINFO_QUERY,
self.raw_prepare(
TYPEINFO_QUERY,
"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")?;
WHERE t.oid = $1",
)?;
}
Err(e) => return Err(e),
}
@ -753,27 +792,39 @@ impl InnerConnection {
#[allow(if_not_else)]
fn read_type(&mut self, oid: Oid) -> Result<Other> {
self.setup_typeinfo_query()?;
self.raw_execute(TYPEINFO_QUERY, "", 0, &[Type::Oid], &[&oid])?;
self.raw_execute(
TYPEINFO_QUERY,
"",
0,
&[Type::Oid],
&[&oid],
)?;
let mut row = None;
self.read_rows(|r| row = Some(r))?;
let get_raw = |i: usize| row.as_ref().and_then(|r| r.get(i));
let (name, type_, elem_oid, rngsubtype, basetype, schema, relid) = {
let name = String::from_sql_nullable(&Type::Name, get_raw(0))
.map_err(Error::Conversion)?;
let type_ = i8::from_sql_nullable(&Type::Char, get_raw(1))
.map_err(Error::Conversion)?;
let elem_oid = Oid::from_sql_nullable(&Type::Oid, get_raw(2))
.map_err(Error::Conversion)?;
let name = String::from_sql_nullable(&Type::Name, get_raw(0)).map_err(
Error::Conversion,
)?;
let type_ = i8::from_sql_nullable(&Type::Char, get_raw(1)).map_err(
Error::Conversion,
)?;
let elem_oid = Oid::from_sql_nullable(&Type::Oid, get_raw(2)).map_err(
Error::Conversion,
)?;
let rngsubtype = Option::<Oid>::from_sql_nullable(&Type::Oid, get_raw(3))
.map_err(Error::Conversion)?;
let basetype = Oid::from_sql_nullable(&Type::Oid, get_raw(4))
.map_err(Error::Conversion)?;
let schema = String::from_sql_nullable(&Type::Name, get_raw(5))
.map_err(Error::Conversion)?;
let relid = Oid::from_sql_nullable(&Type::Oid, get_raw(6))
.map_err(Error::Conversion)?;
let basetype = Oid::from_sql_nullable(&Type::Oid, get_raw(4)).map_err(
Error::Conversion,
)?;
let schema = String::from_sql_nullable(&Type::Name, get_raw(5)).map_err(
Error::Conversion,
)?;
let relid = Oid::from_sql_nullable(&Type::Oid, get_raw(6)).map_err(
Error::Conversion,
)?;
(name, type_, elem_oid, rngsubtype, basetype, schema, relid)
};
@ -802,19 +853,23 @@ impl InnerConnection {
return Ok(());
}
match self.raw_prepare(TYPEINFO_ENUM_QUERY,
match self.raw_prepare(
TYPEINFO_ENUM_QUERY,
"SELECT enumlabel \
FROM pg_catalog.pg_enum \
WHERE enumtypid = $1 \
ORDER BY enumsortorder") {
ORDER BY enumsortorder",
) {
Ok(..) => {}
// Postgres 9.0 doesn't have enumsortorder
Err(Error::Db(ref e)) if e.code == SqlState::UndefinedColumn => {
self.raw_prepare(TYPEINFO_ENUM_QUERY,
self.raw_prepare(
TYPEINFO_ENUM_QUERY,
"SELECT enumlabel \
FROM pg_catalog.pg_enum \
WHERE enumtypid = $1 \
ORDER BY oid")?;
ORDER BY oid",
)?;
}
Err(e) => return Err(e),
}
@ -825,14 +880,21 @@ impl InnerConnection {
fn read_enum_variants(&mut self, oid: Oid) -> Result<Vec<String>> {
self.setup_typeinfo_enum_query()?;
self.raw_execute(TYPEINFO_ENUM_QUERY, "", 0, &[Type::Oid], &[&oid])?;
self.raw_execute(
TYPEINFO_ENUM_QUERY,
"",
0,
&[Type::Oid],
&[&oid],
)?;
let mut rows = vec![];
self.read_rows(|row| rows.push(row))?;
let mut variants = vec![];
for row in rows {
variants.push(String::from_sql_nullable(&Type::Name, row.get(0))
.map_err(Error::Conversion)?);
variants.push(String::from_sql_nullable(&Type::Name, row.get(0)).map_err(
Error::Conversion,
)?);
}
Ok(variants)
@ -843,13 +905,15 @@ impl InnerConnection {
return Ok(());
}
self.raw_prepare(TYPEINFO_COMPOSITE_QUERY,
self.raw_prepare(
TYPEINFO_COMPOSITE_QUERY,
"SELECT attname, atttypid \
FROM pg_catalog.pg_attribute \
WHERE attrelid = $1 \
AND NOT attisdropped \
AND attnum > 0 \
ORDER BY attnum")?;
ORDER BY attnum",
)?;
self.has_typeinfo_composite_query = true;
Ok(())
@ -857,17 +921,25 @@ impl InnerConnection {
fn read_composite_fields(&mut self, relid: Oid) -> Result<Vec<Field>> {
self.setup_typeinfo_composite_query()?;
self.raw_execute(TYPEINFO_COMPOSITE_QUERY, "", 0, &[Type::Oid], &[&relid])?;
self.raw_execute(
TYPEINFO_COMPOSITE_QUERY,
"",
0,
&[Type::Oid],
&[&relid],
)?;
let mut rows = vec![];
self.read_rows(|row| rows.push(row))?;
let mut fields = vec![];
for row in rows {
let (name, type_) = {
let name = String::from_sql_nullable(&Type::Name, row.get(0))
.map_err(Error::Conversion)?;
let type_ = Oid::from_sql_nullable(&Type::Oid, row.get(1))
.map_err(Error::Conversion)?;
let name = String::from_sql_nullable(&Type::Name, row.get(0)).map_err(
Error::Conversion,
)?;
let type_ = Oid::from_sql_nullable(&Type::Oid, row.get(1)).map_err(
Error::Conversion,
)?;
(name, type_)
};
let type_ = self.get_type(type_)?;
@ -892,8 +964,7 @@ impl InnerConnection {
fn quick_query(&mut self, query: &str) -> Result<Vec<Vec<Option<String>>>> {
check_desync!(self);
debug!("executing query: {}", query);
self.stream
.write_message(|buf| frontend::query(query, buf))?;
self.stream.write_message(|buf| frontend::query(query, buf))?;
self.stream.flush()?;
let mut result = vec![];
@ -909,12 +980,12 @@ impl InnerConnection {
result.push(row);
}
backend::Message::CopyInResponse(_) => {
self.stream
.write_message(|buf| {
self.stream.write_message(|buf| {
frontend::copy_fail("COPY queries cannot be directly executed", buf)
})?;
self.stream
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?;
self.stream.write_message(
|buf| Ok::<(), io::Error>(frontend::sync(buf)),
)?;
self.stream.flush()?;
}
backend::Message::ErrorResponse(body) => {
@ -929,8 +1000,9 @@ impl InnerConnection {
fn finish_inner(&mut self) -> Result<()> {
check_desync!(self);
self.stream
.write_message(|buf| Ok::<(), io::Error>(frontend::terminate(buf)))?;
self.stream.write_message(|buf| {
Ok::<(), io::Error>(frontend::terminate(buf))
})?;
self.stream.flush()?;
Ok(())
}
@ -1015,7 +1087,8 @@ impl Connection {
/// # }
/// ```
pub fn connect<T>(params: T, tls: TlsMode) -> result::Result<Connection, ConnectError>
where T: IntoConnectParams
where
T: IntoConnectParams,
{
InnerConnection::connect(params, tls).map(|conn| Connection(RefCell::new(conn)))
}
@ -1127,8 +1200,10 @@ impl Connection {
pub fn transaction_with<'a>(&'a self, config: &transaction::Config) -> Result<Transaction<'a>> {
let mut conn = self.0.borrow_mut();
check_desync!(conn);
assert!(conn.trans_depth == 0,
"`transaction` must be called on the active transaction");
assert!(
conn.trans_depth == 0,
"`transaction` must be called on the active transaction"
);
let mut query = "BEGIN".to_owned();
config.build_command(&mut query);
conn.quick_query(&query)?;
@ -1402,22 +1477,24 @@ trait RowsNew {
}
trait LazyRowsNew<'trans, 'stmt> {
fn new(stmt: &'stmt Statement<'stmt>,
fn new(
stmt: &'stmt Statement<'stmt>,
data: VecDeque<RowData>,
name: String,
row_limit: i32,
more_rows: bool,
finished: bool,
trans: &'trans Transaction<'trans>)
-> LazyRows<'trans, 'stmt>;
trans: &'trans Transaction<'trans>,
) -> LazyRows<'trans, 'stmt>;
}
trait StatementInternals<'conn> {
fn new(conn: &'conn Connection,
fn new(
conn: &'conn Connection,
info: Arc<StatementInfo>,
next_portal_id: Cell<u32>,
finished: bool)
-> Statement<'conn>;
finished: bool,
) -> Statement<'conn>;
fn conn(&self) -> &'conn Connection;

View File

@ -39,8 +39,9 @@ impl MessageStream {
}
pub fn write_message<F, E>(&mut self, f: F) -> Result<(), E>
where F: FnOnce(&mut Vec<u8>) -> Result<(), E>,
E: From<io::Error>
where
F: FnOnce(&mut Vec<u8>) -> Result<(), E>,
E: From<io::Error>,
{
self.out_buf.clear();
f(&mut self.out_buf)?;
@ -59,8 +60,13 @@ impl MessageStream {
fn read_in(&mut self) -> io::Result<()> {
self.in_buf.reserve(1);
match self.stream.get_mut().read(unsafe { self.in_buf.bytes_mut() }) {
Ok(0) => Err(io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected EOF")),
match self.stream.get_mut().read(
unsafe { self.in_buf.bytes_mut() },
) {
Ok(0) => Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"unexpected EOF",
)),
Ok(n) => {
unsafe { self.in_buf.advance_mut(n) };
Ok(())
@ -69,17 +75,19 @@ impl MessageStream {
}
}
pub fn read_message_timeout(&mut self,
timeout: Duration)
-> io::Result<Option<backend::Message>> {
pub fn read_message_timeout(
&mut self,
timeout: Duration,
) -> io::Result<Option<backend::Message>> {
if self.in_buf.is_empty() {
self.set_read_timeout(Some(timeout))?;
let r = self.read_in();
self.set_read_timeout(None)?;
match r {
Ok(()) => {},
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock ||
Ok(()) => {}
Err(ref e)
if e.kind() == io::ErrorKind::WouldBlock ||
e.kind() == io::ErrorKind::TimedOut => return Ok(None),
Err(e) => return Err(e),
}
@ -95,7 +103,7 @@ impl MessageStream {
self.set_nonblocking(false)?;
match r {
Ok(()) => {},
Ok(()) => {}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(None),
Err(e) => return Err(e),
}
@ -225,7 +233,9 @@ fn open_socket(params: &ConnectParams) -> Result<InternalStream, ConnectError> {
let port = params.port();
match *params.host() {
Host::Tcp(ref host) => {
Ok(TcpStream::connect(&(&**host, port)).map(InternalStream::Tcp)?)
Ok(TcpStream::connect(&(&**host, port)).map(
InternalStream::Tcp,
)?)
}
#[cfg(unix)]
Host::Unix(ref path) => {
@ -234,15 +244,18 @@ fn open_socket(params: &ConnectParams) -> Result<InternalStream, ConnectError> {
}
#[cfg(not(unix))]
Host::Unix(..) => {
Err(ConnectError::Io(io::Error::new(io::ErrorKind::InvalidInput,
"unix sockets are not supported on this system")))
Err(ConnectError::Io(io::Error::new(
io::ErrorKind::InvalidInput,
"unix sockets are not supported on this system",
)))
}
}
}
pub fn initialize_stream(params: &ConnectParams,
tls: TlsMode)
-> Result<Box<TlsStream>, ConnectError> {
pub fn initialize_stream(
params: &ConnectParams,
tls: TlsMode,
) -> Result<Box<TlsStream>, ConnectError> {
let mut socket = Stream(open_socket(params)?);
let (tls_required, handshaker) = match tls {
@ -272,5 +285,7 @@ pub fn initialize_stream(params: &ConnectParams,
Host::Unix(_) => return Err(ConnectError::Io(::bad_response())),
};
handshaker.tls_handshake(host, socket).map_err(ConnectError::Tls)
handshaker.tls_handshake(host, socket).map_err(
ConnectError::Tls,
)
}

View File

@ -39,7 +39,7 @@ impl<'a, T> Deref for MaybeOwned<'a, T> {
pub struct Rows<'compat> {
stmt_info: Arc<StatementInfo>,
data: Vec<RowData>,
_marker: PhantomData<&'compat u8>
_marker: PhantomData<&'compat u8>,
}
impl RowsNew for Rows<'static> {
@ -196,8 +196,9 @@ impl<'a> Row<'a> {
/// }
/// ```
pub fn get<I, T>(&self, idx: I) -> T
where I: RowIndex + fmt::Debug,
T: FromSql
where
I: RowIndex + fmt::Debug,
T: FromSql,
{
match self.get_inner(&idx) {
Some(Ok(ok)) => ok,
@ -215,15 +216,17 @@ impl<'a> Row<'a> {
/// if there was an error converting the result value, and `Some(Ok(..))`
/// on success.
pub fn get_opt<I, T>(&self, idx: I) -> Option<Result<T>>
where I: RowIndex,
T: FromSql
where
I: RowIndex,
T: FromSql,
{
self.get_inner(&idx)
}
fn get_inner<I, T>(&self, idx: &I) -> Option<Result<T>>
where I: RowIndex,
T: FromSql
where
I: RowIndex,
T: FromSql,
{
let idx = match idx.idx(&self.stmt_info.columns) {
Some(idx) => idx,
@ -244,7 +247,8 @@ impl<'a> Row<'a> {
///
/// Panics if the index does not reference a column.
pub fn get_bytes<I>(&self, idx: I) -> Option<&[u8]>
where I: RowIndex + fmt::Debug
where
I: RowIndex + fmt::Debug,
{
match idx.idx(&self.stmt_info.columns) {
Some(idx) => self.data.get(idx),
@ -281,7 +285,9 @@ impl<'a> RowIndex for &'a str {
// FIXME ASCII-only case insensitivity isn't really the right thing to
// do. Postgres itself uses a dubious wrapper around tolower and JDBC
// uses the US locale.
columns.iter().position(|d| d.name().eq_ignore_ascii_case(*self))
columns.iter().position(
|d| d.name().eq_ignore_ascii_case(*self),
)
}
}
@ -297,14 +303,15 @@ pub struct LazyRows<'trans, 'stmt> {
}
impl<'trans, 'stmt> LazyRowsNew<'trans, 'stmt> for LazyRows<'trans, 'stmt> {
fn new(stmt: &'stmt Statement<'stmt>,
fn new(
stmt: &'stmt Statement<'stmt>,
data: VecDeque<RowData>,
name: String,
row_limit: i32,
more_rows: bool,
finished: bool,
trans: &'trans Transaction<'trans>)
-> LazyRows<'trans, 'stmt> {
trans: &'trans Transaction<'trans>,
) -> LazyRows<'trans, 'stmt> {
LazyRows {
stmt: stmt,
data: data,
@ -346,10 +353,18 @@ impl<'trans, 'stmt> LazyRows<'trans, 'stmt> {
fn execute(&mut self) -> Result<()> {
let mut conn = self.stmt.conn().0.borrow_mut();
conn.stream.write_message(|buf| frontend::execute(&self.name, self.row_limit, buf))?;
conn.stream.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?;
conn.stream.write_message(|buf| {
frontend::execute(&self.name, self.row_limit, buf)
})?;
conn.stream.write_message(
|buf| Ok::<(), io::Error>(frontend::sync(buf)),
)?;
conn.stream.flush()?;
conn.read_rows(|row| self.data.push_back(row)).map(|more_rows| self.more_rows = more_rows)
conn.read_rows(|row| self.data.push_back(row)).map(
|more_rows| {
self.more_rows = more_rows
},
)
}
/// Returns a slice describing the columns of the `LazyRows`.
@ -375,9 +390,7 @@ impl<'trans, 'stmt> FallibleIterator for LazyRows<'trans, 'stmt> {
self.execute()?;
}
let row = self.data
.pop_front()
.map(|r| {
let row = self.data.pop_front().map(|r| {
Row {
stmt_info: &**self.stmt.info(),
data: MaybeOwned::Owned(r),

View File

@ -37,11 +37,12 @@ impl<'conn> Drop for Statement<'conn> {
}
impl<'conn> StatementInternals<'conn> for Statement<'conn> {
fn new(conn: &'conn Connection,
fn new(
conn: &'conn Connection,
info: Arc<StatementInfo>,
next_portal_id: Cell<u32>,
finished: bool)
-> Statement<'conn> {
finished: bool,
) -> Statement<'conn> {
Statement {
conn: conn,
info: info,
@ -79,21 +80,25 @@ impl<'conn> Statement<'conn> {
}
#[allow(type_complexity)]
fn inner_query<F>(&self,
fn inner_query<F>(
&self,
portal_name: &str,
row_limit: i32,
params: &[&ToSql],
acceptor: F)
-> Result<bool>
where F: FnMut(RowData)
acceptor: F,
) -> Result<bool>
where
F: FnMut(RowData),
{
let mut conn = self.conn.0.borrow_mut();
conn.raw_execute(&self.info.name,
conn.raw_execute(
&self.info.name,
portal_name,
row_limit,
self.param_types(),
params)?;
params,
)?;
conn.read_rows(acceptor)
}
@ -131,7 +136,13 @@ impl<'conn> Statement<'conn> {
pub fn execute(&self, params: &[&ToSql]) -> Result<u64> {
let mut conn = self.conn.0.borrow_mut();
check_desync!(conn);
conn.raw_execute(&self.info.name, "", 0, self.param_types(), params)?;
conn.raw_execute(
&self.info.name,
"",
0,
self.param_types(),
params,
)?;
let num;
loop {
@ -153,8 +164,9 @@ impl<'conn> Statement<'conn> {
conn.stream.write_message(|buf| {
frontend::copy_fail("COPY queries cannot be directly executed", buf)
})?;
conn.stream
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?;
conn.stream.write_message(
|buf| Ok::<(), io::Error>(frontend::sync(buf)),
)?;
conn.stream.flush()?;
}
backend::Message::CopyOutResponse(_) => {
@ -226,18 +238,23 @@ impl<'conn> Statement<'conn> {
/// `Connection` as this `Statement`, if the `Transaction` is not
/// active, or if the number of parameters provided does not match the
/// number of parameters expected.
pub fn lazy_query<'trans, 'stmt>(&'stmt self,
pub fn lazy_query<'trans, 'stmt>(
&'stmt self,
trans: &'trans Transaction,
params: &[&ToSql],
row_limit: i32)
-> Result<LazyRows<'trans, 'stmt>> {
assert!(self.conn as *const _ == trans.conn() as *const _,
row_limit: i32,
) -> Result<LazyRows<'trans, 'stmt>> {
assert!(
self.conn as *const _ == trans.conn() as *const _,
"the `Transaction` passed to `lazy_query` must be associated with the same \
`Connection` as the `Statement`");
`Connection` as the `Statement`"
);
let conn = self.conn.0.borrow();
check_desync!(conn);
assert!(conn.trans_depth == trans.depth(),
"`lazy_query` must be passed the active transaction");
assert!(
conn.trans_depth == trans.depth(),
"`lazy_query` must be passed the active transaction"
);
drop(conn);
let id = self.next_portal_id.get();
@ -245,11 +262,21 @@ impl<'conn> Statement<'conn> {
let portal_name = format!("{}p{}", self.info.name, id);
let mut rows = VecDeque::new();
let more_rows = self.inner_query(&portal_name,
let more_rows = self.inner_query(
&portal_name,
row_limit,
params,
|row| rows.push_back(row))?;
Ok(LazyRows::new(self, rows, portal_name, row_limit, more_rows, false, trans))
|row| rows.push_back(row),
)?;
Ok(LazyRows::new(
self,
rows,
portal_name,
row_limit,
more_rows,
false,
trans,
))
}
/// Executes a `COPY FROM STDIN` statement, returning the number of rows
@ -275,14 +302,18 @@ impl<'conn> Statement<'conn> {
/// ```
pub fn copy_in<R: ReadWithInfo>(&self, params: &[&ToSql], r: &mut R) -> Result<u64> {
let mut conn = self.conn.0.borrow_mut();
conn.raw_execute(&self.info.name, "", 0, self.param_types(), params)?;
conn.raw_execute(
&self.info.name,
"",
0,
self.param_types(),
params,
)?;
let (format, column_formats) = match conn.read_message()? {
backend::Message::CopyInResponse(body) => {
let format = body.format();
let column_formats = body.column_formats()
.map(|f| Format::from_u16(f))
.collect()?;
let column_formats = body.column_formats().map(|f| Format::from_u16(f)).collect()?;
(format, column_formats)
}
backend::Message::ErrorResponse(body) => {
@ -292,10 +323,12 @@ impl<'conn> Statement<'conn> {
_ => {
loop {
if let backend::Message::ReadyForQuery(_) = conn.read_message()? {
return Err(Error::Io(io::Error::new(io::ErrorKind::InvalidInput,
return Err(Error::Io(io::Error::new(
io::ErrorKind::InvalidInput,
"called `copy_in` on a \
non-`COPY FROM STDIN` \
statement")));
statement",
)));
}
}
}
@ -311,14 +344,20 @@ impl<'conn> Statement<'conn> {
match fill_copy_buf(&mut buf, r, &info) {
Ok(0) => break,
Ok(len) => {
conn.stream.write_message(|out| frontend::copy_data(&buf[..len], out))?;
conn.stream.write_message(
|out| frontend::copy_data(&buf[..len], out),
)?;
}
Err(err) => {
conn.stream.write_message(|buf| frontend::copy_fail("", buf))?;
conn.stream
.write_message(|buf| Ok::<(), io::Error>(frontend::copy_done(buf)))?;
conn.stream
.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?;
conn.stream.write_message(
|buf| frontend::copy_fail("", buf),
)?;
conn.stream.write_message(|buf| {
Ok::<(), io::Error>(frontend::copy_done(buf))
})?;
conn.stream.write_message(
|buf| Ok::<(), io::Error>(frontend::sync(buf)),
)?;
conn.stream.flush()?;
match conn.read_message()? {
backend::Message::ErrorResponse(_) => {
@ -335,8 +374,12 @@ impl<'conn> Statement<'conn> {
}
}
conn.stream.write_message(|buf| Ok::<(), io::Error>(frontend::copy_done(buf)))?;
conn.stream.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?;
conn.stream.write_message(|buf| {
Ok::<(), io::Error>(frontend::copy_done(buf))
})?;
conn.stream.write_message(
|buf| Ok::<(), io::Error>(frontend::sync(buf)),
)?;
conn.stream.flush()?;
let num = match conn.read_message()? {
@ -379,21 +422,30 @@ impl<'conn> Statement<'conn> {
/// ```
pub fn copy_out<'a, W: WriteWithInfo>(&'a self, params: &[&ToSql], w: &mut W) -> Result<u64> {
let mut conn = self.conn.0.borrow_mut();
conn.raw_execute(&self.info.name, "", 0, self.param_types(), params)?;
conn.raw_execute(
&self.info.name,
"",
0,
self.param_types(),
params,
)?;
let (format, column_formats) = match conn.read_message()? {
backend::Message::CopyOutResponse(body) => {
let format = body.format();
let column_formats = body.column_formats()
.map(|f| Format::from_u16(f))
.collect()?;
let column_formats = body.column_formats().map(|f| Format::from_u16(f)).collect()?;
(format, column_formats)
}
backend::Message::CopyInResponse(_) => {
conn.stream.write_message(|buf| frontend::copy_fail("", buf))?;
conn.stream
.write_message(|buf| Ok::<(), io::Error>(frontend::copy_done(buf)))?;
conn.stream.write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?;
conn.stream.write_message(
|buf| frontend::copy_fail("", buf),
)?;
conn.stream.write_message(|buf| {
Ok::<(), io::Error>(frontend::copy_done(buf))
})?;
conn.stream.write_message(
|buf| Ok::<(), io::Error>(frontend::sync(buf)),
)?;
conn.stream.flush()?;
match conn.read_message()? {
backend::Message::ErrorResponse(_) => {
@ -405,9 +457,11 @@ impl<'conn> Statement<'conn> {
}
}
conn.wait_for_ready()?;
return Err(Error::Io(io::Error::new(io::ErrorKind::InvalidInput,
return Err(Error::Io(io::Error::new(
io::ErrorKind::InvalidInput,
"called `copy_out` on a non-`COPY TO \
STDOUT` statement")));
STDOUT` statement",
)));
}
backend::Message::ErrorResponse(body) => {
conn.wait_for_ready()?;
@ -416,9 +470,11 @@ impl<'conn> Statement<'conn> {
_ => {
loop {
if let backend::Message::ReadyForQuery(_) = conn.read_message()? {
return Err(Error::Io(io::Error::new(io::ErrorKind::InvalidInput,
return Err(Error::Io(io::Error::new(
io::ErrorKind::InvalidInput,
"called `copy_out` on a \
non-`COPY TO STDOUT` statement")));
non-`COPY TO STDOUT` statement",
)));
}
}
}
@ -440,7 +496,8 @@ impl<'conn> Statement<'conn> {
Err(e) => {
loop {
if let backend::Message::ReadyForQuery(_) =
conn.read_message()? {
conn.read_message()?
{
return Err(Error::Io(e));
}
}
@ -455,16 +512,14 @@ impl<'conn> Statement<'conn> {
}
backend::Message::ErrorResponse(body) => {
loop {
if let backend::Message::ReadyForQuery(_) =
conn.read_message()? {
if let backend::Message::ReadyForQuery(_) = conn.read_message()? {
return Err(err(&mut body.fields()));
}
}
}
_ => {
loop {
if let backend::Message::ReadyForQuery(_) =
conn.read_message()? {
if let backend::Message::ReadyForQuery(_) = conn.read_message()? {
return Err(Error::Io(bad_response()));
}
}

View File

@ -31,18 +31,19 @@ pub trait TlsHandshake: fmt::Debug {
///
/// The host portion of the connection parameters is provided for hostname
/// verification.
fn tls_handshake(&self,
fn tls_handshake(
&self,
host: &str,
stream: Stream)
-> Result<Box<TlsStream>, Box<Error + Sync + Send>>;
stream: Stream,
) -> Result<Box<TlsStream>, Box<Error + Sync + Send>>;
}
impl<T: TlsHandshake + ?Sized> TlsHandshake for Box<T> {
fn tls_handshake(&self,
fn tls_handshake(
&self,
host: &str,
stream: Stream)
-> Result<Box<TlsStream>, Box<Error + Sync + Send>> {
stream: Stream,
) -> Result<Box<TlsStream>, Box<Error + Sync + Send>> {
(**self).tls_handshake(host, stream)
}
}

View File

@ -54,10 +54,11 @@ impl From<TlsConnector> for NativeTls {
}
impl TlsHandshake for NativeTls {
fn tls_handshake(&self,
fn tls_handshake(
&self,
domain: &str,
stream: Stream)
-> Result<Box<TlsStream>, Box<Error + Send + Sync>> {
stream: Stream,
) -> Result<Box<TlsStream>, Box<Error + Send + Sync>> {
let stream = self.0.connect(domain, stream)?;
Ok(Box::new(stream))
}

View File

@ -70,10 +70,11 @@ impl From<SslConnector> for OpenSsl {
}
impl TlsHandshake for OpenSsl {
fn tls_handshake(&self,
fn tls_handshake(
&self,
domain: &str,
stream: Stream)
-> Result<Box<TlsStream>, Box<Error + Send + Sync>> {
stream: Stream,
) -> Result<Box<TlsStream>, Box<Error + Send + Sync>> {
let stream = if self.disable_verification {
self.connector.danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(stream)?
} else {

View File

@ -38,14 +38,16 @@ impl Schannel {
}
impl TlsHandshake for Schannel {
fn tls_handshake(&self,
fn tls_handshake(
&self,
host: &str,
stream: Stream)
-> Result<Box<TlsStream>, Box<Error + Sync + Send>> {
stream: Stream,
) -> Result<Box<TlsStream>, Box<Error + Sync + Send>> {
let creds = SchannelCred::builder().acquire(Direction::Outbound)?;
let stream = tls_stream::Builder::new()
.domain(host)
.connect(creds, stream)?;
let stream = tls_stream::Builder::new().domain(host).connect(
creds,
stream,
)?;
Ok(Box::new(stream))
}
}

View File

@ -45,10 +45,11 @@ impl From<ClientBuilder> for SecurityFramework {
}
impl TlsHandshake for SecurityFramework {
fn tls_handshake(&self,
fn tls_handshake(
&self,
domain: &str,
stream: Stream)
-> Result<Box<TlsStream>, Box<Error + Send + Sync>> {
stream: Stream,
) -> Result<Box<TlsStream>, Box<Error + Send + Sync>> {
let stream = self.0.handshake(domain, stream)?;
Ok(Box::new(stream))
}

View File

@ -256,8 +256,10 @@ impl<'conn> Transaction<'conn> {
pub fn savepoint<'a>(&'a self, name: &str) -> Result<Transaction<'a>> {
let mut conn = self.conn.0.borrow_mut();
check_desync!(conn);
assert!(conn.trans_depth == self.depth,
"`savepoint` may only be called on the active transaction");
assert!(
conn.trans_depth == self.depth,
"`savepoint` may only be called on the active transaction"
);
conn.quick_query(&format!("SAVEPOINT {}", name))?;
conn.trans_depth += 1;
Ok(Transaction {

View File

@ -1,8 +1,8 @@
//! Traits dealing with Postgres data types
#[doc(inline)]
pub use postgres_shared::types::{Oid, Type, Date, Timestamp, Kind, Field, Other,
WasNull, WrongType, FromSql, IsNull, ToSql};
pub use postgres_shared::types::{Oid, Type, Date, Timestamp, Kind, Field, Other, WasNull,
WrongType, FromSql, IsNull, ToSql};
#[doc(hidden)]
pub use postgres_shared::types::__to_sql_checked;

File diff suppressed because it is too large Load Diff

View File

@ -8,8 +8,10 @@ fn test_bit_params() {
let mut bv = BitVec::from_bytes(&[0b0110_1001, 0b0000_0111]);
bv.pop();
bv.pop();
test_type("BIT(14)", &[(Some(bv), "B'01101001000001'"),
(None, "NULL")])
test_type(
"BIT(14)",
&[(Some(bv), "B'01101001000001'"), (None, "NULL")],
)
}
#[test]
@ -17,7 +19,12 @@ fn test_varbit_params() {
let mut bv = BitVec::from_bytes(&[0b0110_1001, 0b0000_0111]);
bv.pop();
bv.pop();
test_type("VARBIT", &[(Some(bv), "B'01101001000001'"),
test_type(
"VARBIT",
&[
(Some(bv), "B'01101001000001'"),
(Some(BitVec::from_bytes(&[])), "B''"),
(None, "NULL")])
(None, "NULL"),
],
)
}

View File

@ -8,87 +8,145 @@ use postgres::types::{Date, Timestamp};
#[test]
fn test_naive_date_time_params() {
fn make_check<'a>(time: &'a str) -> (Option<NaiveDateTime>, &'a str) {
(Some(NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()), time)
(
Some(
NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap(),
),
time,
)
}
test_type("TIMESTAMP",
&[make_check("'1970-01-01 00:00:00.010000000'"),
test_type(
"TIMESTAMP",
&[
make_check("'1970-01-01 00:00:00.010000000'"),
make_check("'1965-09-25 11:19:33.100314000'"),
make_check("'2010-02-09 23:11:45.120200000'"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
#[test]
fn test_with_special_naive_date_time_params() {
fn make_check<'a>(time: &'a str) -> (Timestamp<NaiveDateTime>, &'a str) {
(Timestamp::Value(NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()),
time)
(
Timestamp::Value(
NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap(),
),
time,
)
}
test_type("TIMESTAMP",
&[make_check("'1970-01-01 00:00:00.010000000'"),
test_type(
"TIMESTAMP",
&[
make_check("'1970-01-01 00:00:00.010000000'"),
make_check("'1965-09-25 11:19:33.100314000'"),
make_check("'2010-02-09 23:11:45.120200000'"),
(Timestamp::PosInfinity, "'infinity'"),
(Timestamp::NegInfinity, "'-infinity'")]);
(Timestamp::NegInfinity, "'-infinity'"),
],
);
}
#[test]
fn test_date_time_params() {
fn make_check<'a>(time: &'a str) -> (Option<DateTime<Utc>>, &'a str) {
(Some(Utc.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()), time)
(
Some(
Utc.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'")
.unwrap(),
),
time,
)
}
test_type("TIMESTAMP WITH TIME ZONE",
&[make_check("'1970-01-01 00:00:00.010000000'"),
test_type(
"TIMESTAMP WITH TIME ZONE",
&[
make_check("'1970-01-01 00:00:00.010000000'"),
make_check("'1965-09-25 11:19:33.100314000'"),
make_check("'2010-02-09 23:11:45.120200000'"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
#[test]
fn test_with_special_date_time_params() {
fn make_check<'a>(time: &'a str) -> (Timestamp<DateTime<Utc>>, &'a str) {
(Timestamp::Value(Utc.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()), time)
(
Timestamp::Value(
Utc.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'")
.unwrap(),
),
time,
)
}
test_type("TIMESTAMP WITH TIME ZONE",
&[make_check("'1970-01-01 00:00:00.010000000'"),
test_type(
"TIMESTAMP WITH TIME ZONE",
&[
make_check("'1970-01-01 00:00:00.010000000'"),
make_check("'1965-09-25 11:19:33.100314000'"),
make_check("'2010-02-09 23:11:45.120200000'"),
(Timestamp::PosInfinity, "'infinity'"),
(Timestamp::NegInfinity, "'-infinity'")]);
(Timestamp::NegInfinity, "'-infinity'"),
],
);
}
#[test]
fn test_date_params() {
fn make_check<'a>(time: &'a str) -> (Option<NaiveDate>, &'a str) {
(Some(NaiveDate::parse_from_str(time, "'%Y-%m-%d'").unwrap()), time)
(
Some(NaiveDate::parse_from_str(time, "'%Y-%m-%d'").unwrap()),
time,
)
}
test_type("DATE",
&[make_check("'1970-01-01'"),
test_type(
"DATE",
&[
make_check("'1970-01-01'"),
make_check("'1965-09-25'"),
make_check("'2010-02-09'"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
#[test]
fn test_with_special_date_params() {
fn make_check<'a>(date: &'a str) -> (Date<NaiveDate>, &'a str) {
(Date::Value(NaiveDate::parse_from_str(date, "'%Y-%m-%d'").unwrap()), date)
(
Date::Value(NaiveDate::parse_from_str(date, "'%Y-%m-%d'").unwrap()),
date,
)
}
test_type("DATE",
&[make_check("'1970-01-01'"),
test_type(
"DATE",
&[
make_check("'1970-01-01'"),
make_check("'1965-09-25'"),
make_check("'2010-02-09'"),
(Date::PosInfinity, "'infinity'"),
(Date::NegInfinity, "'-infinity'")]);
(Date::NegInfinity, "'-infinity'"),
],
);
}
#[test]
fn test_time_params() {
fn make_check<'a>(time: &'a str) -> (Option<NaiveTime>, &'a str) {
(Some(NaiveTime::parse_from_str(time, "'%H:%M:%S.%f'").unwrap()), time)
(
Some(NaiveTime::parse_from_str(time, "'%H:%M:%S.%f'").unwrap()),
time,
)
}
test_type("TIME",
&[make_check("'00:00:00.010000000'"),
test_type(
"TIME",
&[
make_check("'00:00:00.010000000'"),
make_check("'11:19:33.100314000'"),
make_check("'23:11:45.120200000'"),
(None, "NULL")]);
(None, "NULL"),
],
);
}

View File

@ -4,6 +4,14 @@ use types::test_type;
#[test]
fn test_eui48_params() {
test_type("MACADDR", &[(Some(eui48::MacAddress::parse_str("12-34-56-AB-CD-EF").unwrap()),
"'12-34-56-ab-cd-ef'"), (None, "NULL")])
test_type(
"MACADDR",
&[
(
Some(eui48::MacAddress::parse_str("12-34-56-AB-CD-EF").unwrap()),
"'12-34-56-ab-cd-ef'",
),
(None, "NULL"),
],
)
}

View File

@ -5,24 +5,50 @@ use types::test_type;
#[test]
fn test_point_params() {
test_type("POINT",
&[(Some(Point::new(0.0, 0.0)), "POINT(0, 0)"),
test_type(
"POINT",
&[
(Some(Point::new(0.0, 0.0)), "POINT(0, 0)"),
(Some(Point::new(-3.14, 1.618)), "POINT(-3.14, 1.618)"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
#[test]
fn test_box_params() {
test_type("BOX",
&[(Some(Bbox{xmax: 160.0, ymax: 69701.5615, xmin: -3.14, ymin: 1.618}),
"BOX(POINT(160.0, 69701.5615), POINT(-3.14, 1.618))"),
(None, "NULL")]);
test_type(
"BOX",
&[
(
Some(Bbox {
xmax: 160.0,
ymax: 69701.5615,
xmin: -3.14,
ymin: 1.618,
}),
"BOX(POINT(160.0, 69701.5615), POINT(-3.14, 1.618))",
),
(None, "NULL"),
],
);
}
#[test]
fn test_path_params() {
let points = vec![Point::new(0.0, 0.0), Point::new(-3.14, 1.618), Point::new(160.0, 69701.5615)];
test_type("PATH",
&[(Some(LineString(points)),"path '((0, 0), (-3.14, 1.618), (160.0, 69701.5615))'"),
(None, "NULL")]);
let points = vec![
Point::new(0.0, 0.0),
Point::new(-3.14, 1.618),
Point::new(160.0, 69701.5615),
];
test_type(
"PATH",
&[
(
Some(LineString(points)),
"path '((0, 0), (-3.14, 1.618), (160.0, 69701.5615))'",
),
(None, "NULL"),
],
);
}

View File

@ -27,7 +27,10 @@ mod chrono;
mod geo;
fn test_type<T: PartialEq + FromSql + ToSql, S: fmt::Display>(sql_type: &str, checks: &[(T, S)]) {
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", TlsMode::None));
let conn = or_panic!(Connection::connect(
"postgres://postgres@localhost",
TlsMode::None,
));
for &(ref val, ref repr) in checks.iter() {
let stmt = or_panic!(conn.prepare(&*format!("SELECT {}::{}", *repr, sql_type)));
let result = or_panic!(stmt.query(&[])).iter().next().unwrap().get(0);
@ -41,7 +44,10 @@ fn test_type<T: PartialEq + FromSql + ToSql, S: fmt::Display>(sql_type: &str, ch
#[test]
fn test_ref_tosql() {
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", TlsMode::None));
let conn = or_panic!(Connection::connect(
"postgres://postgres@localhost",
TlsMode::None,
));
let stmt = conn.prepare("SELECT $1::Int").unwrap();
let num: &ToSql = &&7;
stmt.query(&[num]).unwrap();
@ -49,8 +55,10 @@ fn test_ref_tosql() {
#[test]
fn test_bool_params() {
test_type("BOOL",
&[(Some(true), "'t'"), (Some(false), "'f'"), (None, "NULL")]);
test_type(
"BOOL",
&[(Some(true), "'t'"), (Some(false), "'f'"), (None, "NULL")],
);
}
#[test]
@ -60,120 +68,190 @@ fn test_i8_params() {
#[test]
fn test_name_params() {
test_type("NAME",
&[(Some("hello world".to_owned()), "'hello world'"),
(Some("イロハニホヘト チリヌルヲ".to_owned()),
"'イロハニホヘト チリヌルヲ'"),
(None, "NULL")]);
test_type(
"NAME",
&[
(Some("hello world".to_owned()), "'hello world'"),
(
Some("イロハニホヘト チリヌルヲ".to_owned()),
"'イロハニホヘト チリヌルヲ'",
),
(None, "NULL"),
],
);
}
#[test]
fn test_i16_params() {
test_type("SMALLINT",
&[(Some(15001i16), "15001"),
test_type(
"SMALLINT",
&[
(Some(15001i16), "15001"),
(Some(-15001i16), "-15001"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
#[test]
fn test_i32_params() {
test_type("INT",
&[(Some(2147483548i32), "2147483548"),
test_type(
"INT",
&[
(Some(2147483548i32), "2147483548"),
(Some(-2147483548i32), "-2147483548"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
#[test]
fn test_oid_params() {
test_type("OID",
&[(Some(2147483548u32), "2147483548"),
test_type(
"OID",
&[
(Some(2147483548u32), "2147483548"),
(Some(4000000000), "4000000000"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
#[test]
fn test_i64_params() {
test_type("BIGINT",
&[(Some(9223372036854775708i64), "9223372036854775708"),
test_type(
"BIGINT",
&[
(Some(9223372036854775708i64), "9223372036854775708"),
(Some(-9223372036854775708i64), "-9223372036854775708"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
#[test]
fn test_f32_params() {
test_type("REAL",
&[(Some(f32::INFINITY), "'infinity'"),
test_type(
"REAL",
&[
(Some(f32::INFINITY), "'infinity'"),
(Some(f32::NEG_INFINITY), "'-infinity'"),
(Some(1000.55), "1000.55"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
#[test]
fn test_f64_params() {
test_type("DOUBLE PRECISION",
&[(Some(f64::INFINITY), "'infinity'"),
test_type(
"DOUBLE PRECISION",
&[
(Some(f64::INFINITY), "'infinity'"),
(Some(f64::NEG_INFINITY), "'-infinity'"),
(Some(10000.55), "10000.55"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
#[test]
fn test_varchar_params() {
test_type("VARCHAR",
&[(Some("hello world".to_owned()), "'hello world'"),
(Some("イロハニホヘト チリヌルヲ".to_owned()),
"'イロハニホヘト チリヌルヲ'"),
(None, "NULL")]);
test_type(
"VARCHAR",
&[
(Some("hello world".to_owned()), "'hello world'"),
(
Some("イロハニホヘト チリヌルヲ".to_owned()),
"'イロハニホヘト チリヌルヲ'",
),
(None, "NULL"),
],
);
}
#[test]
fn test_text_params() {
test_type("TEXT",
&[(Some("hello world".to_owned()), "'hello world'"),
(Some("イロハニホヘト チリヌルヲ".to_owned()),
"'イロハニホヘト チリヌルヲ'"),
(None, "NULL")]);
test_type(
"TEXT",
&[
(Some("hello world".to_owned()), "'hello world'"),
(
Some("イロハニホヘト チリヌルヲ".to_owned()),
"'イロハニホヘト チリヌルヲ'",
),
(None, "NULL"),
],
);
}
#[test]
fn test_bpchar_params() {
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", TlsMode::None));
or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (
let conn = or_panic!(Connection::connect(
"postgres://postgres@localhost",
TlsMode::None,
));
or_panic!(conn.execute(
"CREATE TEMPORARY TABLE foo (
id SERIAL PRIMARY KEY,
b CHAR(5)
)",
&[]));
or_panic!(conn.execute("INSERT INTO foo (b) VALUES ($1), ($2), ($3)",
&[&Some("12345"), &Some("123"), &None::<&'static str>]));
&[],
));
or_panic!(conn.execute(
"INSERT INTO foo (b) VALUES ($1), ($2), ($3)",
&[&Some("12345"), &Some("123"), &None::<&'static str>],
));
let stmt = or_panic!(conn.prepare("SELECT b FROM foo ORDER BY id"));
let res = or_panic!(stmt.query(&[]));
assert_eq!(vec![Some("12345".to_owned()), Some("123 ".to_owned()), None],
res.iter().map(|row| row.get(0)).collect::<Vec<_>>());
assert_eq!(
vec![Some("12345".to_owned()), Some("123 ".to_owned()), None],
res.iter().map(|row| row.get(0)).collect::<Vec<_>>()
);
}
#[test]
fn test_citext_params() {
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", TlsMode::None));
or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (
let conn = or_panic!(Connection::connect(
"postgres://postgres@localhost",
TlsMode::None,
));
or_panic!(conn.execute(
"CREATE TEMPORARY TABLE foo (
id SERIAL PRIMARY KEY,
b CITEXT
)",
&[]));
or_panic!(conn.execute("INSERT INTO foo (b) VALUES ($1), ($2), ($3)",
&[&Some("foobar"), &Some("FooBar"), &None::<&'static str>]));
let stmt = or_panic!(conn.prepare("SELECT id FROM foo WHERE b = 'FOOBAR' ORDER BY id"));
&[],
));
or_panic!(conn.execute(
"INSERT INTO foo (b) VALUES ($1), ($2), ($3)",
&[
&Some("foobar"),
&Some("FooBar"),
&None::<&'static str>,
],
));
let stmt = or_panic!(conn.prepare(
"SELECT id FROM foo WHERE b = 'FOOBAR' ORDER BY id",
));
let res = or_panic!(stmt.query(&[]));
assert_eq!(vec![Some(1i32), Some(2i32)],
res.iter().map(|row| row.get(0)).collect::<Vec<_>>());
assert_eq!(
vec![Some(1i32), Some(2i32)],
res.iter().map(|row| row.get(0)).collect::<Vec<_>>()
);
}
#[test]
fn test_bytea_params() {
test_type("BYTEA",
&[(Some(vec![0u8, 1, 2, 3, 254, 255]), "'\\x00010203feff'"),
(None, "NULL")]);
test_type(
"BYTEA",
&[
(Some(vec![0u8, 1, 2, 3, 254, 255]), "'\\x00010203feff'"),
(None, "NULL"),
],
);
}
#[test]
@ -185,26 +263,42 @@ fn test_hstore_params() {
map
})
}
test_type("hstore",
&[(Some(make_map!("a".to_owned() => Some("1".to_owned()))), "'a=>1'"),
(Some(make_map!("hello".to_owned() => Some("world!".to_owned()),
test_type(
"hstore",
&[
(
Some(make_map!("a".to_owned() => Some("1".to_owned()))),
"'a=>1'",
),
(
Some(make_map!("hello".to_owned() => Some("world!".to_owned()),
"hola".to_owned() => Some("mundo!".to_owned()),
"what".to_owned() => None)),
"'hello=>world!,hola=>mundo!,what=>NULL'"),
(None, "NULL")]);
"'hello=>world!,hola=>mundo!,what=>NULL'",
),
(None, "NULL"),
],
);
}
#[test]
fn test_array_params() {
test_type("integer[]",
&[(Some(vec![1i32, 2i32]), "ARRAY[1,2]"),
test_type(
"integer[]",
&[
(Some(vec![1i32, 2i32]), "ARRAY[1,2]"),
(Some(vec![1i32]), "ARRAY[1]"),
(Some(vec![]), "ARRAY[]"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
fn test_nan_param<T: PartialEq + ToSql + FromSql>(sql_type: &str) {
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", TlsMode::None));
let conn = or_panic!(Connection::connect(
"postgres://postgres@localhost",
TlsMode::None,
));
let stmt = or_panic!(conn.prepare(&*format!("SELECT 'NaN'::{}", sql_type)));
let result = or_panic!(stmt.query(&[]));
let val: T = result.iter().next().unwrap().get(0);
@ -223,7 +317,10 @@ fn test_f64_nan_param() {
#[test]
fn test_pg_database_datname() {
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", TlsMode::None));
let conn = or_panic!(Connection::connect(
"postgres://postgres@localhost",
TlsMode::None,
));
let stmt = or_panic!(conn.prepare("SELECT datname FROM pg_database"));
let result = or_panic!(stmt.query(&[]));
@ -235,18 +332,21 @@ fn test_pg_database_datname() {
#[test]
fn test_slice() {
let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();
conn.batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY, f VARCHAR);
INSERT INTO foo (f) VALUES ('a'), ('b'), ('c'), ('d');")
.unwrap();
conn.batch_execute(
"CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY, f VARCHAR);
INSERT INTO foo (f) VALUES ('a'), ('b'), ('c'), ('d');",
).unwrap();
let stmt = conn.prepare("SELECT f FROM foo WHERE id = ANY($1)")
.unwrap();
let result = stmt.query(&[&&[1i32, 3, 4][..]]).unwrap();
assert_eq!(vec!["a".to_owned(), "c".to_owned(), "d".to_owned()],
assert_eq!(
vec!["a".to_owned(), "c".to_owned(), "d".to_owned()],
result
.iter()
.map(|r| r.get::<_, String>(0))
.collect::<Vec<_>>());
.collect::<Vec<_>>()
);
}
#[test]
@ -282,10 +382,11 @@ fn domain() {
struct SessionId(Vec<u8>);
impl ToSql for SessionId {
fn to_sql(&self,
fn to_sql(
&self,
ty: &Type,
out: &mut Vec<u8>)
-> result::Result<IsNull, Box<error::Error + Sync + Send>> {
out: &mut Vec<u8>,
) -> result::Result<IsNull, Box<error::Error + Sync + Send>> {
let inner = match *ty.kind() {
Kind::Domain(ref inner) => inner,
_ => unreachable!(),
@ -305,9 +406,10 @@ fn domain() {
}
impl FromSql for SessionId {
fn from_sql(ty: &Type,
raw: &[u8])
-> result::Result<Self, Box<error::Error + Sync + Send>> {
fn from_sql(
ty: &Type,
raw: &[u8],
) -> result::Result<Self, Box<error::Error + Sync + Send>> {
Vec::<u8>::from_sql(ty, raw).map(SessionId)
}
@ -318,9 +420,10 @@ fn domain() {
}
let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();
conn.batch_execute("CREATE DOMAIN pg_temp.session_id AS bytea CHECK(octet_length(VALUE) = 16);
CREATE TABLE pg_temp.foo (id pg_temp.session_id);")
.unwrap();
conn.batch_execute(
"CREATE DOMAIN pg_temp.session_id AS bytea CHECK(octet_length(VALUE) = 16);
CREATE TABLE pg_temp.foo (id pg_temp.session_id);",
).unwrap();
let id = SessionId(b"0123456789abcdef".to_vec());
conn.execute("INSERT INTO pg_temp.foo (id) VALUES ($1)", &[&id])
@ -332,12 +435,13 @@ fn domain() {
#[test]
fn composite() {
let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();
conn.batch_execute("CREATE TYPE pg_temp.inventory_item AS (
conn.batch_execute(
"CREATE TYPE pg_temp.inventory_item AS (
name TEXT,
supplier INTEGER,
price NUMERIC
)")
.unwrap();
)",
).unwrap();
let stmt = conn.prepare("SELECT $1::inventory_item").unwrap();
let type_ = &stmt.param_types()[0];
@ -366,8 +470,10 @@ fn enum_() {
assert_eq!(type_.name(), "mood");
match *type_.kind() {
Kind::Enum(ref variants) => {
assert_eq!(variants,
&["sad".to_owned(), "ok".to_owned(), "happy".to_owned()]);
assert_eq!(
variants,
&["sad".to_owned(), "ok".to_owned(), "happy".to_owned()]
);
}
_ => panic!("bad type"),
}

View File

@ -6,18 +6,36 @@ use types::test_type;
#[test]
fn test_json_params() {
test_type("JSON", &[(Some(Json::from_str("[10, 11, 12]").unwrap()),
"'[10, 11, 12]'"),
(Some(Json::from_str("{\"f\": \"asd\"}").unwrap()),
"'{\"f\": \"asd\"}'"),
(None, "NULL")])
test_type(
"JSON",
&[
(
Some(Json::from_str("[10, 11, 12]").unwrap()),
"'[10, 11, 12]'",
),
(
Some(Json::from_str("{\"f\": \"asd\"}").unwrap()),
"'{\"f\": \"asd\"}'",
),
(None, "NULL"),
],
)
}
#[test]
fn test_jsonb_params() {
test_type("JSONB", &[(Some(Json::from_str("[10, 11, 12]").unwrap()),
"'[10, 11, 12]'"),
(Some(Json::from_str("{\"f\": \"asd\"}").unwrap()),
"'{\"f\": \"asd\"}'"),
(None, "NULL")])
test_type(
"JSONB",
&[
(
Some(Json::from_str("[10, 11, 12]").unwrap()),
"'[10, 11, 12]'",
),
(
Some(Json::from_str("{\"f\": \"asd\"}").unwrap()),
"'{\"f\": \"asd\"}'",
),
(None, "NULL"),
],
)
}

View File

@ -5,18 +5,36 @@ use types::test_type;
#[test]
fn test_json_params() {
test_type("JSON", &[(Some(serde_json::from_str::<Value>("[10, 11, 12]").unwrap()),
"'[10, 11, 12]'"),
(Some(serde_json::from_str::<Value>("{\"f\": \"asd\"}").unwrap()),
"'{\"f\": \"asd\"}'"),
(None, "NULL")])
test_type(
"JSON",
&[
(
Some(serde_json::from_str::<Value>("[10, 11, 12]").unwrap()),
"'[10, 11, 12]'",
),
(
Some(serde_json::from_str::<Value>("{\"f\": \"asd\"}").unwrap()),
"'{\"f\": \"asd\"}'",
),
(None, "NULL"),
],
)
}
#[test]
fn test_jsonb_params() {
test_type("JSONB", &[(Some(serde_json::from_str::<Value>("[10, 11, 12]").unwrap()),
"'[10, 11, 12]'"),
(Some(serde_json::from_str::<Value>("{\"f\": \"asd\"}").unwrap()),
"'{\"f\": \"asd\"}'"),
(None, "NULL")])
test_type(
"JSONB",
&[
(
Some(serde_json::from_str::<Value>("[10, 11, 12]").unwrap()),
"'[10, 11, 12]'",
),
(
Some(serde_json::from_str::<Value>("{\"f\": \"asd\"}").unwrap()),
"'{\"f\": \"asd\"}'",
),
(None, "NULL"),
],
)
}

View File

@ -8,36 +8,65 @@ use postgres::types::Timestamp;
#[test]
fn test_tm_params() {
fn make_check<'a>(time: &'a str) -> (Option<Timespec>, &'a str) {
(Some(time::strptime(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap().to_timespec()), time)
(
Some(
time::strptime(time, "'%Y-%m-%d %H:%M:%S.%f'")
.unwrap()
.to_timespec(),
),
time,
)
}
test_type("TIMESTAMP",
&[make_check("'1970-01-01 00:00:00.01'"),
test_type(
"TIMESTAMP",
&[
make_check("'1970-01-01 00:00:00.01'"),
make_check("'1965-09-25 11:19:33.100314'"),
make_check("'2010-02-09 23:11:45.1202'"),
(None, "NULL")]);
test_type("TIMESTAMP WITH TIME ZONE",
&[make_check("'1970-01-01 00:00:00.01'"),
(None, "NULL"),
],
);
test_type(
"TIMESTAMP WITH TIME ZONE",
&[
make_check("'1970-01-01 00:00:00.01'"),
make_check("'1965-09-25 11:19:33.100314'"),
make_check("'2010-02-09 23:11:45.1202'"),
(None, "NULL")]);
(None, "NULL"),
],
);
}
#[test]
fn test_with_special_tm_params() {
fn make_check<'a>(time: &'a str) -> (Timestamp<Timespec>, &'a str) {
(Timestamp::Value(time::strptime(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap().to_timespec()),
time)
(
Timestamp::Value(
time::strptime(time, "'%Y-%m-%d %H:%M:%S.%f'")
.unwrap()
.to_timespec(),
),
time,
)
}
test_type("TIMESTAMP",
&[make_check("'1970-01-01 00:00:00.01'"),
test_type(
"TIMESTAMP",
&[
make_check("'1970-01-01 00:00:00.01'"),
make_check("'1965-09-25 11:19:33.100314'"),
make_check("'2010-02-09 23:11:45.1202'"),
(Timestamp::PosInfinity, "'infinity'"),
(Timestamp::NegInfinity, "'-infinity'")]);
test_type("TIMESTAMP WITH TIME ZONE",
&[make_check("'1970-01-01 00:00:00.01'"),
(Timestamp::NegInfinity, "'-infinity'"),
],
);
test_type(
"TIMESTAMP WITH TIME ZONE",
&[
make_check("'1970-01-01 00:00:00.01'"),
make_check("'1965-09-25 11:19:33.100314'"),
make_check("'2010-02-09 23:11:45.1202'"),
(Timestamp::PosInfinity, "'infinity'"),
(Timestamp::NegInfinity, "'-infinity'")]);
(Timestamp::NegInfinity, "'-infinity'"),
],
);
}

View File

@ -4,7 +4,16 @@ use types::test_type;
#[test]
fn test_uuid_params() {
test_type("UUID", &[(Some(uuid::Uuid::parse_str("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11").unwrap()),
"'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'"),
(None, "NULL")])
test_type(
"UUID",
&[
(
Some(
uuid::Uuid::parse_str("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11").unwrap(),
),
"'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'",
),
(None, "NULL"),
],
)
}

View File

@ -137,21 +137,29 @@ pub enum TlsMode {
///
/// Only the host and port of the connection info are used. See
/// `Connection::connect` for details of the `params` argument.
pub fn cancel_query<T>(params: T,
pub fn cancel_query<T>(
params: T,
tls_mode: TlsMode,
cancel_data: CancelData,
handle: &Handle)
-> BoxFuture<(), ConnectError>
where T: IntoConnectParams
handle: &Handle,
) -> BoxFuture<(), ConnectError>
where
T: IntoConnectParams,
{
let params = match params.into_connect_params() {
Ok(params) => {
Either::A(stream::connect(params.host().clone(), params.port(), tls_mode, handle))
Either::A(stream::connect(
params.host().clone(),
params.port(),
tls_mode,
handle,
))
}
Err(e) => Either::B(Err(ConnectError::ConnectParams(e)).into_future()),
};
params.and_then(move |c| {
params
.and_then(move |c| {
let mut buf = vec![];
frontend::cancel_request(cancel_data.process_id, cancel_data.secret_key, &mut buf);
c.send(buf).map_err(ConnectError::Io)
@ -177,8 +185,7 @@ impl InnerConnection {
fn read(self) -> IoFuture<(backend::Message, InnerConnection)> {
self.into_future()
.map_err(|e| e.0)
.and_then(|(m, mut s)| {
match m {
.and_then(|(m, mut s)| match m {
Some(backend::Message::NotificationResponse(body)) => {
let process_id = body.process_id();
let channel = match body.channel() {
@ -202,7 +209,6 @@ impl InnerConnection {
let err = io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected EOF");
Either::A(Err(err).into_future())
}
}
})
.boxed()
}
@ -247,8 +253,7 @@ pub struct Connection(InnerConnection);
// FIXME fill out
impl fmt::Debug for Connection {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Connection")
.finish()
fmt.debug_struct("Connection").finish()
}
}
@ -271,23 +276,28 @@ impl Connection {
/// path contains non-UTF 8 characters, a `ConnectParams` struct should be
/// created manually and passed in. Note that Postgres does not support TLS
/// over Unix sockets.
pub fn connect<T>(params: T,
pub fn connect<T>(
params: T,
tls_mode: TlsMode,
handle: &Handle)
-> BoxFuture<Connection, ConnectError>
where T: IntoConnectParams
handle: &Handle,
) -> BoxFuture<Connection, ConnectError>
where
T: IntoConnectParams,
{
let fut = match params.into_connect_params() {
Ok(params) => {
Either::A(stream::connect(params.host().clone(), params.port(), tls_mode, handle)
.map(|s| (s, params)))
Either::A(
stream::connect(params.host().clone(), params.port(), tls_mode, handle)
.map(|s| (s, params)),
)
}
Err(e) => Either::B(Err(ConnectError::ConnectParams(e)).into_future()),
};
fut.map(|(s, params)| {
let (sender, receiver) = mpsc::channel();
(Connection(InnerConnection {
(
Connection(InnerConnection {
stream: s,
close_sender: sender,
close_receiver: receiver,
@ -302,17 +312,18 @@ impl Connection {
has_typeinfo_enum_query: false,
has_typeinfo_composite_query: false,
}),
params)
})
.and_then(|(s, params)| s.startup(params))
params,
)
}).and_then(|(s, params)| s.startup(params))
.and_then(|(s, params)| s.handle_auth(params))
.and_then(|s| s.finish_startup())
.boxed()
}
fn startup(self,
params: ConnectParams)
-> BoxFuture<(Connection, ConnectParams), ConnectError> {
fn startup(
self,
params: ConnectParams,
) -> BoxFuture<(Connection, ConnectParams), ConnectError> {
let mut buf = vec![];
let result = {
let options = [("client_encoding", "UTF8"), ("timezone", "GMT")];
@ -348,27 +359,35 @@ impl Connection {
.map_err(Into::into)
}
None => {
Err(ConnectError::ConnectParams("password was required but not \
Err(ConnectError::ConnectParams(
"password was required but not \
provided"
.into()))
.into(),
))
}
}
}
backend::Message::AuthenticationMd5Password(body) => {
match params.user().and_then(|u| u.password().map(|p| (u.name(), p))) {
match params.user().and_then(
|u| u.password().map(|p| (u.name(), p)),
) {
Some((user, pass)) => {
let pass = authentication::md5_hash(user.as_bytes(),
let pass = authentication::md5_hash(
user.as_bytes(),
pass.as_bytes(),
body.salt());
body.salt(),
);
let mut buf = vec![];
frontend::password_message(&pass, &mut buf)
.map(|()| Some(buf))
.map_err(Into::into)
}
None => {
Err(ConnectError::ConnectParams("password was required but not \
Err(ConnectError::ConnectParams(
"password was required but not \
provided"
.into()))
.into(),
))
}
}
}
@ -428,9 +447,10 @@ impl Connection {
}
// This has its own read_rows since it will need to handle multiple query completions
fn simple_read_rows(self,
mut rows: Vec<RowData>)
-> BoxFuture<(Vec<RowData>, Connection), Error> {
fn simple_read_rows(
self,
mut rows: Vec<RowData>,
) -> BoxFuture<(Vec<RowData>, Connection), Error> {
self.0
.read()
.map_err(Error::Io)
@ -457,7 +477,8 @@ impl Connection {
}
fn ready<T>(self, t: T) -> BoxFuture<(T, Connection), Error>
where T: 'static + Send
where
T: 'static + Send,
{
self.0
.read()
@ -485,7 +506,9 @@ impl Connection {
frontend::sync(&mut buf);
messages.push(buf);
self.0
.send_all(futures::stream::iter(messages.into_iter().map(Ok::<_, io::Error>)))
.send_all(futures::stream::iter(
messages.into_iter().map(Ok::<_, io::Error>),
))
.map_err(Error::Io)
.and_then(|s| Connection(s.0).finish_close_gc())
.boxed()
@ -505,7 +528,8 @@ impl Connection {
}
fn ready_err<T>(self, body: ErrorResponseBody) -> BoxFuture<T, Error>
where T: 'static + Send
where
T: 'static + Send,
{
DbError::new(&mut body.fields())
.map_err(Error::Io)
@ -529,15 +553,14 @@ impl Connection {
/// data in the statement. Do not form statements via string concatenation
/// and feed them into this method.
pub fn batch_execute(self, query: &str) -> BoxFuture<Connection, Error> {
self.simple_query(query)
.map(|r| r.1)
.boxed()
self.simple_query(query).map(|r| r.1).boxed()
}
fn raw_prepare(self,
fn raw_prepare(
self,
name: &str,
query: &str)
-> BoxFuture<(Vec<Type>, Vec<Column>, Connection), Error> {
query: &str,
) -> BoxFuture<(Vec<Type>, Vec<Column>, Connection), Error> {
let mut parse = vec![];
let mut describe = vec![];
let mut sync = vec![];
@ -605,17 +628,19 @@ impl Connection {
.boxed()
}
fn get_types<T, U, I, F, G>(self,
fn get_types<T, U, I, F, G>(
self,
mut raw: I,
mut out: Vec<U>,
mut get_oid: F,
mut build: G)
-> BoxFuture<(Vec<U>, Connection), Error>
where T: 'static + Send,
mut build: G,
) -> BoxFuture<(Vec<U>, Connection), Error>
where
T: 'static + Send,
U: 'static + Send,
I: 'static + Send + Iterator<Item = T>,
F: 'static + Send + FnMut(&T) -> Oid,
G: 'static + Send + FnMut(T, Type) -> U
G: 'static + Send + FnMut(T, Type) -> U,
{
match raw.next() {
Some(v) => {
@ -651,7 +676,9 @@ impl Connection {
fn get_unknown_type(self, oid: Oid) -> BoxFuture<(Other, Connection), Error> {
self.setup_typeinfo_query()
.and_then(move |c| c.raw_execute(TYPEINFO_QUERY, "", &[Type::Oid], &[&oid]))
.and_then(move |c| {
c.raw_execute(TYPEINFO_QUERY, "", &[Type::Oid], &[&oid])
})
.and_then(|c| c.read_rows().collect())
.and_then(move |(r, c)| {
let get = |idx| r.get(0).and_then(|r| r.get(idx));
@ -688,22 +715,42 @@ impl Connection {
let kind = if type_ == b'p' as i8 {
Either::A(Ok((Kind::Pseudo, c)).into_future())
} else if type_ == b'e' as i8 {
Either::B(c.get_enum_variants(oid).map(|(v, c)| (Kind::Enum(v), c)).boxed())
Either::B(
c.get_enum_variants(oid)
.map(|(v, c)| (Kind::Enum(v), c))
.boxed(),
)
} else if basetype != 0 {
Either::B(c.get_type(basetype).map(|(t, c)| (Kind::Domain(t), c)).boxed())
Either::B(
c.get_type(basetype)
.map(|(t, c)| (Kind::Domain(t), c))
.boxed(),
)
} else if elem_oid != 0 {
Either::B(c.get_type(elem_oid).map(|(t, c)| (Kind::Array(t), c)).boxed())
Either::B(
c.get_type(elem_oid)
.map(|(t, c)| (Kind::Array(t), c))
.boxed(),
)
} else if relid != 0 {
Either::B(c.get_composite_fields(relid)
Either::B(
c.get_composite_fields(relid)
.map(|(f, c)| (Kind::Composite(f), c))
.boxed())
.boxed(),
)
} else if let Some(rngsubtype) = rngsubtype {
Either::B(c.get_type(rngsubtype).map(|(t, c)| (Kind::Range(t), c)).boxed())
Either::B(
c.get_type(rngsubtype)
.map(|(t, c)| (Kind::Range(t), c))
.boxed(),
)
} else {
Either::A(Ok((Kind::Simple, c)).into_future())
};
Either::B(kind.map(move |(k, c)| (Other::new(name, oid, k, schema), c)))
Either::B(kind.map(
move |(k, c)| (Other::new(name, oid, k, schema), c),
))
})
.boxed()
}
@ -713,7 +760,8 @@ impl Connection {
return Ok(self).into_future().boxed();
}
self.raw_prepare(TYPEINFO_QUERY,
self.raw_prepare(
TYPEINFO_QUERY,
"SELECT t.typname, t.typtype, t.typelem, r.rngsubtype, \
t.typbasetype, n.nspname, t.typrelid \
FROM pg_catalog.pg_type t \
@ -721,8 +769,8 @@ impl Connection {
r.rngtypid = t.oid \
INNER JOIN pg_catalog.pg_namespace n ON \
t.typnamespace = n.oid \
WHERE t.oid = $1")
.or_else(|e| {
WHERE t.oid = $1",
).or_else(|e| {
match e {
// Range types weren't added until Postgres 9.2, so pg_range may not exist
Error::Db(e, c) => {
@ -730,14 +778,16 @@ impl Connection {
return Either::B(Err(Error::Db(e, c)).into_future());
}
Either::A(c.raw_prepare(TYPEINFO_QUERY,
Either::A(c.raw_prepare(
TYPEINFO_QUERY,
"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"))
WHERE t.oid = $1",
))
}
e => Either::B(Err(e).into_future()),
}
@ -751,7 +801,9 @@ impl Connection {
fn get_enum_variants(self, oid: Oid) -> BoxFuture<(Vec<String>, Connection), Error> {
self.setup_typeinfo_enum_query()
.and_then(move |c| c.raw_execute(TYPEINFO_ENUM_QUERY, "", &[Type::Oid], &[&oid]))
.and_then(move |c| {
c.raw_execute(TYPEINFO_ENUM_QUERY, "", &[Type::Oid], &[&oid])
})
.and_then(|c| c.read_rows().collect())
.and_then(|(r, c)| {
let mut variants = vec![];
@ -772,20 +824,23 @@ impl Connection {
return Ok(self).into_future().boxed();
}
self.raw_prepare(TYPEINFO_ENUM_QUERY,
self.raw_prepare(
TYPEINFO_ENUM_QUERY,
"SELECT enumlabel \
FROM pg_catalog.pg_enum \
WHERE enumtypid = $1 \
ORDER BY enumsortorder")
.or_else(|e| match e {
ORDER BY enumsortorder",
).or_else(|e| match e {
Error::Db(e, c) => {
if e.code != SqlState::UndefinedColumn {
return Either::B(Err(Error::Db(e, c)).into_future());
}
Either::A(c.raw_prepare(TYPEINFO_ENUM_QUERY,
Either::A(c.raw_prepare(
TYPEINFO_ENUM_QUERY,
"SELECT enumlabel FROM pg_catalog.pg_enum WHERE \
enumtypid = $1 ORDER BY oid"))
enumtypid = $1 ORDER BY oid",
))
}
e => Either::B(Err(e).into_future()),
})
@ -803,8 +858,9 @@ impl Connection {
})
.and_then(|c| c.read_rows().collect())
.and_then(|(r, c)| {
futures::stream::iter(r.into_iter().map(Ok))
.fold((vec![], c), |(mut fields, c), row| {
futures::stream::iter(r.into_iter().map(Ok)).fold(
(vec![], c),
|(mut fields, c), row| {
let name = match String::from_sql_nullable(&Type::Name, row.get(0)) {
Ok(name) => name,
Err(e) => return Either::A(Err(Error::Conversion(e, c)).into_future()),
@ -813,12 +869,12 @@ impl Connection {
Ok(oid) => oid,
Err(e) => return Either::A(Err(Error::Conversion(e, c)).into_future()),
};
Either::B(c.get_type(oid)
.map(move |(ty, c)| {
Either::B(c.get_type(oid).map(move |(ty, c)| {
fields.push(Field::new(name, ty));
(fields, c)
}))
})
},
)
})
.boxed()
}
@ -828,36 +884,41 @@ impl Connection {
return Ok(self).into_future().boxed();
}
self.raw_prepare(TYPEINFO_COMPOSITE_QUERY,
self.raw_prepare(
TYPEINFO_COMPOSITE_QUERY,
"SELECT attname, atttypid \
FROM pg_catalog.pg_attribute \
WHERE attrelid = $1 \
AND NOT attisdropped \
AND attnum > 0 \
ORDER BY attnum")
.map(|(_, _, mut c)| {
ORDER BY attnum",
).map(|(_, _, mut c)| {
c.0.has_typeinfo_composite_query = true;
c
})
.boxed()
}
fn raw_execute(self,
fn raw_execute(
self,
stmt: &str,
portal: &str,
param_types: &[Type],
params: &[&ToSql])
-> BoxFuture<Connection, Error> {
assert!(param_types.len() == params.len(),
params: &[&ToSql],
) -> BoxFuture<Connection, Error> {
assert!(
param_types.len() == params.len(),
"expected {} parameters but got {}",
param_types.len(),
params.len());
params.len()
);
let mut bind = vec![];
let mut execute = vec![];
let mut sync = vec![];
frontend::sync(&mut sync);
let r = frontend::bind(portal,
let r = frontend::bind(
portal,
stmt,
Some(1),
params.iter().zip(param_types),
@ -867,7 +928,8 @@ impl Connection {
Err(e) => Err(e),
},
Some(1),
&mut bind);
&mut bind,
);
let r = match r {
Ok(()) => Ok(self),
Err(frontend::BindError::Conversion(e)) => Err(Error::Conversion(e, self)),
@ -878,8 +940,7 @@ impl Connection {
frontend::execute(portal, 0, &mut execute)
.map(|()| s)
.map_err(Error::Io)
})
.into_future()
}).into_future()
.and_then(|s| {
let it = Some(bind)
.into_iter()
@ -901,17 +962,12 @@ impl Connection {
self.0
.read()
.map_err(Error::Io)
.and_then(|(m, s)| {
match m {
.and_then(|(m, s)| match m {
backend::Message::DataRow(_) => Connection(s).finish_execute().boxed(),
backend::Message::CommandComplete(body) => {
body.tag()
.map(|tag| {
tag.split_whitespace()
.last()
.unwrap()
.parse()
.unwrap_or(0)
tag.split_whitespace().last().unwrap().parse().unwrap_or(0)
})
.map_err(Error::Io)
.into_future()
@ -921,23 +977,20 @@ impl Connection {
backend::Message::EmptyQueryResponse => Connection(s).ready(0).boxed(),
backend::Message::ErrorResponse(body) => Connection(s).ready_err(body).boxed(),
_ => Err(bad_message()).into_future().boxed(),
}
})
.boxed()
}
fn read_rows(self) -> BoxStateStream<RowData, Connection, Error> {
futures_state_stream::unfold(self, |c| {
c.read_row()
.and_then(|(r, c)| match r {
c.read_row().and_then(|(r, c)| match r {
Some(data) => {
let event = StreamEvent::Next((data, c));
Either::A(Ok(event).into_future())
}
None => Either::B(c.ready(()).map(|((), c)| StreamEvent::Done(c))),
})
})
.boxed()
}).boxed()
}
fn read_row(self) -> BoxFuture<(Option<RowData>, Connection), Error> {
@ -948,10 +1001,12 @@ impl Connection {
let c = Connection(s);
match m {
backend::Message::DataRow(body) => {
Either::A(RowData::new(body)
Either::A(
RowData::new(body)
.map(|r| (Some(r), c))
.map_err(Error::Io)
.into_future())
.into_future(),
)
}
backend::Message::EmptyQueryResponse |
backend::Message::CommandComplete(_) => Either::A(Ok((None, c)).into_future()),
@ -981,10 +1036,11 @@ impl Connection {
///
/// Panics if the number of parameters provided does not match the number
/// expected.
pub fn execute(self,
pub fn execute(
self,
statement: &Statement,
params: &[&ToSql])
-> BoxFuture<(u64, Connection), Error> {
params: &[&ToSql],
) -> BoxFuture<(u64, Connection), Error> {
self.raw_execute(statement.name(), "", statement.parameters(), params)
.and_then(|conn| conn.finish_execute())
.boxed()
@ -996,10 +1052,11 @@ impl Connection {
///
/// Panics if the number of parameters provided does not match the number
/// expected.
pub fn query(self,
pub fn query(
self,
statement: &Statement,
params: &[&ToSql])
-> BoxStateStream<Row, Connection, Error> {
params: &[&ToSql],
) -> BoxStateStream<Row, Connection, Error> {
let columns = statement.columns_arc().clone();
self.raw_execute(statement.name(), "", statement.parameters(), params)
.map(|c| c.read_rows().map(move |r| Row::new(columns.clone(), r)))
@ -1077,7 +1134,8 @@ fn connect_err(fields: &mut ErrorFields) -> ConnectError {
}
fn bad_message<T>() -> T
where T: From<io::Error>
where
T: From<io::Error>,
{
io::Error::new(io::ErrorKind::InvalidInput, "unexpected message").into()
}
@ -1087,11 +1145,12 @@ trait RowNew {
}
trait StatementNew {
fn new(close_sender: Sender<(u8, String)>,
fn new(
close_sender: Sender<(u8, String)>,
name: String,
params: Vec<Type>,
columns: Arc<Vec<Column>>)
-> Statement;
columns: Arc<Vec<Column>>,
) -> Statement;
fn columns_arc(&self) -> &Arc<Vec<Column>>;

View File

@ -48,8 +48,9 @@ impl Row {
/// Panics if the index does not reference a column or the return type is
/// not compatible with the Postgres type.
pub fn get<T, I>(&self, idx: I) -> T
where T: FromSql,
I: RowIndex + fmt::Debug
where
T: FromSql,
I: RowIndex + fmt::Debug,
{
match self.try_get(&idx) {
Ok(Some(v)) => v,
@ -67,8 +68,9 @@ impl Row {
/// if there was an error converting the result value, and `Some(Ok(..))`
/// on success.
pub fn try_get<T, I>(&self, idx: I) -> Result<Option<T>, Box<Error + Sync + Send>>
where T: FromSql,
I: RowIndex
where
T: FromSql,
I: RowIndex,
{
let idx = match idx.idx(&self.columns) {
Some(idx) => idx,

View File

@ -19,11 +19,12 @@ pub struct Statement {
}
impl StatementNew for Statement {
fn new(close_sender: Sender<(u8, String)>,
fn new(
close_sender: Sender<(u8, String)>,
name: String,
params: Vec<Type>,
columns: Arc<Vec<Column>>)
-> Statement {
columns: Arc<Vec<Column>>,
) -> Statement {
Statement {
close_sender: close_sender,
name: name,

View File

@ -20,31 +20,39 @@ use tls::TlsStream;
pub type PostgresStream = Framed<Box<TlsStream>, PostgresCodec>;
pub fn connect(host: Host,
pub fn connect(
host: Host,
port: u16,
tls_mode: TlsMode,
handle: &Handle)
-> BoxFuture<PostgresStream, ConnectError> {
handle: &Handle,
) -> BoxFuture<PostgresStream, ConnectError> {
let inner = match host {
Host::Tcp(ref host) => {
Either::A(tokio_dns::tcp_connect((&**host, port), handle.remote().clone())
Either::A(
tokio_dns::tcp_connect((&**host, port), handle.remote().clone())
.map(|s| Stream(InnerStream::Tcp(s)))
.map_err(ConnectError::Io))
.map_err(ConnectError::Io),
)
}
#[cfg(unix)]
Host::Unix(ref host) => {
let addr = host.join(format!(".s.PGSQL.{}", port));
Either::B(UnixStream::connect(addr, handle)
Either::B(
UnixStream::connect(addr, handle)
.map(|s| Stream(InnerStream::Unix(s)))
.map_err(ConnectError::Io)
.into_future())
.into_future(),
)
}
#[cfg(not(unix))]
Host::Unix(_) => {
Either::B(Err(ConnectError::ConnectParams("unix sockets are not supported on this \
Either::B(
Err(ConnectError::ConnectParams(
"unix sockets are not supported on this \
platform"
.into()))
.into_future())
.into(),
)).into_future(),
)
}
};
@ -52,7 +60,8 @@ pub fn connect(host: Host,
TlsMode::Require(h) => (true, h),
TlsMode::Prefer(h) => (false, h),
TlsMode::None => {
return inner.map(|s| {
return inner
.map(|s| {
let s: Box<TlsStream> = Box::new(s);
s.framed(PostgresCodec)
})
@ -60,29 +69,34 @@ pub fn connect(host: Host,
}
};
inner.map(|s| s.framed(SslCodec))
inner
.map(|s| s.framed(SslCodec))
.and_then(|s| {
let mut buf = vec![];
frontend::ssl_request(&mut buf);
s.send(buf)
.map_err(ConnectError::Io)
s.send(buf).map_err(ConnectError::Io)
})
.and_then(|s| s.into_future().map_err(|e| ConnectError::Io(e.0)))
.and_then(move |(m, s)| {
let s = s.into_inner();
match (m, required) {
(Some(b'N'), true) => {
Either::A(Err(ConnectError::Tls("the server does not support TLS".into()))
.into_future())
Either::A(
Err(ConnectError::Tls("the server does not support TLS".into()))
.into_future(),
)
}
(Some(b'N'), false) => {
let s: Box<TlsStream> = Box::new(s);
Either::A(Ok(s).into_future())
}
(None, _) => {
Either::A(Err(ConnectError::Io(io::Error::new(io::ErrorKind::UnexpectedEof,
"unexpected EOF")))
.into_future())
Either::A(
Err(ConnectError::Io(io::Error::new(
io::ErrorKind::UnexpectedEof,
"unexpected EOF",
))).into_future(),
)
}
_ => {
let host = match host {
@ -144,7 +158,8 @@ impl AsyncRead for Stream {
}
fn read_buf<B>(&mut self, buf: &mut B) -> Poll<usize, io::Error>
where B: BufMut
where
B: BufMut,
{
match self.0 {
InnerStream::Tcp(ref mut s) => s.read_buf(buf),

View File

@ -14,9 +14,11 @@ use types::{ToSql, FromSql, Type, IsNull, Kind};
fn md5_user() {
let mut l = Core::new().unwrap();
let handle = l.handle();
let done = Connection::connect("postgres://md5_user:password@localhost/postgres",
let done = Connection::connect(
"postgres://md5_user:password@localhost/postgres",
TlsMode::None,
&handle);
&handle,
);
l.run(done).unwrap();
}
@ -24,9 +26,11 @@ fn md5_user() {
fn md5_user_no_pass() {
let mut l = Core::new().unwrap();
let handle = l.handle();
let done = Connection::connect("postgres://md5_user@localhost/postgres",
let done = Connection::connect(
"postgres://md5_user@localhost/postgres",
TlsMode::None,
&handle);
&handle,
);
match l.run(done) {
Err(ConnectError::ConnectParams(_)) => {}
Err(e) => panic!("unexpected error {}", e),
@ -38,9 +42,11 @@ fn md5_user_no_pass() {
fn md5_user_wrong_pass() {
let mut l = Core::new().unwrap();
let handle = l.handle();
let done = Connection::connect("postgres://md5_user:foobar@localhost/postgres",
let done = Connection::connect(
"postgres://md5_user:foobar@localhost/postgres",
TlsMode::None,
&handle);
&handle,
);
match l.run(done) {
Err(ConnectError::Db(ref e)) if e.code == SqlState::InvalidPassword => {}
Err(e) => panic!("unexpected error {}", e),
@ -52,9 +58,11 @@ fn md5_user_wrong_pass() {
fn pass_user() {
let mut l = Core::new().unwrap();
let handle = l.handle();
let done = Connection::connect("postgres://pass_user:password@localhost/postgres",
let done = Connection::connect(
"postgres://pass_user:password@localhost/postgres",
TlsMode::None,
&handle);
&handle,
);
l.run(done).unwrap();
}
@ -62,9 +70,11 @@ fn pass_user() {
fn pass_user_no_pass() {
let mut l = Core::new().unwrap();
let handle = l.handle();
let done = Connection::connect("postgres://pass_user@localhost/postgres",
let done = Connection::connect(
"postgres://pass_user@localhost/postgres",
TlsMode::None,
&handle);
&handle,
);
match l.run(done) {
Err(ConnectError::ConnectParams(_)) => {}
Err(e) => panic!("unexpected error {}", e),
@ -76,9 +86,11 @@ fn pass_user_no_pass() {
fn pass_user_wrong_pass() {
let mut l = Core::new().unwrap();
let handle = l.handle();
let done = Connection::connect("postgres://pass_user:foobar@localhost/postgres",
let done = Connection::connect(
"postgres://pass_user:foobar@localhost/postgres",
TlsMode::None,
&handle);
&handle,
);
match l.run(done) {
Err(ConnectError::Db(ref e)) if e.code == SqlState::InvalidPassword => {}
Err(e) => panic!("unexpected error {}", e),
@ -90,7 +102,11 @@ fn pass_user_wrong_pass() {
fn batch_execute_ok() {
let mut l = Core::new().unwrap();
let done = Connection::connect("postgres://postgres@localhost", TlsMode::None, &l.handle())
.then(|c| c.unwrap().batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL);"));
.then(|c| {
c.unwrap().batch_execute(
"CREATE TEMPORARY TABLE foo (id SERIAL);",
)
});
l.run(done).unwrap();
}
@ -99,9 +115,10 @@ fn batch_execute_err() {
let mut l = Core::new().unwrap();
let done = Connection::connect("postgres://postgres@localhost", TlsMode::None, &l.handle())
.then(|r| {
r.unwrap()
.batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL); INSERT INTO foo DEFAULT \
VALUES;")
r.unwrap().batch_execute(
"CREATE TEMPORARY TABLE foo (id SERIAL); INSERT INTO foo DEFAULT \
VALUES;",
)
})
.and_then(|c| c.batch_execute("SELECT * FROM bogo"))
.then(|r| match r {
@ -120,7 +137,9 @@ fn prepare_execute() {
let mut l = Core::new().unwrap();
let done = Connection::connect("postgres://postgres@localhost", TlsMode::None, &l.handle())
.then(|c| {
c.unwrap().prepare("CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY, name VARCHAR)")
c.unwrap().prepare(
"CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY, name VARCHAR)",
)
})
.and_then(|(s, c)| c.execute(&s, &[]))
.and_then(|(n, c)| {
@ -146,8 +165,10 @@ fn query() {
let mut l = Core::new().unwrap();
let done = Connection::connect("postgres://postgres@localhost", TlsMode::None, &l.handle())
.then(|c| {
c.unwrap().batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL, name VARCHAR);
INSERT INTO foo (name) VALUES ('joe'), ('bob')")
c.unwrap().batch_execute(
"CREATE TEMPORARY TABLE foo (id SERIAL, name VARCHAR);
INSERT INTO foo (name) VALUES ('joe'), ('bob')",
)
})
.and_then(|c| c.prepare("SELECT id, name FROM foo ORDER BY id"))
.and_then(|(s, c)| c.query(&s, &[]).collect())
@ -166,16 +187,25 @@ fn query() {
#[test]
fn transaction() {
let mut l = Core::new().unwrap();
let done =
Connection::connect("postgres://postgres@localhost", TlsMode::None, &l.handle())
let done = Connection::connect("postgres://postgres@localhost", TlsMode::None, &l.handle())
.then(|c| {
c.unwrap().batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL, name VARCHAR);")
c.unwrap().batch_execute(
"CREATE TEMPORARY TABLE foo (id SERIAL, name VARCHAR);",
)
})
.then(|c| c.unwrap().transaction())
.then(|t| t.unwrap().batch_execute("INSERT INTO foo (name) VALUES ('joe');"))
.then(|t| {
t.unwrap().batch_execute(
"INSERT INTO foo (name) VALUES ('joe');",
)
})
.then(|t| t.unwrap().rollback())
.then(|c| c.unwrap().transaction())
.then(|t| t.unwrap().batch_execute("INSERT INTO foo (name) VALUES ('bob');"))
.then(|t| {
t.unwrap().batch_execute(
"INSERT INTO foo (name) VALUES ('bob');",
)
})
.then(|t| t.unwrap().commit())
.then(|c| c.unwrap().prepare("SELECT name FROM foo"))
.and_then(|(s, c)| c.query(&s, &[]).collect())
@ -195,9 +225,11 @@ fn unix_socket() {
.and_then(|(s, c)| c.query(&s, &[]).collect())
.then(|r| {
let r = r.unwrap().0;
let params = ConnectParams::builder()
.user("postgres", None)
.build(Host::Unix(PathBuf::from(r[0].get::<String, _>(0))));
let params = ConnectParams::builder().user("postgres", None).build(
Host::Unix(
PathBuf::from(r[0].get::<String, _>(0)),
),
);
Connection::connect(params, TlsMode::None, &handle)
})
.then(|c| c.unwrap().batch_execute(""));
@ -209,9 +241,11 @@ fn ssl_user_ssl_required() {
let mut l = Core::new().unwrap();
let handle = l.handle();
let done = Connection::connect("postgres://ssl_user@localhost/postgres",
let done = Connection::connect(
"postgres://ssl_user@localhost/postgres",
TlsMode::None,
&handle);
&handle,
);
match l.run(done) {
Err(ConnectError::Db(e)) => assert!(e.code == SqlState::InvalidAuthorizationSpecification),
@ -227,14 +261,18 @@ fn openssl_required() {
use tls::openssl::OpenSsl;
let mut builder = SslConnectorBuilder::new(SslMethod::tls()).unwrap();
builder.builder_mut().set_ca_file("../.travis/server.crt").unwrap();
builder
.builder_mut()
.set_ca_file("../.travis/server.crt")
.unwrap();
let negotiator = OpenSsl::from(builder.build());
let mut l = Core::new().unwrap();
let done = Connection::connect("postgres://ssl_user@localhost/postgres",
let done = Connection::connect(
"postgres://ssl_user@localhost/postgres",
TlsMode::Require(Box::new(negotiator)),
&l.handle())
.then(|c| c.unwrap().prepare("SELECT 1"))
&l.handle(),
).then(|c| c.unwrap().prepare("SELECT 1"))
.and_then(|(s, c)| c.query(&s, &[]).collect())
.map(|(r, _)| assert_eq!(r[0].get::<i32, _>(0), 1));
l.run(done).unwrap();
@ -246,10 +284,11 @@ fn domain() {
struct SessionId(Vec<u8>);
impl ToSql for SessionId {
fn to_sql(&self,
fn to_sql(
&self,
ty: &Type,
out: &mut Vec<u8>)
-> Result<IsNull, Box<StdError + Sync + Send>> {
out: &mut Vec<u8>,
) -> Result<IsNull, Box<StdError + Sync + Send>> {
let inner = match *ty.kind() {
Kind::Domain(ref inner) => inner,
_ => unreachable!(),
@ -282,10 +321,12 @@ fn domain() {
let handle = l.handle();
let done = Connection::connect("postgres://postgres@localhost", TlsMode::None, &handle)
.then(|c| {
c.unwrap().batch_execute("CREATE DOMAIN pg_temp.session_id AS bytea \
c.unwrap().batch_execute(
"CREATE DOMAIN pg_temp.session_id AS bytea \
CHECK(octet_length(VALUE) = 16);
CREATE \
TABLE pg_temp.foo (id pg_temp.session_id);")
TABLE pg_temp.foo (id pg_temp.session_id);",
)
})
.and_then(|c| c.prepare("INSERT INTO pg_temp.foo (id) VALUES ($1)"))
.and_then(|(s, c)| {
@ -309,11 +350,13 @@ fn composite() {
let done = Connection::connect("postgres://postgres@localhost", TlsMode::None, &handle)
.then(|c| {
c.unwrap().batch_execute("CREATE TYPE pg_temp.inventory_item AS (
c.unwrap().batch_execute(
"CREATE TYPE pg_temp.inventory_item AS (
name TEXT,
supplier INTEGER,
price NUMERIC
)")
)",
)
})
.and_then(|c| c.prepare("SELECT $1::inventory_item"))
.map(|(s, _)| {
@ -341,7 +384,9 @@ fn enum_() {
let done = Connection::connect("postgres://postgres@localhost", TlsMode::None, &handle)
.then(|c| {
c.unwrap().batch_execute("CREATE TYPE pg_temp.mood AS ENUM ('sad', 'ok', 'happy');")
c.unwrap().batch_execute(
"CREATE TYPE pg_temp.mood AS ENUM ('sad', 'ok', 'happy');",
)
})
.and_then(|c| c.prepare("SELECT $1::mood"))
.map(|(s, _)| {
@ -349,8 +394,10 @@ fn enum_() {
assert_eq!(type_.name(), "mood");
match *type_.kind() {
Kind::Enum(ref variants) => {
assert_eq!(variants,
&["sad".to_owned(), "ok".to_owned(), "happy".to_owned()]);
assert_eq!(
variants,
&["sad".to_owned(), "ok".to_owned(), "happy".to_owned()]
);
}
_ => panic!("bad type"),
}
@ -373,10 +420,12 @@ fn cancel() {
.into_future()
.then(move |r| {
assert!(r.is_ok());
cancel_query("postgres://postgres@localhost",
cancel_query(
"postgres://postgres@localhost",
TlsMode::None,
cancel_data,
&handle)
&handle,
)
})
.then(Ok::<_, ()>);
c.batch_execute("SELECT pg_sleep(10)")
@ -401,10 +450,13 @@ fn notifications() {
let done = Connection::connect("postgres://postgres@localhost", TlsMode::None, &handle)
.then(|c| c.unwrap().batch_execute("LISTEN test_notifications"))
.and_then(|c1| {
Connection::connect("postgres://postgres@localhost", TlsMode::None, &handle)
.then(|c2| {
c2.unwrap().batch_execute("NOTIFY test_notifications, 'foo'").map(|_| c1)
})
Connection::connect("postgres://postgres@localhost", TlsMode::None, &handle).then(
|c2| {
c2.unwrap()
.batch_execute("NOTIFY test_notifications, 'foo'")
.map(|_| c1)
},
)
})
.and_then(|c| c.notifications().into_future().map_err(|(e, _)| e))
.map(|(n, _)| {

View File

@ -31,8 +31,9 @@ impl TlsStream for Stream {
/// A trait implemented by types that can manage TLS encryption for a stream.
pub trait Handshake: 'static + Sync + Send {
/// Performs a TLS handshake, returning a wrapped stream.
fn handshake(self: Box<Self>,
fn handshake(
self: Box<Self>,
host: &str,
stream: Stream)
-> BoxFuture<Box<TlsStream>, Box<Error + Sync + Send>>;
stream: Stream,
) -> BoxFuture<Box<TlsStream>, Box<Error + Sync + Send>>;
}

View File

@ -38,10 +38,11 @@ impl From<SslConnector> for OpenSsl {
}
impl Handshake for OpenSsl {
fn handshake(self: Box<Self>,
fn handshake(
self: Box<Self>,
host: &str,
stream: Stream)
-> BoxFuture<Box<TlsStream>, Box<Error + Sync + Send>> {
stream: Stream,
) -> BoxFuture<Box<TlsStream>, Box<Error + Sync + Send>> {
self.0
.connect_async(host, stream)
.map(|s| {

View File

@ -39,10 +39,11 @@ impl Transaction {
}
/// Like `Connection::execute`.
pub fn execute(self,
pub fn execute(
self,
statement: &Statement,
params: &[&ToSql])
-> BoxFuture<(u64, Transaction), Error<Transaction>> {
params: &[&ToSql],
) -> BoxFuture<(u64, Transaction), Error<Transaction>> {
self.0
.execute(statement, params)
.map(|(n, c)| (n, Transaction(c)))
@ -51,10 +52,11 @@ impl Transaction {
}
/// Like `Connection::query`.
pub fn query(self,
pub fn query(
self,
statement: &Statement,
params: &[&ToSql])
-> BoxStateStream<Row, Transaction, Error<Transaction>> {
params: &[&ToSql],
) -> BoxStateStream<Row, Transaction, Error<Transaction>> {
self.0
.query(statement, params)
.map_state(Transaction)
@ -73,10 +75,7 @@ impl Transaction {
}
fn finish(self, query: &str) -> BoxFuture<Connection, Error> {
self.0
.simple_query(query)
.map(|(_, c)| c)
.boxed()
self.0.simple_query(query).map(|(_, c)| c).boxed()
}
}

View File

@ -1,8 +1,8 @@
//! Postgres types
#[doc(inline)]
pub use postgres_shared::types::{Oid, Type, Date, Timestamp, Kind, Field, Other, WasNull, WrongType,
FromSql, IsNull, ToSql};
pub use postgres_shared::types::{Oid, Type, Date, Timestamp, Kind, Field, Other, WasNull,
WrongType, FromSql, IsNull, ToSql};
#[doc(hidden)]
pub use postgres_shared::types::__to_sql_checked;