Merge branch 'release-v0.11.5' into release

This commit is contained in:
Steven Fackler 2016-03-29 21:48:17 -07:00
commit 08d6c7b700
12 changed files with 165 additions and 115 deletions

View File

@ -11,3 +11,4 @@ before_script:
script:
- cargo test
- cargo test --features "uuid rustc-serialize time unix_socket serde_json chrono openssl bit-vec eui48"
- (test $TRAVIS_RUST_VERSION != "nightly" || cargo test --features nightly)

View File

@ -1,11 +1,11 @@
[package]
name = "postgres"
version = "0.11.4"
version = "0.11.5"
authors = ["Steven Fackler <sfackler@gmail.com>"]
license = "MIT"
description = "A native PostgreSQL driver"
repository = "https://github.com/sfackler/rust-postgres"
documentation = "https://sfackler.github.io/rust-postgres/doc/v0.11.4/postgres"
documentation = "https://sfackler.github.io/rust-postgres/doc/v0.11.5/postgres"
readme = "README.md"
keywords = ["database", "postgres", "postgresql", "sql"]
include = ["src/*", "Cargo.toml", "LICENSE", "README.md", "THIRD_PARTY"]
@ -20,11 +20,14 @@ bench = false
name = "test"
path = "tests/test.rs"
[features]
nightly = []
[dependencies]
bufstream = "0.1"
byteorder = ">= 0.3, < 0.5"
byteorder = "0.5"
log = "0.3"
phf = "0.7"
phf = "=0.7.14"
hex = "0.1"
net2 = "0.2.16"
rustc-serialize = { version = "0.3", optional = true }

View File

@ -1,7 +1,7 @@
# Rust-Postgres
A native PostgreSQL driver for Rust.
[Documentation](https://sfackler.github.io/rust-postgres/doc/v0.11.4/postgres)
[Documentation](https://sfackler.github.io/rust-postgres/doc/v0.11.5/postgres)
[![Build Status](https://travis-ci.org/sfackler/rust-postgres.png?branch=master)](https://travis-ci.org/sfackler/rust-postgres) [![Latest Version](https://img.shields.io/crates/v/postgres.svg)](https://crates.io/crates/postgres)
@ -74,11 +74,11 @@ let conn = try!(Connection::connect("postgres://user:pass@host:port/database?arg
defaults to the value of `user` if not specified. The driver supports `trust`,
`password`, and `md5` authentication.
Unix domain sockets can be used as well by activating the `unix_socket` feature.
The `host` portion of the URI should be set to the absolute path to the
directory containing the socket file. Since `/` is a reserved character in
URLs, the path should be URL encoded. If Postgres stored its socket files in
`/run/postgres`, the connection would then look like:
Unix domain sockets can be used as well by activating the `unix_socket` or
`nightly` features. The `host` portion of the URI should be set to the absolute
path to the directory containing the socket file. Since `/` is a reserved
character in URLs, the path should be URL encoded. If Postgres stored its socket
files in `/run/postgres`, the connection would then look like:
```rust
let conn = try!(Connection::connect("postgres://postgres@%2Frun%2Fpostgres", SslMode::None));
```
@ -269,7 +269,7 @@ The [postgres-derive](https://github.com/sfackler/rust-postgres-derive)
crate will synthesize `ToSql` and `FromSql` implementations for enum, domain,
and composite Postgres types.
Support for array types is located in the
Full support for array types is located in the
[postgres-array](https://github.com/sfackler/rust-postgres-array) crate.
Support for range types is located in the
@ -284,8 +284,8 @@ crate.
### Unix socket connections
Support for connections through Unix domain sockets is provided optionally by
the `unix_socket` feature. It is only available on "unixy" platforms such as
OSX, BSD and Linux.
either the `unix_socket` or `nightly` features. It is only available on "unixy"
platforms such as OSX, BSD and Linux.
### UUID type

View File

@ -4,6 +4,6 @@ version = "0.1.0"
authors = ["Steven Fackler <sfackler@gmail.com>"]
[dependencies]
phf_codegen = "0.7"
phf_codegen = "=0.7.14"
regex = "0.1"
marksman_escape = "0.1"

View File

@ -1,6 +1,5 @@
//! Error types.
use byteorder;
use std::error;
use std::convert::From;
use std::fmt;
@ -227,12 +226,6 @@ impl From<DbError> for ConnectError {
}
}
impl From<byteorder::Error> for ConnectError {
fn from(err: byteorder::Error) -> ConnectError {
ConnectError::Io(From::from(err))
}
}
/// Represents the position of an error in a query.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ErrorPosition {
@ -299,12 +292,6 @@ impl From<io::Error> for Error {
}
}
impl From<byteorder::Error> for Error {
fn from(err: byteorder::Error) -> Error {
Error::Io(From::from(err))
}
}
impl From<Error> for io::Error {
fn from(err: Error) -> io::Error {
io::Error::new(io::ErrorKind::Other, err)

View File

@ -482,7 +482,7 @@ pub enum SqlState {
#[cfg_attr(rustfmt, rustfmt_skip)]
static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = ::phf::Map {
key: 1897749892740154578,
disps: &[
disps: ::phf::Slice::Static(&[
(0, 10),
(1, 206),
(0, 38),
@ -531,8 +531,8 @@ static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = ::phf::Map {
(0, 233),
(2, 149),
(0, 105),
],
entries: &[
]),
entries: ::phf::Slice::Static(&[
("42P03", SqlState::DuplicateCursor),
("22019", SqlState::InvalidEscapeCharacter),
("22022", SqlState::IndicatorOverflow),
@ -769,7 +769,7 @@ static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = ::phf::Map {
("28000", SqlState::InvalidAuthorizationSpecification),
("0Z002", SqlState::StackedDiagnosticsAccessedWithoutActiveHandler),
("02000", SqlState::NoData),
]
]),
};
impl SqlState {

View File

@ -38,9 +38,10 @@
//! }
//! }
//! ```
#![doc(html_root_url="https://sfackler.github.io/rust-postgres/doc/v0.11.4")]
#![doc(html_root_url="https://sfackler.github.io/rust-postgres/doc/v0.11.5")]
#![warn(missing_docs)]
#![allow(unknown_lints, needless_lifetimes)] // for clippy
#![cfg_attr(all(unix, feature = "nightly"), feature(unix_socket))]
extern crate bufstream;
extern crate byteorder;
@ -68,7 +69,7 @@ use std::mem;
use std::result;
use std::sync::Arc;
use std::time::Duration;
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
use std::path::PathBuf;
// FIXME remove in 0.12
@ -115,8 +116,8 @@ pub enum ConnectTarget {
Tcp(String),
/// Connect via a Unix domain socket in the specified directory.
///
/// Requires the `unix_socket` feature.
#[cfg(feature = "unix_socket")]
/// Requires the `unix_socket` or `nightly` feature.
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
Unix(PathBuf),
}
@ -173,14 +174,14 @@ impl<'a> IntoConnectParams for &'a str {
impl IntoConnectParams for Url {
fn into_connect_params(self) -> result::Result<ConnectParams, Box<StdError + StdSync + Send>> {
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
fn make_unix(maybe_path: String)
-> result::Result<ConnectTarget, Box<StdError + StdSync + Send>> {
Ok(ConnectTarget::Unix(PathBuf::from(maybe_path)))
}
#[cfg(not(feature = "unix_socket"))]
#[cfg(not(any(feature = "unix_socket", all(unix, feature = "nightly"))))]
fn make_unix(_: String) -> result::Result<ConnectTarget, Box<StdError + StdSync + Send>> {
Err("unix socket support requires the `unix_socket` feature".into())
Err("unix socket support requires the `unix_socket` or `nightly` features".into())
}
let Url { host, port, user, path: url::Path { mut path, query: options, .. }, .. } = self;
@ -1019,13 +1020,13 @@ impl Connection {
/// (5432) is used if none is specified. The database name defaults to the
/// username if not specified.
///
/// Connection via Unix sockets is supported with the `unix_socket`
/// feature. To connect to the server via Unix sockets, `host` should be
/// set to the absolute path of the directory containing the socket file.
/// Since `/` is a reserved character in URLs, the path should be URL
/// encoded. If the path contains non-UTF 8 characters, a `ConnectParams`
/// struct should be created manually and passed in. Note that Postgres
/// does not support SSL over Unix sockets.
/// Connection via Unix sockets is supported with either the `unix_socket`
/// or `nightly` features. To connect to the server via Unix sockets, `host`
/// should be set to the absolute path of the directory containing the
/// socket file. Since `/` is a reserved character in URLs, the path should
/// be URL encoded. If the path contains non-UTF 8 characters, a
/// `ConnectParams` struct should be created manually and passed in. Note
/// that Postgres does not support SSL over Unix sockets.
///
/// # Examples
///

View File

@ -9,6 +9,8 @@ use std::time::Duration;
use bufstream::BufStream;
#[cfg(feature = "unix_socket")]
use unix_socket::UnixStream;
#[cfg(all(not(feature = "unix_socket"), all(unix, feature = "nightly")))]
use std::os::unix::net::UnixStream;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(windows)]
@ -32,7 +34,7 @@ impl StreamOptions for BufStream<Box<StreamWrapper>> {
fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
match self.get_ref().get_ref().0 {
InternalStream::Tcp(ref s) => s.set_read_timeout(timeout),
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
InternalStream::Unix(ref s) => s.set_read_timeout(timeout),
}
}
@ -40,7 +42,7 @@ impl StreamOptions for BufStream<Box<StreamWrapper>> {
fn set_nonblocking(&self, nonblock: bool) -> io::Result<()> {
match self.get_ref().get_ref().0 {
InternalStream::Tcp(ref s) => s.set_nonblocking(nonblock),
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
InternalStream::Unix(ref s) => s.set_nonblocking(nonblock),
}
}
@ -56,7 +58,7 @@ impl fmt::Debug for Stream {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
InternalStream::Tcp(ref s) => fmt::Debug::fmt(s, fmt),
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
InternalStream::Unix(ref s) => fmt::Debug::fmt(s, fmt),
}
}
@ -93,7 +95,7 @@ impl AsRawFd for Stream {
fn as_raw_fd(&self) -> RawFd {
match self.0 {
InternalStream::Tcp(ref s) => s.as_raw_fd(),
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
InternalStream::Unix(ref s) => s.as_raw_fd(),
}
}
@ -111,7 +113,7 @@ impl AsRawSocket for Stream {
enum InternalStream {
Tcp(TcpStream),
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
Unix(UnixStream),
}
@ -119,7 +121,7 @@ impl Read for InternalStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
InternalStream::Tcp(ref mut s) => s.read(buf),
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
InternalStream::Unix(ref mut s) => s.read(buf),
}
}
@ -129,7 +131,7 @@ impl Write for InternalStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match *self {
InternalStream::Tcp(ref mut s) => s.write(buf),
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
InternalStream::Unix(ref mut s) => s.write(buf),
}
}
@ -137,7 +139,7 @@ impl Write for InternalStream {
fn flush(&mut self) -> io::Result<()> {
match *self {
InternalStream::Tcp(ref mut s) => s.flush(),
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
InternalStream::Unix(ref mut s) => s.flush(),
}
}
@ -149,7 +151,7 @@ fn open_socket(params: &ConnectParams) -> Result<InternalStream, ConnectError> {
ConnectTarget::Tcp(ref host) => {
Ok(try!(TcpStream::connect(&(&**host, port)).map(InternalStream::Tcp)))
}
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
ConnectTarget::Unix(ref path) => {
let path = path.join(&format!(".s.PGSQL.{}", port));
Ok(try!(UnixStream::connect(&path).map(InternalStream::Unix)))
@ -183,7 +185,7 @@ pub fn initialize_stream(params: &ConnectParams,
// Postgres doesn't support SSL over unix sockets
let host = match params.target {
ConnectTarget::Tcp(ref host) => host,
#[cfg(feature = "unix_socket")]
#[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))]
ConnectTarget::Unix(_) => return Err(ConnectError::Io(::bad_response())),
};

View File

@ -296,6 +296,11 @@ impl WrongTypeNew for WrongType {
/// In addition to the types listed above, `FromSql` is implemented for
/// `Option<T>` where `T` implements `FromSql`. An `Option<T>` represents a
/// nullable Postgres value.
///
/// # Arrays
///
/// `FromSql` is implemented for `Vec<T>` where `T` implements `FromSql`, and
/// corresponds to one-dimensional Postgres arrays.
pub trait FromSql: Sized {
/// Creates a new value of this type from a `Read`er of the binary format
/// of the specified Postgres `Type`.
@ -343,6 +348,46 @@ impl FromSql for bool {
accepts!(Type::Bool);
}
impl<T: FromSql> FromSql for Vec<T> {
fn from_sql<R: Read>(ty: &Type, raw: &mut R, info: &SessionInfo) -> Result<Vec<T>> {
let member_type = match *ty.kind() {
Kind::Array(ref member) => member,
_ => panic!("expected array type"),
};
if try!(raw.read_i32::<BigEndian>()) != 1 {
return Err(Error::Conversion("array contains too many dimensions".into()));
}
let _has_nulls = try!(raw.read_i32::<BigEndian>());
let _member_oid = try!(raw.read_u32::<BigEndian>());
let count = try!(raw.read_i32::<BigEndian>());
let _index_offset = try!(raw.read_i32::<BigEndian>());
let mut out = Vec::with_capacity(count as usize);
for _ in 0..count {
let len = try!(raw.read_i32::<BigEndian>());
let value = if len < 0 {
try!(T::from_sql_null(&member_type, info))
} else {
let mut raw = raw.take(len as u64);
try!(T::from_sql(&member_type, &mut raw, info))
};
out.push(value)
}
Ok(out)
}
fn accepts(ty: &Type) -> bool {
match *ty.kind() {
Kind::Array(ref inner) => T::accepts(inner),
_ => false,
}
}
}
impl FromSql for Vec<u8> {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<Vec<u8>> {
let mut buf = vec![];
@ -496,6 +541,12 @@ pub enum IsNull {
/// In addition to the types listed above, `ToSql` is implemented for
/// `Option<T>` where `T` implements `ToSql`. An `Option<T>` represents a
/// nullable Postgres value.
///
/// # Arrays
///
/// `ToSql` is implemented for `Vec<T>` and `&[T]` where `T` implements `ToSql`,
/// and corresponds to one-dimentional Postgres arrays with an index offset of
/// 0.
pub trait ToSql: fmt::Debug {
/// Converts the value of `self` into the binary format of the specified
/// Postgres `Type`, writing it to `out`.
@ -573,6 +624,48 @@ impl ToSql for bool {
accepts!(Type::Bool);
}
impl<'a, T: ToSql> ToSql for &'a [T] {
to_sql_checked!();
fn to_sql<W: Write + ?Sized>(&self, ty: &Type,
mut w: &mut W,
ctx: &SessionInfo)
-> Result<IsNull> {
let member_type = match *ty.kind() {
Kind::Array(ref member) => member,
_ => panic!("expected array type"),
};
try!(w.write_i32::<BigEndian>(1)); // number of dimensions
try!(w.write_i32::<BigEndian>(1)); // has nulls
try!(w.write_u32::<BigEndian>(member_type.oid()));
try!(w.write_i32::<BigEndian>(try!(downcast(self.len()))));
try!(w.write_i32::<BigEndian>(0)); // index offset
let mut inner_buf = vec![];
for e in *self {
match try!(e.to_sql(&member_type, &mut inner_buf, ctx)) {
IsNull::No => {
try!(w.write_i32::<BigEndian>(try!(downcast(inner_buf.len()))));
try!(w.write_all(&inner_buf));
}
IsNull::Yes => try!(w.write_i32::<BigEndian>(-1)),
}
inner_buf.clear();
}
Ok(IsNull::No)
}
fn accepts(ty: &Type) -> bool {
match *ty.kind() {
Kind::Array(ref member) => T::accepts(member),
_ => false,
}
}
}
impl<'a> ToSql for &'a [u8] {
to_sql_checked!();
@ -584,6 +677,18 @@ impl<'a> ToSql for &'a [u8] {
accepts!(Type::Bytea);
}
impl<T: ToSql> ToSql for Vec<T> {
to_sql_checked!();
fn to_sql<W: Write + ?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
<&[T] as ToSql>::to_sql(&&**self, ty, w, ctx)
}
fn accepts(ty: &Type) -> bool {
<&[T] as ToSql>::accepts(ty)
}
}
impl ToSql for Vec<u8> {
to_sql_checked!();

View File

@ -1,70 +1,21 @@
use std::io::prelude::*;
use byteorder::{WriteBytesExt, BigEndian};
use Result;
use types::{Type, ToSql, Kind, IsNull, SessionInfo, downcast};
use types::{Type, ToSql, IsNull, SessionInfo};
/// An adapter type mapping slices to Postgres arrays.
/// # Deprecated
///
/// `Slice`'s `ToSql` implementation maps the slice to a one-dimensional
/// Postgres array of the relevant type. This is particularly useful with the
/// `ANY` function to match a column against multiple values without having
/// to dynamically construct the query string.
///
/// # Examples
///
/// ```rust,no_run
/// # use postgres::{Connection, SslMode};
/// use postgres::types::Slice;
///
/// # let conn = Connection::connect("", SslMode::None).unwrap();
/// let values = &[1i32, 2, 3, 4, 5, 6];
/// let stmt = conn.prepare("SELECT * FROM foo WHERE id = ANY($1)").unwrap();
/// for row in &stmt.query(&[&Slice(values)]).unwrap() {
/// // ...
/// }
/// ```
/// `ToSql` is now implemented directly for slices.
#[derive(Debug)]
pub struct Slice<'a, T: 'a + ToSql>(pub &'a [T]);
impl<'a, T: 'a + ToSql> ToSql for Slice<'a, T> {
fn to_sql<W: Write + ?Sized>(&self,
ty: &Type,
mut w: &mut W,
ctx: &SessionInfo)
-> Result<IsNull> {
let member_type = match *ty.kind() {
Kind::Array(ref member) => member,
_ => panic!("expected array type"),
};
try!(w.write_i32::<BigEndian>(1)); // number of dimensions
try!(w.write_i32::<BigEndian>(1)); // has nulls
try!(w.write_u32::<BigEndian>(member_type.oid()));
try!(w.write_i32::<BigEndian>(try!(downcast(self.0.len()))));
try!(w.write_i32::<BigEndian>(0)); // index offset
let mut inner_buf = vec![];
for e in self.0 {
match try!(e.to_sql(&member_type, &mut inner_buf, ctx)) {
IsNull::No => {
try!(w.write_i32::<BigEndian>(try!(downcast(inner_buf.len()))));
try!(w.write_all(&inner_buf));
}
IsNull::Yes => try!(w.write_i32::<BigEndian>(-1)),
}
inner_buf.clear();
}
Ok(IsNull::No)
fn to_sql<W: Write + ?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
self.0.to_sql(ty, w, ctx)
}
fn accepts(ty: &Type) -> bool {
match *ty.kind() {
Kind::Array(ref member) => T::accepts(member),
_ => false,
}
<&[T] as ToSql>::accepts(ty)
}
to_sql_checked!();

View File

@ -75,7 +75,7 @@ fn test_connection_finish() {
}
#[test]
#[cfg_attr(not(feature = "unix_socket"), ignore)]
#[cfg_attr(not(any(feature = "unix_socket", all(unix, feature = "nightly"))), ignore)]
fn test_unix_connection() {
let conn = or_panic!(Connection::connect("postgres://postgres@localhost", SslMode::None));
let stmt = or_panic!(conn.prepare("SHOW unix_socket_directories"));

View File

@ -6,7 +6,7 @@ use std::io::{Read, Write};
use postgres::{Connection, SslMode, Result};
use postgres::error::Error;
use postgres::types::{ToSql, FromSql, Slice, WrongType, Type, IsNull, Kind, SessionInfo};
use postgres::types::{ToSql, FromSql, WrongType, Type, IsNull, Kind, SessionInfo};
#[cfg(feature = "bit-vec")]
mod bit_vec;
@ -207,7 +207,7 @@ fn test_slice() {
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(&[&Slice(&[1i32, 3, 4])]).unwrap();
let result = stmt.query(&[&&[1i32, 3, 4][..]]).unwrap();
assert_eq!(vec!["a".to_owned(), "c".to_owned(), "d".to_owned()],
result.iter().map(|r| r.get::<_, String>(0)).collect::<Vec<_>>());
}
@ -218,7 +218,7 @@ fn test_slice_wrong_type() {
conn.batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY)").unwrap();
let stmt = conn.prepare("SELECT * FROM foo WHERE id = ANY($1)").unwrap();
match stmt.query(&[&Slice(&["hi"])]) {
match stmt.query(&[&&["hi"][..]]) {
Ok(_) => panic!("Unexpected success"),
Err(Error::Conversion(ref e)) if e.is::<WrongType>() => {}
Err(e) => panic!("Unexpected error {:?}", e),
@ -230,7 +230,7 @@ fn test_slice_range() {
let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap();
let stmt = conn.prepare("SELECT $1::INT8RANGE").unwrap();
match stmt.query(&[&Slice(&[1i64])]) {
match stmt.query(&[&&[1i64][..]]) {
Ok(_) => panic!("Unexpected success"),
Err(Error::Conversion(ref e)) if e.is::<WrongType>() => {}
Err(e) => panic!("Unexpected error {:?}", e),