Merge branch 'release-v0.10.2' into release

This commit is contained in:
Steven Fackler 2015-12-02 21:58:29 -08:00
commit a402f7d3a6
23 changed files with 922 additions and 831 deletions

1
.gitattributes vendored
View File

@ -1 +0,0 @@
*.rs eol=lf

View File

@ -1,16 +0,0 @@
#!/bin/bash
set -o errexit -o nounset
git clone --branch gh-pages "https://$GH_TOKEN@github.com/${TRAVIS_REPO_SLUG}.git" deploy_docs
cd deploy_docs
git config user.name "Steven Fackler"
git config user.email "sfackler@gmail.com"
rm -rf doc/master
mv ../target/doc doc/master
git add -A .
git commit -m "rebuild pages at ${TRAVIS_COMMIT}"
git push

View File

@ -1,11 +1,11 @@
[package] [package]
name = "postgres" name = "postgres"
version = "0.10.1" version = "0.10.2"
authors = ["Steven Fackler <sfackler@gmail.com>"] authors = ["Steven Fackler <sfackler@gmail.com>"]
license = "MIT" license = "MIT"
description = "A native PostgreSQL driver" description = "A native PostgreSQL driver"
repository = "https://github.com/sfackler/rust-postgres" repository = "https://github.com/sfackler/rust-postgres"
documentation = "https://sfackler.github.io/rust-postgres/doc/v0.10.1/postgres" documentation = "https://sfackler.github.io/rust-postgres/doc/v0.10.2/postgres"
readme = "README.md" readme = "README.md"
keywords = ["database", "sql"] keywords = ["database", "sql"]
build = "build.rs" build = "build.rs"
@ -31,7 +31,7 @@ phf = "0.7"
rustc-serialize = "0.3" rustc-serialize = "0.3"
net2 = { version = "0.2", features = ["nightly"] } net2 = { version = "0.2", features = ["nightly"] }
chrono = { version = "0.2.14", optional = true } chrono = { version = "0.2.14", optional = true }
openssl = { version = "0.6.4", optional = true } openssl = { version = ">= 0.6.4, < 0.8", optional = true }
serde = { version = "0.3", optional = true } # Delete for 0.11 serde = { version = "0.3", optional = true } # Delete for 0.11
serde_json = { version = "0.6", optional = true } serde_json = { version = "0.6", optional = true }
time = { version = "0.1.14", optional = true } time = { version = "0.1.14", optional = true }

View File

@ -1,7 +1,7 @@
# Rust-Postgres # Rust-Postgres
A native PostgreSQL driver for Rust. A native PostgreSQL driver for Rust.
[Documentation](https://sfackler.github.io/rust-postgres/doc/v0.10.1/postgres) [Documentation](https://sfackler.github.io/rust-postgres/doc/v0.10.2/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) [![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)
@ -198,16 +198,16 @@ types. The driver currently supports the following conversions:
</tr> </tr>
<tr> <tr>
<td> <td>
<a href="https://github.com/rust-lang/rustc-serialize">serialize::json::Json</a> <a href="https://github.com/rust-lang-nursery/rustc-serialize">serialize::json::Json</a>
and and
<a href="https://github.com/erickt/serde_json">serde_json::Value</a> <a href="https://github.com/serde-rs/json">serde_json::Value</a>
(<a href="#optional-features">optional</a>) (<a href="#optional-features">optional</a>)
</td> </td>
<td>JSON, JSONB</td> <td>JSON, JSONB</td>
</tr> </tr>
<tr> <tr>
<td> <td>
<a href="https://github.com/rust-lang/time">time::Timespec</a> <a href="https://github.com/rust-lang-deprecated/time">time::Timespec</a>
and and
<a href="https://github.com/lifthrasiir/rust-chrono">chrono::NaiveDateTime</a> <a href="https://github.com/lifthrasiir/rust-chrono">chrono::NaiveDateTime</a>
(<a href="#optional-features">optional</a>) (<a href="#optional-features">optional</a>)
@ -216,7 +216,7 @@ types. The driver currently supports the following conversions:
</tr> </tr>
<tr> <tr>
<td> <td>
<a href="https://github.com/rust-lang/time">time::Timespec</a>, <a href="https://github.com/rust-lang-deprecated/time">time::Timespec</a>,
<a href="https://github.com/lifthrasiir/rust-chrono">chrono::DateTime&lt;UTC&gt;</a>, <a href="https://github.com/lifthrasiir/rust-chrono">chrono::DateTime&lt;UTC&gt;</a>,
<a href="https://github.com/lifthrasiir/rust-chrono">chrono::DateTime&lt;Local&gt;</a>, <a href="https://github.com/lifthrasiir/rust-chrono">chrono::DateTime&lt;Local&gt;</a>,
and and
@ -241,7 +241,7 @@ types. The driver currently supports the following conversions:
</tr> </tr>
<tr> <tr>
<td> <td>
<a href="https://github.com/rust-lang/uuid">uuid::Uuid</a> <a href="https://github.com/rust-lang-nursery/uuid">uuid::Uuid</a>
(<a href="#optional-features">optional</a>) (<a href="#optional-features">optional</a>)
</td> </td>
<td>UUID</td> <td>UUID</td>
@ -284,8 +284,9 @@ implementations for `uuid`'s `Uuid` type.
[JSON and JSONB](http://www.postgresql.org/docs/9.4/static/datatype-json.html) [JSON and JSONB](http://www.postgresql.org/docs/9.4/static/datatype-json.html)
support is provided optionally by the `rustc-serialize` feature, which adds support is provided optionally by the `rustc-serialize` feature, which adds
`ToSql` and `FromSql` implementations for `rustc-serialize`'s `Json` type, and `ToSql` and `FromSql` implementations for `rustc-serialize`'s `Json` type, and
the `serde` feature, which adds implementations for `serde_json`'s `Value` the `serde_json` feature, which adds implementations for `serde_json`'s `Value`
type. type. The `serde` feature provides implementations for the older
`serde::json::Value` type.
### TIMESTAMP/TIMESTAMPTZ/DATE/TIME types ### TIMESTAMP/TIMESTAMPTZ/DATE/TIME types

View File

@ -48,10 +48,10 @@ impl DbErrorNew for DbError {
None => match map.remove(&b'p') { None => match map.remove(&b'p') {
Some(pos) => Some(ErrorPosition::Internal { Some(pos) => Some(ErrorPosition::Internal {
position: try!(pos.parse().map_err(|_| ())), position: try!(pos.parse().map_err(|_| ())),
query: try!(map.remove(&b'q').ok_or(())) query: try!(map.remove(&b'q').ok_or(())),
}), }),
None => None None => None,
} },
}, },
where_: map.remove(&b'W'), where_: map.remove(&b'W'),
schema: map.remove(&b's'), schema: map.remove(&b's'),
@ -194,7 +194,7 @@ impl error::Error for DbError {
#[derive(Debug)] #[derive(Debug)]
pub enum ConnectError { pub enum ConnectError {
/// An error creating `ConnectParams`. /// An error creating `ConnectParams`.
BadConnectParams(Box<error::Error+Sync+Send>), BadConnectParams(Box<error::Error + Sync + Send>),
/// The `ConnectParams` was missing a user. /// The `ConnectParams` was missing a user.
MissingUser, MissingUser,
/// An error from the Postgres server itself. /// An error from the Postgres server itself.
@ -207,7 +207,7 @@ pub enum ConnectError {
/// The Postgres server does not support SSL encryption. /// The Postgres server does not support SSL encryption.
NoSslSupport, NoSslSupport,
/// An error initializing the SSL session. /// An error initializing the SSL session.
SslError(Box<error::Error+Sync+Send>), SslError(Box<error::Error + Sync + Send>),
/// An error communicating with the server. /// An error communicating with the server.
IoError(io::Error), IoError(io::Error),
} }
@ -220,7 +220,7 @@ impl fmt::Display for ConnectError {
ConnectError::DbError(ref err) => write!(fmt, ": {}", err), ConnectError::DbError(ref err) => write!(fmt, ": {}", err),
ConnectError::SslError(ref err) => write!(fmt, ": {}", err), ConnectError::SslError(ref err) => write!(fmt, ": {}", err),
ConnectError::IoError(ref err) => write!(fmt, ": {}", err), ConnectError::IoError(ref err) => write!(fmt, ": {}", err),
_ => Ok(()) _ => Ok(()),
} }
} }
} }
@ -231,7 +231,8 @@ impl error::Error for ConnectError {
ConnectError::BadConnectParams(_) => "Error creating `ConnectParams`", ConnectError::BadConnectParams(_) => "Error creating `ConnectParams`",
ConnectError::MissingUser => "User missing in `ConnectParams`", ConnectError::MissingUser => "User missing in `ConnectParams`",
ConnectError::DbError(_) => "Error reported by Postgres", ConnectError::DbError(_) => "Error reported by Postgres",
ConnectError::MissingPassword => "The server requested a password but none was provided", ConnectError::MissingPassword =>
"The server requested a password but none was provided",
ConnectError::UnsupportedAuthentication => { ConnectError::UnsupportedAuthentication => {
"The server requested an unsupported authentication method" "The server requested an unsupported authentication method"
} }
@ -247,7 +248,7 @@ impl error::Error for ConnectError {
ConnectError::DbError(ref err) => Some(&**err), ConnectError::DbError(ref err) => Some(&**err),
ConnectError::SslError(ref err) => Some(&**err), ConnectError::SslError(ref err) => Some(&**err),
ConnectError::IoError(ref err) => Some(err), ConnectError::IoError(ref err) => Some(err),
_ => None _ => None,
} }
} }
} }
@ -280,8 +281,8 @@ pub enum ErrorPosition {
/// The byte position. /// The byte position.
position: u32, position: u32,
/// A query generated by the Postgres server. /// A query generated by the Postgres server.
query: String query: String,
} },
} }
/// An error encountered when communicating with the Postgres server. /// An error encountered when communicating with the Postgres server.
@ -297,7 +298,7 @@ pub enum Error {
/// An attempt was made to read from a column that does not exist. /// An attempt was made to read from a column that does not exist.
InvalidColumn, InvalidColumn,
/// An error converting between Postgres and Rust types. /// An error converting between Postgres and Rust types.
Conversion(Box<error::Error+Sync+Send>), Conversion(Box<error::Error + Sync + Send>),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -329,7 +330,7 @@ impl error::Error for Error {
Error::DbError(ref err) => Some(&**err), Error::DbError(ref err) => Some(&**err),
Error::IoError(ref err) => Some(err), Error::IoError(ref err) => Some(err),
Error::Conversion(ref err) => Some(&**err), Error::Conversion(ref err) => Some(&**err),
_ => None _ => None,
} }
} }
} }

View File

@ -26,6 +26,8 @@ pub trait NegotiateSsl {
/// ///
/// The host portion of the connection parameters is provided for hostname /// The host portion of the connection parameters is provided for hostname
/// verification. /// verification.
fn negotiate_ssl(&self, host: &str, stream: Stream) fn negotiate_ssl(&self,
-> Result<Box<StreamWrapper>, Box<Error+Sync+Send>>; host: &str,
stream: Stream)
-> Result<Box<StreamWrapper>, Box<Error + Sync + Send>>;
} }

View File

@ -16,8 +16,10 @@ impl StreamWrapper for SslStream<Stream> {
} }
impl NegotiateSsl for SslContext { impl NegotiateSsl for SslContext {
fn negotiate_ssl(&self, _: &str, stream: Stream) fn negotiate_ssl(&self,
-> Result<Box<StreamWrapper>, Box<Error+Send+Sync>> { _: &str,
stream: Stream)
-> Result<Box<StreamWrapper>, Box<Error + Send + Sync>> {
let stream = try!(SslStream::connect(self, stream)); let stream = try!(SslStream::connect(self, stream));
Ok(Box::new(stream)) Ok(Box::new(stream))
} }

View File

@ -41,7 +41,7 @@
//! } //! }
//! } //! }
//! ``` //! ```
#![doc(html_root_url="https://sfackler.github.io/rust-postgres/doc/v0.10.1")] #![doc(html_root_url="https://sfackler.github.io/rust-postgres/doc/v0.10.2")]
#![warn(missing_docs)] #![warn(missing_docs)]
extern crate bufstream; extern crate bufstream;
@ -114,7 +114,7 @@ pub enum ConnectTarget {
/// ///
/// Only available on Unix platforms with the `unix_socket` feature. /// Only available on Unix platforms with the `unix_socket` feature.
#[cfg(feature = "unix_socket")] #[cfg(feature = "unix_socket")]
Unix(PathBuf) Unix(PathBuf),
} }
/// Authentication information. /// Authentication information.
@ -148,17 +148,17 @@ pub struct ConnectParams {
/// A trait implemented by types that can be converted into a `ConnectParams`. /// A trait implemented by types that can be converted into a `ConnectParams`.
pub trait IntoConnectParams { pub trait IntoConnectParams {
/// Converts the value of `self` into a `ConnectParams`. /// Converts the value of `self` into a `ConnectParams`.
fn into_connect_params(self) -> result::Result<ConnectParams, Box<StdError+StdSync+Send>>; fn into_connect_params(self) -> result::Result<ConnectParams, Box<StdError + StdSync + Send>>;
} }
impl IntoConnectParams for ConnectParams { impl IntoConnectParams for ConnectParams {
fn into_connect_params(self) -> result::Result<ConnectParams, Box<StdError+StdSync+Send>> { fn into_connect_params(self) -> result::Result<ConnectParams, Box<StdError + StdSync + Send>> {
Ok(self) Ok(self)
} }
} }
impl<'a> IntoConnectParams for &'a str { impl<'a> IntoConnectParams for &'a str {
fn into_connect_params(self) -> result::Result<ConnectParams, Box<StdError+StdSync+Send>> { fn into_connect_params(self) -> result::Result<ConnectParams, Box<StdError + StdSync + Send>> {
match Url::parse(self) { match Url::parse(self) {
Ok(url) => url.into_connect_params(), Ok(url) => url.into_connect_params(),
Err(err) => return Err(err.into()), Err(err) => return Err(err.into()),
@ -167,7 +167,7 @@ impl<'a> IntoConnectParams for &'a str {
} }
impl IntoConnectParams for Url { impl IntoConnectParams for Url {
fn into_connect_params(self) -> result::Result<ConnectParams, Box<StdError+StdSync+Send>> { fn into_connect_params(self) -> result::Result<ConnectParams, Box<StdError + StdSync + Send>> {
let Url { let Url {
host, host,
port, port,
@ -178,11 +178,11 @@ impl IntoConnectParams for Url {
#[cfg(feature = "unix_socket")] #[cfg(feature = "unix_socket")]
fn make_unix(maybe_path: String) fn make_unix(maybe_path: String)
-> result::Result<ConnectTarget, Box<StdError+StdSync+Send>> { -> result::Result<ConnectTarget, Box<StdError + StdSync + Send>> {
Ok(ConnectTarget::Unix(PathBuf::from(maybe_path))) Ok(ConnectTarget::Unix(PathBuf::from(maybe_path)))
} }
#[cfg(not(feature = "unix_socket"))] #[cfg(not(feature = "unix_socket"))]
fn make_unix(_: String) -> result::Result<ConnectTarget, Box<StdError+StdSync+Send>> { 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` feature".into())
} }
@ -194,7 +194,10 @@ impl IntoConnectParams for Url {
}; };
let user = user.map(|url::UserInfo { user, pass }| { let user = user.map(|url::UserInfo { user, pass }| {
UserInfo { user: user, password: pass } UserInfo {
user: user,
password: pass,
}
}); });
let database = if path.is_empty() { let database = if path.is_empty() {
@ -254,7 +257,7 @@ pub struct CancelData {
/// Only the host and port of the connection info are used. See /// Only the host and port of the connection info are used. See
/// `Connection::connect` for details of the `params` argument. /// `Connection::connect` for details of the `params` argument.
/// ///
/// ## Example /// # Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use postgres::{Connection, SslMode}; /// # use postgres::{Connection, SslMode};
@ -265,19 +268,21 @@ pub struct CancelData {
/// thread::spawn(move || { /// thread::spawn(move || {
/// conn.execute("SOME EXPENSIVE QUERY", &[]).unwrap(); /// conn.execute("SOME EXPENSIVE QUERY", &[]).unwrap();
/// }); /// });
/// # let _ = /// postgres::cancel_query(url, &SslMode::None, cancel_data).unwrap();
/// postgres::cancel_query(url, &SslMode::None, cancel_data);
/// ``` /// ```
pub fn cancel_query<T>(params: T, ssl: &SslMode, data: CancelData) pub fn cancel_query<T>(params: T,
-> result::Result<(), ConnectError> ssl: &SslMode,
where T: IntoConnectParams { data: CancelData)
-> result::Result<(), ConnectError>
where T: IntoConnectParams
{
let params = try!(params.into_connect_params().map_err(ConnectError::BadConnectParams)); let params = try!(params.into_connect_params().map_err(ConnectError::BadConnectParams));
let mut socket = try!(priv_io::initialize_stream(&params, ssl)); let mut socket = try!(priv_io::initialize_stream(&params, ssl));
try!(socket.write_message(&CancelRequest { try!(socket.write_message(&CancelRequest {
code: message::CANCEL_CODE, code: message::CANCEL_CODE,
process_id: data.process_id, process_id: data.process_id,
secret_key: data.secret_key secret_key: data.secret_key,
})); }));
try!(socket.flush()); try!(socket.flush());
@ -291,7 +296,8 @@ fn bad_response() -> std_io::Error {
fn desynchronized() -> std_io::Error { fn desynchronized() -> std_io::Error {
std_io::Error::new(std_io::ErrorKind::Other, std_io::Error::new(std_io::ErrorKind::Other,
"communication with the server has desynchronized due to an earlier IO error") "communication with the server has desynchronized due to an earlier IO \
error")
} }
/// An enumeration of transaction isolation levels. /// An enumeration of transaction isolation levels.
@ -353,9 +359,9 @@ pub enum SslMode {
/// The connection will not use SSL. /// The connection will not use SSL.
None, None,
/// The connection will use SSL if the backend supports it. /// The connection will use SSL if the backend supports it.
Prefer(Box<NegotiateSsl+std::marker::Sync+Send>), Prefer(Box<NegotiateSsl + std::marker::Sync + Send>),
/// The connection must use SSL. /// The connection must use SSL.
Require(Box<NegotiateSsl+std::marker::Sync+Send>), Require(Box<NegotiateSsl + std::marker::Sync + Send>),
} }
impl fmt::Debug for SslMode { impl fmt::Debug for SslMode {
@ -399,7 +405,8 @@ impl Drop for InnerConnection {
impl InnerConnection { impl InnerConnection {
fn connect<T>(params: T, ssl: &SslMode) -> result::Result<InnerConnection, ConnectError> fn connect<T>(params: T, ssl: &SslMode) -> result::Result<InnerConnection, ConnectError>
where T: IntoConnectParams { where T: IntoConnectParams
{
let params = try!(params.into_connect_params().map_err(ConnectError::BadConnectParams)); let params = try!(params.into_connect_params().map_err(ConnectError::BadConnectParams));
let stream = try!(priv_io::initialize_stream(&params, ssl)); let stream = try!(priv_io::initialize_stream(&params, ssl));
@ -412,7 +419,10 @@ impl InnerConnection {
next_stmt_id: 0, next_stmt_id: 0,
notice_handler: Box::new(LoggingNoticeHandler), notice_handler: Box::new(LoggingNoticeHandler),
notifications: VecDeque::new(), notifications: VecDeque::new(),
cancel_data: CancelData { process_id: 0, secret_key: 0 }, cancel_data: CancelData {
process_id: 0,
secret_key: 0,
},
unknown_types: HashMap::new(), unknown_types: HashMap::new(),
cached_statements: HashMap::new(), cached_statements: HashMap::new(),
parameters: HashMap::new(), parameters: HashMap::new(),
@ -432,9 +442,9 @@ impl InnerConnection {
} }
try!(conn.write_messages(&[StartupMessage { try!(conn.write_messages(&[StartupMessage {
version: message::PROTOCOL_VERSION, version: message::PROTOCOL_VERSION,
parameters: &options parameters: &options,
}])); }]));
try!(conn.handle_auth(user)); try!(conn.handle_auth(user));
@ -455,21 +465,22 @@ impl InnerConnection {
Ok(conn) Ok(conn)
} }
#[cfg_attr(rustfmt, rustfmt_skip)]
fn setup_typeinfo_query(&mut self) -> result::Result<(), ConnectError> { fn setup_typeinfo_query(&mut self) -> result::Result<(), ConnectError> {
match self.raw_prepare(TYPEINFO_QUERY, match self.raw_prepare(TYPEINFO_QUERY,
"SELECT t.typname, t.typelem, r.rngsubtype, n.nspname \ "SELECT t.typname, t.typelem, r.rngsubtype, n.nspname \
FROM pg_catalog.pg_type t \ FROM pg_catalog.pg_type t \
LEFT OUTER JOIN pg_catalog.pg_range r \ LEFT OUTER JOIN pg_catalog.pg_range r ON \
ON r.rngtypid = t.oid \ r.rngtypid = t.oid \
INNER JOIN pg_catalog.pg_namespace n \ INNER JOIN pg_catalog.pg_namespace n ON \
ON t.typnamespace = n.oid \ t.typnamespace = n.oid \
WHERE t.oid = $1") { WHERE t.oid = $1") {
Ok(..) => return Ok(()), Ok(..) => return Ok(()),
Err(Error::IoError(e)) => return Err(ConnectError::IoError(e)), Err(Error::IoError(e)) => return Err(ConnectError::IoError(e)),
// Range types weren't added until Postgres 9.2, so pg_range may not exist // Range types weren't added until Postgres 9.2, so pg_range may not exist
Err(Error::DbError(ref e)) if e.code() == &SqlState::UndefinedTable => {} Err(Error::DbError(ref e)) if e.code() == &SqlState::UndefinedTable => {}
Err(Error::DbError(e)) => return Err(ConnectError::DbError(e)), Err(Error::DbError(e)) => return Err(ConnectError::DbError(e)),
_ => unreachable!() _ => unreachable!(),
} }
match self.raw_prepare(TYPEINFO_QUERY, match self.raw_prepare(TYPEINFO_QUERY,
@ -481,7 +492,7 @@ impl InnerConnection {
Ok(..) => Ok(()), Ok(..) => Ok(()),
Err(Error::IoError(e)) => Err(ConnectError::IoError(e)), Err(Error::IoError(e)) => Err(ConnectError::IoError(e)),
Err(Error::DbError(e)) => Err(ConnectError::DbError(e)), Err(Error::DbError(e)) => Err(ConnectError::DbError(e)),
_ => unreachable!() _ => unreachable!(),
} }
} }
@ -505,12 +516,13 @@ impl InnerConnection {
ParameterStatus { parameter, value } => { ParameterStatus { parameter, value } => {
self.parameters.insert(parameter, value); self.parameters.insert(parameter, value);
} }
val => return Ok(val) val => return Ok(val),
} }
} }
} }
fn read_message_with_notification_timeout(&mut self, timeout: Duration) fn read_message_with_notification_timeout(&mut self,
timeout: Duration)
-> std::io::Result<Option<BackendMessage>> { -> std::io::Result<Option<BackendMessage>> {
debug_assert!(!self.desynchronized); debug_assert!(!self.desynchronized);
loop { loop {
@ -523,7 +535,7 @@ impl InnerConnection {
Some(ParameterStatus { parameter, value }) => { Some(ParameterStatus { parameter, value }) => {
self.parameters.insert(parameter, value); self.parameters.insert(parameter, value);
} }
val => return Ok(val) val => return Ok(val),
} }
} }
} }
@ -535,10 +547,10 @@ impl InnerConnection {
self.notifications.push_back(Notification { self.notifications.push_back(Notification {
pid: pid, pid: pid,
channel: channel, channel: channel,
payload: payload payload: payload,
}) })
} }
val => return Ok(val) val => return Ok(val),
} }
} }
} }
@ -548,9 +560,7 @@ impl InnerConnection {
AuthenticationOk => return Ok(()), AuthenticationOk => return Ok(()),
AuthenticationCleartextPassword => { AuthenticationCleartextPassword => {
let pass = try!(user.password.ok_or(ConnectError::MissingPassword)); let pass = try!(user.password.ok_or(ConnectError::MissingPassword));
try!(self.write_messages(&[PasswordMessage { try!(self.write_messages(&[PasswordMessage { password: &pass }]));
password: &pass,
}]));
} }
AuthenticationMD5Password { salt } => { AuthenticationMD5Password { salt } => {
let pass = try!(user.password.ok_or(ConnectError::MissingPassword)); let pass = try!(user.password.ok_or(ConnectError::MissingPassword));
@ -562,22 +572,20 @@ impl InnerConnection {
let _ = hasher.input(output.as_bytes()); let _ = hasher.input(output.as_bytes());
let _ = hasher.input(&salt); let _ = hasher.input(&salt);
let output = format!("md5{}", hasher.result_str()); let output = format!("md5{}", hasher.result_str());
try!(self.write_messages(&[PasswordMessage { try!(self.write_messages(&[PasswordMessage { password: &output }]));
password: &output
}]));
} }
AuthenticationKerberosV5 AuthenticationKerberosV5 |
| AuthenticationSCMCredential AuthenticationSCMCredential |
| AuthenticationGSS AuthenticationGSS |
| AuthenticationSSPI => return Err(ConnectError::UnsupportedAuthentication), AuthenticationSSPI => return Err(ConnectError::UnsupportedAuthentication),
ErrorResponse { fields } => return DbError::new_connect(fields), ErrorResponse { fields } => return DbError::new_connect(fields),
_ => return Err(ConnectError::IoError(bad_response())) _ => return Err(ConnectError::IoError(bad_response())),
} }
match try!(self.read_message()) { match try!(self.read_message()) {
AuthenticationOk => Ok(()), AuthenticationOk => Ok(()),
ErrorResponse { fields } => return DbError::new_connect(fields), ErrorResponse { fields } => return DbError::new_connect(fields),
_ => return Err(ConnectError::IoError(bad_response())) _ => return Err(ConnectError::IoError(bad_response())),
} }
} }
@ -588,17 +596,16 @@ impl InnerConnection {
fn raw_prepare(&mut self, stmt_name: &str, query: &str) -> Result<(Vec<Type>, Vec<Column>)> { fn raw_prepare(&mut self, stmt_name: &str, query: &str) -> Result<(Vec<Type>, Vec<Column>)> {
debug!("preparing query with name `{}`: {}", stmt_name, query); debug!("preparing query with name `{}`: {}", stmt_name, query);
try!(self.write_messages(&[ try!(self.write_messages(&[Parse {
Parse { name: stmt_name,
name: stmt_name, query: query,
query: query, param_types: &[],
param_types: &[] },
}, Describe {
Describe { variant: b'S',
variant: b'S', name: stmt_name,
name: stmt_name, },
}, Sync]));
Sync]));
match try!(self.read_message()) { match try!(self.read_message()) {
ParseComplete => {} ParseComplete => {}
@ -617,7 +624,7 @@ impl InnerConnection {
let raw_columns = match try!(self.read_message()) { let raw_columns = match try!(self.read_message()) {
RowDescription { descriptions } => descriptions, RowDescription { descriptions } => descriptions,
NoData => vec![], NoData => vec![],
_ => bad_response!(self) _ => bad_response!(self),
}; };
try!(self.wait_for_ready()); try!(self.wait_for_ready());
@ -669,16 +676,15 @@ impl InnerConnection {
} }
fn close_statement(&mut self, name: &str, type_: u8) -> Result<()> { fn close_statement(&mut self, name: &str, type_: u8) -> Result<()> {
try!(self.write_messages(&[ try!(self.write_messages(&[Close {
Close { variant: type_,
variant: type_, name: name,
name: name, },
}, Sync]));
Sync]));
let resp = match try!(self.read_message()) { let resp = match try!(self.read_message()) {
CloseComplete => Ok(()), CloseComplete => Ok(()),
ErrorResponse { fields } => DbError::new(fields), ErrorResponse { fields } => DbError::new(fields),
_ => bad_response!(self) _ => bad_response!(self),
}; };
try!(self.wait_for_ready()); try!(self.wait_for_ready());
resp resp
@ -699,57 +705,56 @@ impl InnerConnection {
IsNull::Yes => None, IsNull::Yes => None,
IsNull::No => Some(buf), IsNull::No => Some(buf),
}; };
try!(self.write_messages(&[ try!(self.write_messages(&[Bind {
Bind { portal: "",
portal: "", statement: TYPEINFO_QUERY,
statement: TYPEINFO_QUERY, formats: &[1],
formats: &[1], values: &[value],
values: &[value], result_formats: &[1],
result_formats: &[1] },
}, Execute {
Execute { portal: "",
portal: "", max_rows: 0,
max_rows: 0, },
}, Sync]));
Sync]));
match try!(self.read_message()) { match try!(self.read_message()) {
BindComplete => {} BindComplete => {}
ErrorResponse { fields } => { ErrorResponse { fields } => {
try!(self.wait_for_ready()); try!(self.wait_for_ready());
return DbError::new(fields); return DbError::new(fields);
} }
_ => bad_response!(self) _ => bad_response!(self),
} }
let (name, elem_oid, rngsubtype, schema): (String, Oid, Option<Oid>, String) = let (name, elem_oid, rngsubtype, schema): (String, Oid, Option<Oid>, String) =
match try!(self.read_message()) { match try!(self.read_message()) {
DataRow { row } => { DataRow { row } => {
let ctx = SessionInfo::new(self); let ctx = SessionInfo::new(self);
(try!(FromSql::from_sql_nullable(&Type::Name, (try!(FromSql::from_sql_nullable(&Type::Name,
row[0].as_ref().map(|r| &**r).as_mut(), row[0].as_ref().map(|r| &**r).as_mut(),
&ctx)), &ctx)),
try!(FromSql::from_sql_nullable(&Type::Oid, try!(FromSql::from_sql_nullable(&Type::Oid,
row[1].as_ref().map(|r| &**r).as_mut(), row[1].as_ref().map(|r| &**r).as_mut(),
&ctx)), &ctx)),
try!(FromSql::from_sql_nullable(&Type::Oid, try!(FromSql::from_sql_nullable(&Type::Oid,
row[2].as_ref().map(|r| &**r).as_mut(), row[2].as_ref().map(|r| &**r).as_mut(),
&ctx)), &ctx)),
try!(FromSql::from_sql_nullable(&Type::Name, try!(FromSql::from_sql_nullable(&Type::Name,
row[3].as_ref().map(|r| &**r).as_mut(), row[3].as_ref().map(|r| &**r).as_mut(),
&ctx))) &ctx)))
} }
ErrorResponse { fields } => { ErrorResponse { fields } => {
try!(self.wait_for_ready()); try!(self.wait_for_ready());
return DbError::new(fields); return DbError::new(fields);
} }
_ => bad_response!(self) _ => bad_response!(self),
}; };
match try!(self.read_message()) { match try!(self.read_message()) {
CommandComplete { .. } => {} CommandComplete { .. } => {}
ErrorResponse { fields } => { ErrorResponse { fields } => {
try!(self.wait_for_ready()); try!(self.wait_for_ready());
return DbError::new(fields); return DbError::new(fields);
} }
_ => bad_response!(self) _ => bad_response!(self),
} }
try!(self.wait_for_ready()); try!(self.wait_for_ready());
@ -758,7 +763,7 @@ impl InnerConnection {
} else { } else {
match rngsubtype { match rngsubtype {
Some(oid) => Kind::Range(try!(self.get_type(oid))), Some(oid) => Kind::Range(try!(self.get_type(oid))),
None => Kind::Simple None => Kind::Simple,
} }
}; };
@ -774,7 +779,7 @@ impl InnerConnection {
fn wait_for_ready(&mut self) -> Result<()> { fn wait_for_ready(&mut self) -> Result<()> {
match try!(self.read_message()) { match try!(self.read_message()) {
ReadyForQuery { .. } => Ok(()), ReadyForQuery { .. } => Ok(()),
_ => bad_response!(self) _ => bad_response!(self),
} }
} }
@ -788,16 +793,19 @@ impl InnerConnection {
match try!(self.read_message()) { match try!(self.read_message()) {
ReadyForQuery { .. } => break, ReadyForQuery { .. } => break,
DataRow { row } => { DataRow { row } => {
result.push(row.into_iter().map(|opt| { result.push(row.into_iter()
opt.map(|b| String::from_utf8_lossy(&b).into_owned()) .map(|opt| {
}).collect()); opt.map(|b| String::from_utf8_lossy(&b).into_owned())
})
.collect());
} }
CopyInResponse { .. } => { CopyInResponse { .. } |
try!(self.write_messages(&[ CopyOutResponse { .. } => {
CopyFail { try!(self.write_messages(&[CopyFail {
message: "COPY queries cannot be directly executed", message: "COPY queries cannot be directly \
}, executed",
Sync])); },
Sync]));
} }
ErrorResponse { fields } => { ErrorResponse { fields } => {
try!(self.wait_for_ready()); try!(self.wait_for_ready());
@ -817,25 +825,26 @@ impl InnerConnection {
} }
fn _ensure_send() { fn _ensure_send() {
fn _is_send<T: Send>() {} fn _is_send<T: Send>() {
}
_is_send::<Connection>(); _is_send::<Connection>();
} }
/// A connection to a Postgres database. /// A connection to a Postgres database.
pub struct Connection { pub struct Connection {
conn: RefCell<InnerConnection> conn: RefCell<InnerConnection>,
} }
impl fmt::Debug for Connection { impl fmt::Debug for Connection {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let conn = self.conn.borrow(); let conn = self.conn.borrow();
fmt.debug_struct("Connection") fmt.debug_struct("Connection")
.field("cancel_data", &conn.cancel_data) .field("cancel_data", &conn.cancel_data)
.field("notifications", &conn.notifications.len()) .field("notifications", &conn.notifications.len())
.field("transaction_depth", &conn.trans_depth) .field("transaction_depth", &conn.trans_depth)
.field("desynchronized", &conn.desynchronized) .field("desynchronized", &conn.desynchronized)
.field("cached_statements", &conn.cached_statements.len()) .field("cached_statements", &conn.cached_statements.len())
.finish() .finish()
} }
} }
@ -860,28 +869,27 @@ impl Connection {
/// struct should be created manually and passed in. Note that Postgres /// struct should be created manually and passed in. Note that Postgres
/// does not support SSL over Unix sockets. /// does not support SSL over Unix sockets.
/// ///
/// ## Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use postgres::{Connection, SslMode}; /// use postgres::{Connection, SslMode};
/// # fn f() -> Result<(), ::postgres::error::ConnectError> { ///
/// let url = "postgresql://postgres:hunter2@localhost:2994/foodb"; /// let url = "postgresql://postgres:hunter2@localhost:2994/foodb";
/// let conn = try!(Connection::connect(url, &SslMode::None)); /// let conn = Connection::connect(url, &SslMode::None).unwrap();
/// # Ok(()) };
/// ``` /// ```
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use postgres::{Connection, SslMode}; /// use postgres::{Connection, SslMode};
/// # fn f() -> Result<(), ::postgres::error::ConnectError> { ///
/// let url = "postgresql://postgres@%2Frun%2Fpostgres"; /// let url = "postgresql://postgres@%2Frun%2Fpostgres";
/// let conn = try!(Connection::connect(url, &SslMode::None)); /// let conn = Connection::connect(url, &SslMode::None).unwrap();
/// # Ok(()) };
/// ``` /// ```
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use postgres::{Connection, UserInfo, ConnectParams, SslMode, ConnectTarget}; /// use postgres::{Connection, UserInfo, ConnectParams, SslMode, ConnectTarget};
///
/// # #[cfg(feature = "unix_socket")] /// # #[cfg(feature = "unix_socket")]
/// # fn f() -> Result<(), ::postgres::error::ConnectError> { /// # fn f() {
/// # let some_crazy_path = Path::new(""); /// # let some_crazy_path = Path::new("");
/// let params = ConnectParams { /// let params = ConnectParams {
/// target: ConnectTarget::Unix(some_crazy_path), /// target: ConnectTarget::Unix(some_crazy_path),
@ -893,14 +901,13 @@ impl Connection {
/// database: None, /// database: None,
/// options: vec![], /// options: vec![],
/// }; /// };
/// let conn = try!(Connection::connect(params, &SslMode::None)); /// let conn = Connection::connect(params, &SslMode::None).unwrap();
/// # Ok(()) }; /// # }
/// ``` /// ```
pub fn connect<T>(params: T, ssl: &SslMode) -> result::Result<Connection, ConnectError> pub fn connect<T>(params: T, ssl: &SslMode) -> result::Result<Connection, ConnectError>
where T: IntoConnectParams { where T: IntoConnectParams
InnerConnection::connect(params, ssl).map(|conn| { {
Connection { conn: RefCell::new(conn) } InnerConnection::connect(params, ssl).map(|conn| Connection { conn: RefCell::new(conn) })
})
} }
/// Sets the notice handler for the connection, returning the old handler. /// Sets the notice handler for the connection, returning the old handler.
@ -924,16 +931,18 @@ impl Connection {
/// The statement is associated with the connection that created it and may /// The statement is associated with the connection that created it and may
/// not outlive that connection. /// not outlive that connection.
/// ///
/// ## Example /// # Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use postgres::{Connection, SslMode}; /// # use postgres::{Connection, SslMode};
/// # let x = 10i32;
/// # let conn = Connection::connect("", &SslMode::None).unwrap(); /// # let conn = Connection::connect("", &SslMode::None).unwrap();
/// let maybe_stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1"); /// let stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1").unwrap();
/// let stmt = match maybe_stmt { /// for row in stmt.query(&[&x]).unwrap() {
/// Ok(stmt) => stmt, /// let foo: String = row.get(0);
/// Err(err) => panic!("Error preparing statement: {:?}", err) /// println!("foo: {}", foo);
/// }; /// }
/// ```
pub fn prepare<'a>(&'a self, query: &str) -> Result<Statement<'a>> { pub fn prepare<'a>(&'a self, query: &str) -> Result<Statement<'a>> {
self.conn.borrow_mut().prepare(query, self) self.conn.borrow_mut().prepare(query, self)
} }
@ -945,18 +954,17 @@ impl Connection {
/// is going to be used frequently, caching it can improve performance by /// is going to be used frequently, caching it can improve performance by
/// reducing the number of round trips to the Postgres backend. /// reducing the number of round trips to the Postgres backend.
/// ///
/// ## Example /// # Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use postgres::{Connection, SslMode}; /// # use postgres::{Connection, SslMode};
/// # fn f() -> postgres::Result<()> {
/// # let x = 10i32; /// # let x = 10i32;
/// # let conn = Connection::connect("", &SslMode::None).unwrap(); /// # let conn = Connection::connect("", &SslMode::None).unwrap();
/// let stmt = try!(conn.prepare_cached("SELECT foo FROM bar WHERE baz = $1")); /// let stmt = conn.prepare_cached("SELECT foo FROM bar WHERE baz = $1").unwrap();
/// for row in try!(stmt.query(&[&x])) { /// for row in stmt.query(&[&x]).unwrap() {
/// println!("foo: {}", row.get::<_, String>(0)); /// let foo: String = row.get(0);
/// println!("foo: {}", foo);
/// } /// }
/// # Ok(()) };
/// ``` /// ```
pub fn prepare_cached<'a>(&'a self, query: &str) -> Result<Statement<'a>> { pub fn prepare_cached<'a>(&'a self, query: &str) -> Result<Statement<'a>> {
self.conn.borrow_mut().prepare_cached(query, self) self.conn.borrow_mut().prepare_cached(query, self)
@ -968,32 +976,30 @@ impl Connection {
/// the connection for the duration of the transaction. The transaction /// the connection for the duration of the transaction. The transaction
/// is active until the `Transaction` object falls out of scope. /// is active until the `Transaction` object falls out of scope.
/// ///
/// ## Note /// # Note
/// A transaction will roll back by default. The `set_commit`, /// A transaction will roll back by default. The `set_commit`,
/// `set_rollback`, and `commit` methods alter this behavior. /// `set_rollback`, and `commit` methods alter this behavior.
/// ///
/// ## Panics /// # Panics
/// ///
/// Panics if a transaction is already active. /// Panics if a transaction is already active.
/// ///
/// ## Example /// # Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use postgres::{Connection, SslMode}; /// # use postgres::{Connection, SslMode};
/// # fn foo() -> Result<(), postgres::error::Error> {
/// # let conn = Connection::connect("", &SslMode::None).unwrap(); /// # let conn = Connection::connect("", &SslMode::None).unwrap();
/// let trans = try!(conn.transaction()); /// let trans = conn.transaction().unwrap();
/// try!(trans.execute("UPDATE foo SET bar = 10", &[])); /// trans.execute("UPDATE foo SET bar = 10", &[]).unwrap();
/// // ... /// // ...
/// ///
/// try!(trans.commit()); /// trans.commit().unwrap();
/// # Ok(())
/// # }
/// ``` /// ```
pub fn transaction<'a>(&'a self) -> Result<Transaction<'a>> { pub fn transaction<'a>(&'a self) -> Result<Transaction<'a>> {
let mut conn = self.conn.borrow_mut(); let mut conn = self.conn.borrow_mut();
check_desync!(conn); 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");
try!(conn.quick_query("BEGIN")); try!(conn.quick_query("BEGIN"));
conn.trans_depth += 1; conn.trans_depth += 1;
Ok(Transaction { Ok(Transaction {
@ -1006,7 +1012,9 @@ impl Connection {
/// Sets the isolation level which will be used for future transactions. /// Sets the isolation level which will be used for future transactions.
/// ///
/// ## Note /// This is a simple wrapper around `SET TRANSACTION ISOLATION LEVEL ...`.
///
/// # Note
/// ///
/// This will not change the behavior of an active transaction. /// This will not change the behavior of an active transaction.
pub fn set_transaction_isolation(&self, level: IsolationLevel) -> Result<()> { pub fn set_transaction_isolation(&self, level: IsolationLevel) -> Result<()> {
@ -1014,6 +1022,8 @@ impl Connection {
} }
/// Returns the isolation level which will be used for future transactions. /// Returns the isolation level which will be used for future transactions.
///
/// This is a simple wrapper around `SHOW TRANSACTION ISOLATION LEVEL`.
pub fn transaction_isolation(&self) -> Result<IsolationLevel> { pub fn transaction_isolation(&self) -> Result<IsolationLevel> {
let mut conn = self.conn.borrow_mut(); let mut conn = self.conn.borrow_mut();
check_desync!(conn); check_desync!(conn);
@ -1028,13 +1038,18 @@ impl Connection {
/// ///
/// On success, returns the number of rows modified or 0 if not applicable. /// On success, returns the number of rows modified or 0 if not applicable.
/// ///
/// ## Panics /// # Panics
/// ///
/// Panics if the number of parameters provided does not match the number /// Panics if the number of parameters provided does not match the number
/// expected. /// expected.
pub fn execute(&self, query: &str, params: &[&ToSql]) -> Result<u64> { pub fn execute(&self, query: &str, params: &[&ToSql]) -> Result<u64> {
let (param_types, columns) = try!(self.conn.borrow_mut().raw_prepare("", query)); let (param_types, columns) = try!(self.conn.borrow_mut().raw_prepare("", query));
let stmt = Statement::new(self, "".to_owned(), param_types, columns, Cell::new(0), true); let stmt = Statement::new(self,
"".to_owned(),
param_types,
columns,
Cell::new(0),
true);
stmt.execute(params) stmt.execute(params)
} }
@ -1045,33 +1060,32 @@ impl Connection {
/// execution of batches of non-dynamic statements - for example, creation /// execution of batches of non-dynamic statements - for example, creation
/// of a schema for a fresh database. /// of a schema for a fresh database.
/// ///
/// ## Warning /// # Warning
/// ///
/// Prepared statements should be used for any SQL statement which contains /// Prepared statements should be used for any SQL statement which contains
/// user-specified data, as it provides functionality to safely embed that /// user-specified data, as it provides functionality to safely embed that
/// data in the statement. Do not form statements via string concatenation /// data in the statement. Do not form statements via string concatenation
/// and feed them into this method. /// and feed them into this method.
/// ///
/// ## Example /// # Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use postgres::{Connection, Result}; /// # use postgres::{Connection, SslMode, Result};
/// fn init_db(conn: &Connection) -> Result<()> { /// # let conn = Connection::connect("", &SslMode::None).unwrap();
/// conn.batch_execute(" /// conn.batch_execute("
/// CREATE TABLE person ( /// CREATE TABLE person (
/// id SERIAL PRIMARY KEY, /// id SERIAL PRIMARY KEY,
/// name NOT NULL /// name NOT NULL
/// ); /// );
/// ///
/// CREATE TABLE purchase ( /// CREATE TABLE purchase (
/// id SERIAL PRIMARY KEY, /// id SERIAL PRIMARY KEY,
/// person INT NOT NULL REFERENCES person (id), /// person INT NOT NULL REFERENCES person (id),
/// time TIMESTAMPTZ NOT NULL, /// time TIMESTAMPTZ NOT NULL,
/// ); /// );
/// ///
/// CREATE INDEX ON purchase (time); /// CREATE INDEX ON purchase (time);
/// ") /// ").unwrap();
/// }
/// ``` /// ```
pub fn batch_execute(&self, query: &str) -> Result<()> { pub fn batch_execute(&self, query: &str) -> Result<()> {
self.conn.borrow_mut().quick_query(query).map(|_| ()) self.conn.borrow_mut().quick_query(query).map(|_| ())
@ -1133,9 +1147,9 @@ pub struct Transaction<'conn> {
impl<'a> fmt::Debug for Transaction<'a> { impl<'a> fmt::Debug for Transaction<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Transaction") fmt.debug_struct("Transaction")
.field("commit", &self.commit.get()) .field("commit", &self.commit.get())
.field("depth", &self.depth) .field("depth", &self.depth)
.finish() .finish()
} }
} }
@ -1186,7 +1200,7 @@ impl<'conn> Transaction<'conn> {
/// Like `Connection::transaction`. /// Like `Connection::transaction`.
/// ///
/// ## Panics /// # Panics
/// ///
/// Panics if there is an active nested transaction. /// Panics if there is an active nested transaction.
pub fn transaction<'a>(&'a self) -> Result<Transaction<'a>> { pub fn transaction<'a>(&'a self) -> Result<Transaction<'a>> {
@ -1263,11 +1277,10 @@ fn read_rows(conn: &mut InnerConnection, buf: &mut VecDeque<Vec<Option<Vec<u8>>>
return DbError::new(fields); return DbError::new(fields);
} }
CopyInResponse { .. } => { CopyInResponse { .. } => {
try!(conn.write_messages(&[ try!(conn.write_messages(&[CopyFail {
CopyFail { message: "COPY queries cannot be directly executed",
message: "COPY queries cannot be directly executed", },
}, Sync]));
Sync]));
} }
CopyOutResponse { .. } => { CopyOutResponse { .. } => {
loop { loop {
@ -1276,9 +1289,9 @@ fn read_rows(conn: &mut InnerConnection, buf: &mut VecDeque<Vec<Option<Vec<u8>>>
_ => {} _ => {}
} }
} }
return Err(Error::IoError(std_io::Error::new( return Err(Error::IoError(std_io::Error::new(std_io::ErrorKind::InvalidInput,
std_io::ErrorKind::InvalidInput, "COPY queries cannot be directly \
"COPY queries cannot be directly executed"))); executed")));
} }
_ => { _ => {
conn.desynchronized = true; conn.desynchronized = true;
@ -1384,7 +1397,8 @@ trait LazyRowsNew<'trans, 'stmt> {
row_limit: i32, row_limit: i32,
more_rows: bool, more_rows: bool,
finished: bool, finished: bool,
trans: &'trans Transaction<'trans>) -> LazyRows<'trans, 'stmt>; trans: &'trans Transaction<'trans>)
-> LazyRows<'trans, 'stmt>;
} }
trait SessionInfoNew<'a> { trait SessionInfoNew<'a> {
@ -1397,7 +1411,8 @@ trait StatementInternals<'conn> {
param_types: Vec<Type>, param_types: Vec<Type>,
columns: Vec<Column>, columns: Vec<Column>,
next_portal_id: Cell<u32>, next_portal_id: Cell<u32>,
finished: bool) -> Statement<'conn>; finished: bool)
-> Statement<'conn>;
fn conn(&self) -> &'conn Connection; fn conn(&self) -> &'conn Connection;
} }

View File

@ -18,7 +18,7 @@ use std::iter::repeat;
struct StepUp<T> { struct StepUp<T> {
next: T, next: T,
end: T, end: T,
ammount: T ammount: T,
} }
impl <T> Iterator for StepUp<T> where impl <T> Iterator for StepUp<T> where
@ -47,7 +47,7 @@ impl <T> RangeExt<T> for Range<T> where
StepUp { StepUp {
next: self.start, next: self.start,
end: self.end, end: self.end,
ammount: ammount ammount: ammount,
} }
} }
} }
@ -72,7 +72,7 @@ fn zero(dst: &mut [u8]) {
} }
/// Read a vector of bytes into a vector of u32s. The values are read in little-endian format. /// Read a vector of bytes into a vector of u32s. The values are read in little-endian format.
fn read_u32v_le(dst: &mut[u32], input: &[u8]) { fn read_u32v_le(dst: &mut [u32], input: &[u8]) {
assert!(dst.len() * 4 == input.len()); assert!(dst.len() * 4 == input.len());
unsafe { unsafe {
let mut x: *mut u32 = dst.get_unchecked_mut(0); let mut x: *mut u32 = dst.get_unchecked_mut(0);
@ -89,7 +89,7 @@ fn read_u32v_le(dst: &mut[u32], input: &[u8]) {
/// Write a u32 into a vector, which must be 4 bytes long. The value is written in little-endian /// Write a u32 into a vector, which must be 4 bytes long. The value is written in little-endian
/// format. /// format.
fn write_u32_le(dst: &mut[u8], mut input: u32) { fn write_u32_le(dst: &mut [u8], mut input: u32) {
assert!(dst.len() == 4); assert!(dst.len() == 4);
input = input.to_le(); input = input.to_le();
unsafe { unsafe {
@ -147,7 +147,7 @@ trait FixedBuffer {
/// Get the current buffer. The buffer must already be full. This clears the buffer as well. /// Get the current buffer. The buffer must already be full. This clears the buffer as well.
fn full_buffer<'s>(&'s mut self) -> &'s [u8]; fn full_buffer<'s>(&'s mut self) -> &'s [u8];
/// Get the current buffer. /// Get the current buffer.
fn current_buffer<'s>(&'s mut self) -> &'s [u8]; fn current_buffer<'s>(&'s mut self) -> &'s [u8];
/// Get the current position of the buffer. /// Get the current position of the buffer.
@ -247,14 +247,18 @@ struct FixedBuffer64 {
buffer_idx: usize, buffer_idx: usize,
} }
impl Clone for FixedBuffer64 { fn clone(&self) -> FixedBuffer64 { *self } } impl Clone for FixedBuffer64 {
fn clone(&self) -> FixedBuffer64 {
*self
}
}
impl FixedBuffer64 { impl FixedBuffer64 {
/// Create a new buffer /// Create a new buffer
fn new() -> FixedBuffer64 { fn new() -> FixedBuffer64 {
FixedBuffer64 { FixedBuffer64 {
buffer: [0u8; 64], buffer: [0u8; 64],
buffer_idx: 0 buffer_idx: 0,
} }
} }
} }
@ -266,7 +270,7 @@ struct Md5State {
s0: u32, s0: u32,
s1: u32, s1: u32,
s2: u32, s2: u32,
s3: u32 s3: u32,
} }
impl Md5State { impl Md5State {
@ -275,7 +279,7 @@ impl Md5State {
s0: 0x67452301, s0: 0x67452301,
s1: 0xefcdab89, s1: 0xefcdab89,
s2: 0x98badcfe, s2: 0x98badcfe,
s3: 0x10325476 s3: 0x10325476,
} }
} }
@ -341,8 +345,18 @@ impl Md5State {
for i in (0..16).step_up(4) { for i in (0..16).step_up(4) {
a = op_g(a, b, c, d, data[t & 0x0f].wrapping_add(C2[i]), 5); a = op_g(a, b, c, d, data[t & 0x0f].wrapping_add(C2[i]), 5);
d = op_g(d, a, b, c, data[(t + 5) & 0x0f].wrapping_add(C2[i + 1]), 9); d = op_g(d, a, b, c, data[(t + 5) & 0x0f].wrapping_add(C2[i + 1]), 9);
c = op_g(c, d, a, b, data[(t + 10) & 0x0f].wrapping_add(C2[i + 2]), 14); c = op_g(c,
b = op_g(b, c, d, a, data[(t + 15) & 0x0f].wrapping_add(C2[i + 3]), 20); d,
a,
b,
data[(t + 10) & 0x0f].wrapping_add(C2[i + 2]),
14);
b = op_g(b,
c,
d,
a,
data[(t + 15) & 0x0f].wrapping_add(C2[i + 3]),
20);
t += 20; t += 20;
} }
@ -361,8 +375,18 @@ impl Md5State {
for i in (0..16).step_up(4) { for i in (0..16).step_up(4) {
a = op_i(a, b, c, d, data[t & 0x0f].wrapping_add(C4[i]), 6); a = op_i(a, b, c, d, data[t & 0x0f].wrapping_add(C4[i]), 6);
d = op_i(d, a, b, c, data[(t + 7) & 0x0f].wrapping_add(C4[i + 1]), 10); d = op_i(d, a, b, c, data[(t + 7) & 0x0f].wrapping_add(C4[i + 1]), 10);
c = op_i(c, d, a, b, data[(t + 14) & 0x0f].wrapping_add(C4[i + 2]), 15); c = op_i(c,
b = op_i(b, c, d, a, data[(t + 21) & 0x0f].wrapping_add(C4[i + 3]), 21); d,
a,
b,
data[(t + 14) & 0x0f].wrapping_add(C4[i + 2]),
15);
b = op_i(b,
c,
d,
a,
data[(t + 21) & 0x0f].wrapping_add(C4[i + 3]),
21);
t += 28; t += 28;
} }
@ -374,28 +398,24 @@ impl Md5State {
} }
// Round 1 constants // Round 1 constants
static C1: [u32; 16] = [ static C1: [u32; 16] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a,
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821];
];
// Round 2 constants // Round 2 constants
static C2: [u32; 16] = [ static C2: [u32; 16] = [0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a];
];
// Round 3 constants // Round 3 constants
static C3: [u32; 16] = [ static C3: [u32; 16] = [0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665];
];
// Round 4 constants // Round 4 constants
static C4: [u32; 16] = [ static C4: [u32; 16] = [0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391];
];
/// The MD5 Digest algorithm /// The MD5 Digest algorithm
pub struct Md5 { pub struct Md5 {
@ -412,7 +432,7 @@ impl Md5 {
length_bytes: 0, length_bytes: 0,
buffer: FixedBuffer64::new(), buffer: FixedBuffer64::new(),
state: Md5State::new(), state: Md5State::new(),
finished: false finished: false,
} }
} }
@ -422,8 +442,9 @@ impl Md5 {
// 2^64 - ie: integer overflow is OK. // 2^64 - ie: integer overflow is OK.
self.length_bytes += input.len() as u64; self.length_bytes += input.len() as u64;
let self_state = &mut self.state; let self_state = &mut self.state;
self.buffer.input(input, |d: &[u8]| { self_state.process_block(d);} self.buffer.input(input, |d: &[u8]| {
); self_state.process_block(d);
});
} }
pub fn reset(&mut self) { pub fn reset(&mut self) {
@ -436,7 +457,9 @@ impl Md5 {
pub fn result(&mut self, out: &mut [u8]) { pub fn result(&mut self, out: &mut [u8]) {
if !self.finished { if !self.finished {
let self_state = &mut self.state; let self_state = &mut self.state;
self.buffer.standard_padding(8, |d: &[u8]| { self_state.process_block(d); }); self.buffer.standard_padding(8, |d: &[u8]| {
self_state.process_block(d);
});
write_u32_le(self.buffer.next(4), (self.length_bytes << 3) as u32); write_u32_le(self.buffer.next(4), (self.length_bytes << 3) as u32);
write_u32_le(self.buffer.next(4), (self.length_bytes >> 29) as u32); write_u32_le(self.buffer.next(4), (self.length_bytes >> 29) as u32);
self_state.process_block(self.buffer.full_buffer()); self_state.process_block(self.buffer.full_buffer());
@ -449,12 +472,14 @@ impl Md5 {
write_u32_le(&mut out[12..16], self.state.s3); write_u32_le(&mut out[12..16], self.state.s3);
} }
fn output_bits(&self) -> usize { 128 } fn output_bits(&self) -> usize {
128
}
pub fn result_str(&mut self) -> String { pub fn result_str(&mut self) -> String {
use serialize::hex::ToHex; use serialize::hex::ToHex;
let mut buf: Vec<u8> = repeat(0).take((self.output_bits()+7)/8).collect(); let mut buf: Vec<u8> = repeat(0).take((self.output_bits() + 7) / 8).collect();
self.result(&mut buf); self.result(&mut buf);
buf[..].to_hex() buf[..].to_hex()
} }

View File

@ -20,14 +20,14 @@ pub enum BackendMessage {
AuthenticationGSS, AuthenticationGSS,
AuthenticationKerberosV5, AuthenticationKerberosV5,
AuthenticationMD5Password { AuthenticationMD5Password {
salt: [u8; 4] salt: [u8; 4],
}, },
AuthenticationOk, AuthenticationOk,
AuthenticationSCMCredential, AuthenticationSCMCredential,
AuthenticationSSPI, AuthenticationSSPI,
BackendKeyData { BackendKeyData {
process_id: u32, process_id: u32,
secret_key: u32 secret_key: u32,
}, },
BindComplete, BindComplete,
CloseComplete, CloseComplete,
@ -48,15 +48,15 @@ pub enum BackendMessage {
column_formats: Vec<u16>, column_formats: Vec<u16>,
}, },
DataRow { DataRow {
row: Vec<Option<Vec<u8>>> row: Vec<Option<Vec<u8>>>,
}, },
EmptyQueryResponse, EmptyQueryResponse,
ErrorResponse { ErrorResponse {
fields: Vec<(u8, String)> fields: Vec<(u8, String)>,
}, },
NoData, NoData,
NoticeResponse { NoticeResponse {
fields: Vec<(u8, String)> fields: Vec<(u8, String)>,
}, },
NotificationResponse { NotificationResponse {
pid: u32, pid: u32,
@ -64,7 +64,7 @@ pub enum BackendMessage {
payload: String, payload: String,
}, },
ParameterDescription { ParameterDescription {
types: Vec<Oid> types: Vec<Oid>,
}, },
ParameterStatus { ParameterStatus {
parameter: String, parameter: String,
@ -73,11 +73,11 @@ pub enum BackendMessage {
ParseComplete, ParseComplete,
PortalSuspended, PortalSuspended,
ReadyForQuery { ReadyForQuery {
_state: u8 _state: u8,
}, },
RowDescription { RowDescription {
descriptions: Vec<RowDescriptionEntry> descriptions: Vec<RowDescriptionEntry>,
} },
} }
pub struct RowDescriptionEntry { pub struct RowDescriptionEntry {
@ -87,7 +87,7 @@ pub struct RowDescriptionEntry {
pub type_oid: Oid, pub type_oid: Oid,
pub type_size: i16, pub type_size: i16,
pub type_modifier: i32, pub type_modifier: i32,
pub format: i16 pub format: i16,
} }
pub enum FrontendMessage<'a> { pub enum FrontendMessage<'a> {
@ -96,7 +96,7 @@ pub enum FrontendMessage<'a> {
statement: &'a str, statement: &'a str,
formats: &'a [i16], formats: &'a [i16],
values: &'a [Option<Vec<u8>>], values: &'a [Option<Vec<u8>>],
result_formats: &'a [i16] result_formats: &'a [i16],
}, },
CancelRequest { CancelRequest {
code: u32, code: u32,
@ -105,43 +105,43 @@ pub enum FrontendMessage<'a> {
}, },
Close { Close {
variant: u8, variant: u8,
name: &'a str name: &'a str,
}, },
CopyData { CopyData {
data: &'a [u8], data: &'a [u8],
}, },
CopyDone, CopyDone,
CopyFail { CopyFail {
message: &'a str message: &'a str,
}, },
Describe { Describe {
variant: u8, variant: u8,
name: &'a str name: &'a str,
}, },
Execute { Execute {
portal: &'a str, portal: &'a str,
max_rows: i32 max_rows: i32,
}, },
Parse { Parse {
name: &'a str, name: &'a str,
query: &'a str, query: &'a str,
param_types: &'a [Oid] param_types: &'a [Oid],
}, },
PasswordMessage { PasswordMessage {
password: &'a str password: &'a str,
}, },
Query { Query {
query: &'a str query: &'a str,
}, },
SslRequest { SslRequest {
code: u32 code: u32,
}, },
StartupMessage { StartupMessage {
version: u32, version: u32,
parameters: &'a [(String, String)] parameters: &'a [(String, String)],
}, },
Sync, Sync,
Terminate Terminate,
} }
#[doc(hidden)] #[doc(hidden)]
@ -158,7 +158,7 @@ impl<W: Write> WriteCStr for W {
#[doc(hidden)] #[doc(hidden)]
pub trait WriteMessage { pub trait WriteMessage {
fn write_message(&mut self, &FrontendMessage) -> io::Result<()> ; fn write_message(&mut self, &FrontendMessage) -> io::Result<()>;
} }
impl<W: Write> WriteMessage for W { impl<W: Write> WriteMessage for W {
@ -285,8 +285,7 @@ impl<R: BufRead> ReadCStr for R {
pub trait ReadMessage { pub trait ReadMessage {
fn read_message(&mut self) -> io::Result<BackendMessage>; fn read_message(&mut self) -> io::Result<BackendMessage>;
fn read_message_timeout(&mut self, timeout: Duration) fn read_message_timeout(&mut self, timeout: Duration) -> io::Result<Option<BackendMessage>>;
-> io::Result<Option<BackendMessage>>;
fn finish_read_message(&mut self, ident: u8) -> io::Result<BackendMessage>; fn finish_read_message(&mut self, ident: u8) -> io::Result<BackendMessage>;
} }
@ -297,8 +296,7 @@ impl<R: BufRead + ReadTimeout> ReadMessage for R {
self.finish_read_message(ident) self.finish_read_message(ident)
} }
fn read_message_timeout(&mut self, timeout: Duration) fn read_message_timeout(&mut self, timeout: Duration) -> io::Result<Option<BackendMessage>> {
-> io::Result<Option<BackendMessage>> {
try!(self.set_read_timeout(Some(timeout))); try!(self.set_read_timeout(Some(timeout)));
let ident = self.read_u8(); let ident = self.read_u8();
try!(self.set_read_timeout(None)); try!(self.set_read_timeout(None));
@ -328,16 +326,14 @@ impl<R: BufRead + ReadTimeout> ReadMessage for R {
b'A' => NotificationResponse { b'A' => NotificationResponse {
pid: try!(rdr.read_u32::<BigEndian>()), pid: try!(rdr.read_u32::<BigEndian>()),
channel: try!(rdr.read_cstr()), channel: try!(rdr.read_cstr()),
payload: try!(rdr.read_cstr()) payload: try!(rdr.read_cstr()),
}, },
b'c' => BCopyDone, b'c' => BCopyDone,
b'C' => CommandComplete { tag: try!(rdr.read_cstr()) }, b'C' => CommandComplete { tag: try!(rdr.read_cstr()) },
b'd' => { b'd' => {
let mut data = vec![]; let mut data = vec![];
try!(rdr.read_to_end(&mut data)); try!(rdr.read_to_end(&mut data));
BCopyData { BCopyData { data: data }
data: data,
}
} }
b'D' => try!(read_data_row(&mut rdr)), b'D' => try!(read_data_row(&mut rdr)),
b'E' => ErrorResponse { fields: try!(read_fields(&mut rdr)) }, b'E' => ErrorResponse { fields: try!(read_fields(&mut rdr)) },
@ -366,7 +362,7 @@ impl<R: BufRead + ReadTimeout> ReadMessage for R {
b'I' => EmptyQueryResponse, b'I' => EmptyQueryResponse,
b'K' => BackendKeyData { b'K' => BackendKeyData {
process_id: try!(rdr.read_u32::<BigEndian>()), process_id: try!(rdr.read_u32::<BigEndian>()),
secret_key: try!(rdr.read_u32::<BigEndian>()) secret_key: try!(rdr.read_u32::<BigEndian>()),
}, },
b'n' => NoData, b'n' => NoData,
b'N' => NoticeResponse { fields: try!(read_fields(&mut rdr)) }, b'N' => NoticeResponse { fields: try!(read_fields(&mut rdr)) },
@ -374,7 +370,7 @@ impl<R: BufRead + ReadTimeout> ReadMessage for R {
b's' => PortalSuspended, b's' => PortalSuspended,
b'S' => ParameterStatus { b'S' => ParameterStatus {
parameter: try!(rdr.read_cstr()), parameter: try!(rdr.read_cstr()),
value: try!(rdr.read_cstr()) value: try!(rdr.read_cstr()),
}, },
b't' => try!(read_parameter_description(&mut rdr)), b't' => try!(read_parameter_description(&mut rdr)),
b'T' => try!(read_row_description(&mut rdr)), b'T' => try!(read_row_description(&mut rdr)),
@ -431,7 +427,7 @@ fn read_auth_message<R: Read>(buf: &mut R) -> io::Result<BackendMessage> {
let mut salt = [0; 4]; let mut salt = [0; 4];
try!(util::read_all(buf, &mut salt)); try!(util::read_all(buf, &mut salt));
AuthenticationMD5Password { salt: salt } AuthenticationMD5Password { salt: salt }
}, }
6 => AuthenticationSCMCredential, 6 => AuthenticationSCMCredential,
7 => AuthenticationGSS, 7 => AuthenticationGSS,
9 => AuthenticationSSPI, 9 => AuthenticationSSPI,

View File

@ -20,14 +20,14 @@ pub struct Notification {
/// An iterator over asynchronous notifications. /// An iterator over asynchronous notifications.
pub struct Notifications<'conn> { pub struct Notifications<'conn> {
conn: &'conn Connection conn: &'conn Connection,
} }
impl<'a> fmt::Debug for Notifications<'a> { impl<'a> fmt::Debug for Notifications<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Notifications") fmt.debug_struct("Notifications")
.field("pending", &self.len()) .field("pending", &self.len())
.finish() .finish()
} }
} }
@ -45,9 +45,7 @@ impl<'conn> Notifications<'conn> {
/// `None` if more notifications are received. However, those notifications /// `None` if more notifications are received. However, those notifications
/// will not be registered until the connection is used in some way. /// will not be registered until the connection is used in some way.
pub fn iter<'a>(&'a self) -> Iter<'a> { pub fn iter<'a>(&'a self) -> Iter<'a> {
Iter { Iter { conn: self.conn }
conn: self.conn,
}
} }
/// Returns an iterator over notifications that blocks until one is /// Returns an iterator over notifications that blocks until one is
@ -55,9 +53,7 @@ impl<'conn> Notifications<'conn> {
/// ///
/// The iterator will never return `None`. /// The iterator will never return `None`.
pub fn blocking_iter<'a>(&'a self) -> BlockingIter<'a> { pub fn blocking_iter<'a>(&'a self) -> BlockingIter<'a> {
BlockingIter { BlockingIter { conn: self.conn }
conn: self.conn,
}
} }
/// Returns an iterator over notifications that blocks for a limited time /// Returns an iterator over notifications that blocks for a limited time
@ -86,9 +82,7 @@ impl<'a, 'conn> IntoIterator for &'a Notifications<'conn> {
impl<'conn> NotificationsNew<'conn> for Notifications<'conn> { impl<'conn> NotificationsNew<'conn> for Notifications<'conn> {
fn new(conn: &'conn Connection) -> Notifications<'conn> { fn new(conn: &'conn Connection) -> Notifications<'conn> {
Notifications { Notifications { conn: conn }
conn: conn,
}
} }
} }
@ -129,11 +123,11 @@ impl<'a> Iterator for BlockingIter<'a> {
Some(Ok(Notification { Some(Ok(Notification {
pid: pid, pid: pid,
channel: channel, channel: channel,
payload: payload payload: payload,
})) }))
} }
Err(err) => Some(Err(Error::IoError(err))), Err(err) => Some(Err(Error::IoError(err))),
_ => unreachable!() _ => unreachable!(),
} }
} }
} }
@ -164,12 +158,12 @@ impl<'a> Iterator for TimeoutIter<'a> {
Some(Ok(Notification { Some(Ok(Notification {
pid: pid, pid: pid,
channel: channel, channel: channel,
payload: payload payload: payload,
})) }))
} }
Ok(None) => None, Ok(None) => None,
Err(err) => Some(Err(Error::IoError(err))), Err(err) => Some(Err(Error::IoError(err))),
_ => unreachable!() _ => unreachable!(),
} }
} }
} }

View File

@ -13,7 +13,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
use std::os::windows::io::{AsRawSocket, RawSocket}; use std::os::windows::io::{AsRawSocket, RawSocket};
use {SslMode, ConnectParams, ConnectTarget}; use {SslMode, ConnectParams, ConnectTarget};
use error::{ConnectError}; use error::ConnectError;
use io::{NegotiateSsl, StreamWrapper}; use io::{NegotiateSsl, StreamWrapper};
use message::{self, WriteMessage}; use message::{self, WriteMessage};
use message::FrontendMessage::SslRequest; use message::FrontendMessage::SslRequest;
@ -28,7 +28,8 @@ pub trait ReadTimeout {
impl ReadTimeout for BufStream<Box<StreamWrapper>> { impl ReadTimeout for BufStream<Box<StreamWrapper>> {
fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
match self.get_ref().get_ref().0 { match self.get_ref().get_ref().0 {
InternalStream::Tcp(ref s) => <TcpStream as TcpStreamExt>::set_read_timeout(s, timeout), InternalStream::Tcp(ref s) =>
<TcpStream as TcpStreamExt>::set_read_timeout(s, timeout),
#[cfg(feature = "unix_socket")] #[cfg(feature = "unix_socket")]
InternalStream::Unix(ref s) => s.set_read_timeout(timeout), InternalStream::Unix(ref s) => s.set_read_timeout(timeout),
} }
@ -136,8 +137,9 @@ fn open_socket(params: &ConnectParams) -> Result<InternalStream, ConnectError> {
} }
} }
pub fn initialize_stream(params: &ConnectParams, ssl: &SslMode) pub fn initialize_stream(params: &ConnectParams,
-> Result<Box<StreamWrapper>, ConnectError> { ssl: &SslMode)
-> Result<Box<StreamWrapper>, ConnectError> {
let mut socket = Stream(try!(open_socket(params))); let mut socket = Stream(try!(open_socket(params)));
let (ssl_required, negotiator) = match *ssl { let (ssl_required, negotiator) = match *ssl {
@ -161,11 +163,11 @@ pub fn initialize_stream(params: &ConnectParams, ssl: &SslMode)
let host = match params.target { let host = match params.target {
ConnectTarget::Tcp(ref host) => host, ConnectTarget::Tcp(ref host) => host,
#[cfg(feature = "unix_socket")] #[cfg(feature = "unix_socket")]
ConnectTarget::Unix(_) => return Err(ConnectError::IoError(::bad_response())) ConnectTarget::Unix(_) => return Err(ConnectError::IoError(::bad_response())),
}; };
match negotiator.negotiate_ssl(host, socket) { match negotiator.negotiate_ssl(host, socket) {
Ok(stream) => Ok(stream), Ok(stream) => Ok(stream),
Err(err) => Err(ConnectError::SslError(err)) Err(err) => Err(ConnectError::SslError(err)),
} }
} }

View File

@ -7,13 +7,7 @@ use std::borrow::Cow;
use std::slice; use std::slice;
use std::vec; use std::vec;
use {Result, use {Result, Transaction, read_rows, DbErrorNew, SessionInfoNew, RowsNew, LazyRowsNew,
Transaction,
read_rows,
DbErrorNew,
SessionInfoNew,
RowsNew,
LazyRowsNew,
StatementInternals}; StatementInternals};
use types::{FromSql, SessionInfo}; use types::{FromSql, SessionInfo};
use stmt::{Statement, Column}; use stmt::{Statement, Column};
@ -38,9 +32,9 @@ impl<'a> RowsNew<'a> for Rows<'a> {
impl<'a> fmt::Debug for Rows<'a> { impl<'a> fmt::Debug for Rows<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Rows") fmt.debug_struct("Rows")
.field("columns", &self.columns()) .field("columns", &self.columns())
.field("rows", &self.data.len()) .field("rows", &self.data.len())
.finish() .finish()
} }
} }
@ -71,7 +65,7 @@ impl<'stmt> Rows<'stmt> {
pub fn iter<'a>(&'a self) -> Iter<'a> { pub fn iter<'a>(&'a self) -> Iter<'a> {
Iter { Iter {
stmt: self.stmt, stmt: self.stmt,
iter: self.data.iter() iter: self.data.iter(),
} }
} }
} }
@ -92,7 +86,7 @@ impl<'stmt> IntoIterator for Rows<'stmt> {
fn into_iter(self) -> IntoIter<'stmt> { fn into_iter(self) -> IntoIter<'stmt> {
IntoIter { IntoIter {
stmt: self.stmt, stmt: self.stmt,
iter: self.data.into_iter() iter: self.data.into_iter(),
} }
} }
} }
@ -172,14 +166,14 @@ impl<'stmt> ExactSizeIterator for IntoIter<'stmt> {}
/// A single result row of a query. /// A single result row of a query.
pub struct Row<'a> { pub struct Row<'a> {
stmt: &'a Statement<'a>, stmt: &'a Statement<'a>,
data: Cow<'a, [Option<Vec<u8>>]> data: Cow<'a, [Option<Vec<u8>>]>,
} }
impl<'a> fmt::Debug for Row<'a> { impl<'a> fmt::Debug for Row<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Row") fmt.debug_struct("Row")
.field("statement", self.stmt) .field("statement", self.stmt)
.finish() .finish()
} }
} }
@ -201,14 +195,18 @@ impl<'a> Row<'a> {
/// ///
/// Returns an `Error` value if the index does not reference a column or /// Returns an `Error` value if the index does not reference a column or
/// the return type is not compatible with the Postgres type. /// the return type is not compatible with the Postgres type.
pub fn get_opt<I, T>(&self, idx: I) -> Result<T> where I: RowIndex, T: FromSql { pub fn get_opt<I, T>(&self, idx: I) -> Result<T>
where I: RowIndex,
T: FromSql
{
let idx = try!(idx.idx(self.stmt).ok_or(Error::InvalidColumn)); let idx = try!(idx.idx(self.stmt).ok_or(Error::InvalidColumn));
let ty = self.stmt.columns()[idx].type_(); let ty = self.stmt.columns()[idx].type_();
if !<T as FromSql>::accepts(ty) { if !<T as FromSql>::accepts(ty) {
return Err(Error::WrongType(ty.clone())); return Err(Error::WrongType(ty.clone()));
} }
let conn = self.stmt.conn().conn.borrow(); let conn = self.stmt.conn().conn.borrow();
FromSql::from_sql_nullable(ty, self.data[idx].as_ref().map(|e| &**e).as_mut(), FromSql::from_sql_nullable(ty,
self.data[idx].as_ref().map(|e| &**e).as_mut(),
&SessionInfo::new(&*conn)) &SessionInfo::new(&*conn))
} }
@ -217,35 +215,41 @@ impl<'a> Row<'a> {
/// A field can be accessed by the name or index of its column, though /// A field can be accessed by the name or index of its column, though
/// access by index is more efficient. Rows are 0-indexed. /// access by index is more efficient. Rows are 0-indexed.
/// ///
/// ## Panics /// # Panics
/// ///
/// Panics if the index does not reference a column or the return type is /// Panics if the index does not reference a column or the return type is
/// not compatible with the Postgres type. /// not compatible with the Postgres type.
/// ///
/// ## Example /// # Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use postgres::{Connection, SslMode}; /// # use postgres::{Connection, SslMode};
/// # let conn = Connection::connect("", &SslMode::None).unwrap(); /// # let conn = Connection::connect("", &SslMode::None).unwrap();
/// # let stmt = conn.prepare("").unwrap(); /// let stmt = conn.prepare("SELECT foo, bar from BAZ").unwrap();
/// # let mut result = stmt.query(&[]).unwrap(); /// for row in stmt.query(&[]).unwrap() {
/// # let row = result.iter().next().unwrap(); /// let foo: i32 = row.get(0);
/// let foo: i32 = row.get(0); /// let bar: String = row.get("bar");
/// let bar: String = row.get("bar"); /// println!("{}: {}", foo, bar);
/// }
/// ``` /// ```
pub fn get<I, T>(&self, idx: I) -> T where I: RowIndex + fmt::Debug + Clone, T: FromSql { pub fn get<I, T>(&self, idx: I) -> T
where I: RowIndex + fmt::Debug + Clone,
T: FromSql
{
match self.get_opt(idx.clone()) { match self.get_opt(idx.clone()) {
Ok(ok) => ok, Ok(ok) => ok,
Err(err) => panic!("error retrieving column {:?}: {:?}", idx, err) Err(err) => panic!("error retrieving column {:?}: {:?}", idx, err),
} }
} }
/// Retrieves the specified field as a raw buffer of Postgres data. /// Retrieves the specified field as a raw buffer of Postgres data.
/// ///
/// ## Panics /// # Panics
/// ///
/// Panics if the index does not reference a column. /// Panics if the index does not reference a column.
pub fn get_bytes<I>(&self, idx: I) -> Option<&[u8]> where I: RowIndex + fmt::Debug { pub fn get_bytes<I>(&self, idx: I) -> Option<&[u8]>
where I: RowIndex + fmt::Debug
{
match idx.idx(self.stmt) { match idx.idx(self.stmt) {
Some(idx) => self.data[idx].as_ref().map(|e| &**e), Some(idx) => self.data[idx].as_ref().map(|e| &**e),
None => panic!("invalid index {:?}", idx), None => panic!("invalid index {:?}", idx),
@ -303,7 +307,8 @@ impl<'trans, 'stmt> LazyRowsNew<'trans, 'stmt> for LazyRows<'trans, 'stmt> {
row_limit: i32, row_limit: i32,
more_rows: bool, more_rows: bool,
finished: bool, finished: bool,
trans: &'trans Transaction<'trans>) -> LazyRows<'trans, 'stmt> { trans: &'trans Transaction<'trans>)
-> LazyRows<'trans, 'stmt> {
LazyRows { LazyRows {
stmt: stmt, stmt: stmt,
data: data, data: data,
@ -311,7 +316,7 @@ impl<'trans, 'stmt> LazyRowsNew<'trans, 'stmt> for LazyRows<'trans, 'stmt> {
row_limit: row_limit, row_limit: row_limit,
more_rows: more_rows, more_rows: more_rows,
finished: finished, finished: finished,
_trans: trans _trans: trans,
} }
} }
} }
@ -327,11 +332,11 @@ impl<'a, 'b> Drop for LazyRows<'a, 'b> {
impl<'a, 'b> fmt::Debug for LazyRows<'a, 'b> { impl<'a, 'b> fmt::Debug for LazyRows<'a, 'b> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("LazyRows") fmt.debug_struct("LazyRows")
.field("name", &self.name) .field("name", &self.name)
.field("row_limit", &self.row_limit) .field("row_limit", &self.row_limit)
.field("remaining_rows", &self.data.len()) .field("remaining_rows", &self.data.len())
.field("more_rows", &self.more_rows) .field("more_rows", &self.more_rows)
.finish() .finish()
} }
} }
@ -345,12 +350,11 @@ impl<'trans, 'stmt> LazyRows<'trans, 'stmt> {
fn execute(&mut self) -> Result<()> { fn execute(&mut self) -> Result<()> {
let mut conn = self.stmt.conn().conn.borrow_mut(); let mut conn = self.stmt.conn().conn.borrow_mut();
try!(conn.write_messages(&[ try!(conn.write_messages(&[Execute {
Execute { portal: &self.name,
portal: &self.name, max_rows: self.row_limit,
max_rows: self.row_limit },
}, Sync]));
Sync]));
read_rows(&mut conn, &mut self.data).map(|more_rows| self.more_rows = more_rows) read_rows(&mut conn, &mut self.data).map(|more_rows| self.more_rows = more_rows)
} }

View File

@ -28,10 +28,10 @@ pub struct Statement<'conn> {
impl<'a> fmt::Debug for Statement<'a> { impl<'a> fmt::Debug for Statement<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Statement") fmt.debug_struct("Statement")
.field("name", &self.name) .field("name", &self.name)
.field("parameter_types", &self.param_types) .field("parameter_types", &self.param_types)
.field("columns", &self.columns) .field("columns", &self.columns)
.finish() .finish()
} }
} }
@ -47,7 +47,8 @@ impl<'conn> StatementInternals<'conn> for Statement<'conn> {
param_types: Vec<Type>, param_types: Vec<Type>,
columns: Vec<Column>, columns: Vec<Column>,
next_portal_id: Cell<u32>, next_portal_id: Cell<u32>,
finished: bool) -> Statement<'conn> { finished: bool)
-> Statement<'conn> {
Statement { Statement {
conn: conn, conn: conn,
name: name, name: name,
@ -81,7 +82,9 @@ impl<'conn> Statement<'conn> {
"expected {} parameters but got {}", "expected {} parameters but got {}",
self.param_types.len(), self.param_types.len(),
params.len()); params.len());
debug!("executing statement {} with parameters: {:?}", self.name, params); debug!("executing statement {} with parameters: {:?}",
self.name,
params);
let mut values = vec![]; let mut values = vec![];
for (param, ty) in params.iter().zip(self.param_types.iter()) { for (param, ty) in params.iter().zip(self.param_types.iter()) {
let mut buf = vec![]; let mut buf = vec![];
@ -89,21 +92,20 @@ impl<'conn> Statement<'conn> {
IsNull::Yes => values.push(None), IsNull::Yes => values.push(None),
IsNull::No => values.push(Some(buf)), IsNull::No => values.push(Some(buf)),
} }
}; }
try!(conn.write_messages(&[ try!(conn.write_messages(&[Bind {
Bind { portal: portal_name,
portal: portal_name, statement: &self.name,
statement: &self.name, formats: &[1],
formats: &[1], values: &values,
values: &values, result_formats: &[1],
result_formats: &[1] },
}, Execute {
Execute { portal: portal_name,
portal: portal_name, max_rows: row_limit,
max_rows: row_limit },
}, Sync]));
Sync]));
match try!(conn.read_message()) { match try!(conn.read_message()) {
BindComplete => Ok(()), BindComplete => Ok(()),
@ -118,7 +120,10 @@ impl<'conn> Statement<'conn> {
} }
} }
fn inner_query<'a>(&'a self, portal_name: &str, row_limit: i32, params: &[&ToSql]) fn inner_query<'a>(&'a self,
portal_name: &str,
row_limit: i32,
params: &[&ToSql])
-> Result<(VecDeque<Vec<Option<Vec<u8>>>>, bool)> { -> Result<(VecDeque<Vec<Option<Vec<u8>>>>, bool)> {
try!(self.inner_execute(portal_name, row_limit, params)); try!(self.inner_execute(portal_name, row_limit, params));
@ -141,12 +146,12 @@ impl<'conn> Statement<'conn> {
/// ///
/// If the statement does not modify any rows (e.g. SELECT), 0 is returned. /// If the statement does not modify any rows (e.g. SELECT), 0 is returned.
/// ///
/// ## Panics /// # Panics
/// ///
/// Panics if the number of parameters provided does not match the number /// Panics if the number of parameters provided does not match the number
/// expected. /// expected.
/// ///
/// ## Example /// # Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use postgres::{Connection, SslMode}; /// # use postgres::{Connection, SslMode};
@ -154,10 +159,8 @@ impl<'conn> Statement<'conn> {
/// # let bar = 1i32; /// # let bar = 1i32;
/// # let baz = true; /// # let baz = true;
/// let stmt = conn.prepare("UPDATE foo SET bar = $1 WHERE baz = $2").unwrap(); /// let stmt = conn.prepare("UPDATE foo SET bar = $1 WHERE baz = $2").unwrap();
/// match stmt.execute(&[&bar, &baz]) { /// let rows_updated = stmt.execute(&[&bar, &baz]).unwrap();
/// Ok(count) => println!("{} row(s) updated", count), /// println!("{} rows updated", rows_updated);
/// Err(err) => println!("Error executing query: {:?}", err)
/// }
/// ``` /// ```
pub fn execute(&self, params: &[&ToSql]) -> Result<u64> { pub fn execute(&self, params: &[&ToSql]) -> Result<u64> {
check_desync!(self.conn); check_desync!(self.conn);
@ -181,11 +184,11 @@ impl<'conn> Statement<'conn> {
break; break;
} }
CopyInResponse { .. } => { CopyInResponse { .. } => {
try!(conn.write_messages(&[ try!(conn.write_messages(&[CopyFail {
CopyFail { message: "COPY queries cannot be directly \
message: "COPY queries cannot be directly executed", executed",
}, },
Sync])); Sync]));
} }
CopyOutResponse { .. } => { CopyOutResponse { .. } => {
loop { loop {
@ -214,32 +217,26 @@ impl<'conn> Statement<'conn> {
/// Executes the prepared statement, returning the resulting rows. /// Executes the prepared statement, returning the resulting rows.
/// ///
/// ## Panics /// # Panics
/// ///
/// Panics if the number of parameters provided does not match the number /// Panics if the number of parameters provided does not match the number
/// expected. /// expected.
/// ///
/// ## Example /// # Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use postgres::{Connection, SslMode}; /// # use postgres::{Connection, SslMode};
/// # let conn = Connection::connect("", &SslMode::None).unwrap(); /// # let conn = Connection::connect("", &SslMode::None).unwrap();
/// let stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1").unwrap(); /// let stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1").unwrap();
/// # let baz = true; /// # let baz = true;
/// let rows = match stmt.query(&[&baz]) { /// for row in stmt.query(&[&baz]).unwrap() {
/// Ok(rows) => rows,
/// Err(err) => panic!("Error running query: {:?}", err)
/// };
/// for row in &rows {
/// let foo: i32 = row.get("foo"); /// let foo: i32 = row.get("foo");
/// println!("foo: {}", foo); /// println!("foo: {}", foo);
/// } /// }
/// ``` /// ```
pub fn query<'a>(&'a self, params: &[&ToSql]) -> Result<Rows<'a>> { pub fn query<'a>(&'a self, params: &[&ToSql]) -> Result<Rows<'a>> {
check_desync!(self.conn); check_desync!(self.conn);
self.inner_query("", 0, params).map(|(buf, _)| { self.inner_query("", 0, params).map(|(buf, _)| Rows::new(self, buf.into_iter().collect()))
Rows::new(self, buf.into_iter().collect())
})
} }
/// Executes the prepared statement, returning a lazily loaded iterator /// Executes the prepared statement, returning a lazily loaded iterator
@ -254,7 +251,7 @@ impl<'conn> Statement<'conn> {
/// object representing the active transaction must be passed to /// object representing the active transaction must be passed to
/// `lazy_query`. /// `lazy_query`.
/// ///
/// ## Panics /// # Panics
/// ///
/// Panics if the provided `Transaction` is not associated with the same /// Panics if the provided `Transaction` is not associated with the same
/// `Connection` as this `Statement`, if the `Transaction` is not /// `Connection` as this `Statement`, if the `Transaction` is not
@ -318,9 +315,10 @@ impl<'conn> Statement<'conn> {
loop { loop {
match try!(conn.read_message()) { match try!(conn.read_message()) {
ReadyForQuery { .. } => { ReadyForQuery { .. } => {
return Err(Error::IoError(io::Error::new( return Err(Error::IoError(io::Error::new(io::ErrorKind::InvalidInput,
io::ErrorKind::InvalidInput, "called `copy_in` on a \
"called `copy_in` on a non-`COPY FROM STDIN` statement"))); non-`COPY FROM STDIN` \
statement")));
} }
_ => {} _ => {}
} }
@ -339,20 +337,15 @@ impl<'conn> Statement<'conn> {
match fill_copy_buf(&mut buf, r, &info) { match fill_copy_buf(&mut buf, r, &info) {
Ok(0) => break, Ok(0) => break,
Ok(len) => { Ok(len) => {
try_desync!(info.conn, info.conn.stream.write_message( try_desync!(info.conn,
&CopyData { info.conn.stream.write_message(&CopyData { data: &buf[..len] }));
data: &buf[..len],
}));
} }
Err(err) => { Err(err) => {
try!(info.conn.write_messages(&[ try!(info.conn.write_messages(&[CopyFail { message: "" }, CopyDone, Sync]));
CopyFail {
message: "",
},
CopyDone,
Sync]));
match try!(info.conn.read_message()) { match try!(info.conn.read_message()) {
ErrorResponse { .. } => { /* expected from the CopyFail */ } ErrorResponse { .. } => {
// expected from the CopyFail
}
_ => { _ => {
info.conn.desynchronized = true; info.conn.desynchronized = true;
return Err(Error::IoError(bad_response())); return Err(Error::IoError(bad_response()));
@ -401,7 +394,7 @@ impl<'conn> Statement<'conn> {
/// INSERT INTO people (id, name) VALUES (1, 'john'), (2, 'jane');").unwrap(); /// INSERT INTO people (id, name) VALUES (1, 'john'), (2, 'jane');").unwrap();
/// let stmt = conn.prepare("COPY people TO STDOUT").unwrap(); /// let stmt = conn.prepare("COPY people TO STDOUT").unwrap();
/// let mut buf = vec![]; /// let mut buf = vec![];
/// let mut r = stmt.copy_out(&[], &mut buf).unwrap(); /// stmt.copy_out(&[], &mut buf).unwrap();
/// assert_eq!(buf, b"1\tjohn\n2\tjane\n"); /// assert_eq!(buf, b"1\tjohn\n2\tjane\n");
/// ``` /// ```
pub fn copy_out<'a, W: WriteWithInfo>(&'a self, params: &[&ToSql], w: &mut W) -> Result<u64> { pub fn copy_out<'a, W: WriteWithInfo>(&'a self, params: &[&ToSql], w: &mut W) -> Result<u64> {
@ -411,23 +404,20 @@ impl<'conn> Statement<'conn> {
let (format, column_formats) = match try!(conn.read_message()) { let (format, column_formats) = match try!(conn.read_message()) {
CopyOutResponse { format, column_formats } => (format, column_formats), CopyOutResponse { format, column_formats } => (format, column_formats),
CopyInResponse { .. } => { CopyInResponse { .. } => {
try!(conn.write_messages(&[ try!(conn.write_messages(&[CopyFail { message: "" }, CopyDone, Sync]));
CopyFail {
message: "",
},
CopyDone,
Sync]));
match try!(conn.read_message()) { match try!(conn.read_message()) {
ErrorResponse { .. } => { /* expected from the CopyFail */ } ErrorResponse { .. } => {
// expected from the CopyFail
}
_ => { _ => {
conn.desynchronized = true; conn.desynchronized = true;
return Err(Error::IoError(bad_response())); return Err(Error::IoError(bad_response()));
} }
} }
try!(conn.wait_for_ready()); try!(conn.wait_for_ready());
return Err(Error::IoError(io::Error::new( return Err(Error::IoError(io::Error::new(io::ErrorKind::InvalidInput,
io::ErrorKind::InvalidInput, "called `copy_out` on a non-`COPY TO \
"called `copy_out` on a non-`COPY TO STDOUT` statement"))); STDOUT` statement")));
} }
ErrorResponse { fields } => { ErrorResponse { fields } => {
try!(conn.wait_for_ready()); try!(conn.wait_for_ready());
@ -437,9 +427,10 @@ impl<'conn> Statement<'conn> {
loop { loop {
match try!(conn.read_message()) { match try!(conn.read_message()) {
ReadyForQuery { .. } => { ReadyForQuery { .. } => {
return Err(Error::IoError(io::Error::new( return Err(Error::IoError(io::Error::new(io::ErrorKind::InvalidInput,
io::ErrorKind::InvalidInput, "called `copy_out` on a \
"called `copy_out` on a non-`COPY TO STDOUT` statement"))); non-`COPY TO STDOUT` \
statement")));
} }
_ => {} _ => {}
} }
@ -472,7 +463,7 @@ impl<'conn> Statement<'conn> {
} }
} }
} }
BCopyDone => {}, BCopyDone => {}
CommandComplete { tag } => { CommandComplete { tag } => {
count = util::parse_update_count(tag); count = util::parse_update_count(tag);
break; break;
@ -512,8 +503,7 @@ impl<'conn> Statement<'conn> {
} }
} }
fn fill_copy_buf<R: ReadWithInfo>(buf: &mut [u8], r: &mut R, info: &CopyInfo) fn fill_copy_buf<R: ReadWithInfo>(buf: &mut [u8], r: &mut R, info: &CopyInfo) -> io::Result<usize> {
-> io::Result<usize> {
let mut nread = 0; let mut nread = 0;
while nread < buf.len() { while nread < buf.len() {
match r.read_with_info(&mut buf[nread..], info) { match r.read_with_info(&mut buf[nread..], info) {
@ -530,7 +520,7 @@ fn fill_copy_buf<R: ReadWithInfo>(buf: &mut [u8], r: &mut R, info: &CopyInfo)
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct Column { pub struct Column {
name: String, name: String,
type_: Type type_: Type,
} }
impl ColumnNew for Column { impl ColumnNew for Column {

View File

@ -3,7 +3,8 @@ extern crate chrono;
use std::error; use std::error;
use std::io::prelude::*; use std::io::prelude::*;
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
use self::chrono::{Duration, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC, Local, FixedOffset}; use self::chrono::{Duration, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC, Local,
FixedOffset};
use Result; use Result;
use error::Error; use error::Error;
@ -23,11 +24,15 @@ impl FromSql for NaiveDateTime {
} }
impl ToSql for NaiveDateTime { impl ToSql for NaiveDateTime {
fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) -> Result<IsNull> { fn to_sql<W: Write + ?Sized>(&self,
_: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
let time = match (*self - base()).num_microseconds() { let time = match (*self - base()).num_microseconds() {
Some(time) => time, Some(time) => time,
None => { None => {
let err: Box<error::Error+Sync+Send> = "value too large to transmit".into(); let err: Box<error::Error + Sync + Send> = "value too large to transmit".into();
return Err(Error::Conversion(err)); return Err(Error::Conversion(err));
} }
}; };
@ -49,8 +54,11 @@ impl FromSql for DateTime<UTC> {
} }
impl ToSql for DateTime<UTC> { impl ToSql for DateTime<UTC> {
fn to_sql<W: Write+?Sized>(&self, type_: &Type, mut w: &mut W, info: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self,
-> Result<IsNull> { type_: &Type,
mut w: &mut W,
info: &SessionInfo)
-> Result<IsNull> {
self.naive_utc().to_sql(type_, w, info) self.naive_utc().to_sql(type_, w, info)
} }
@ -68,8 +76,11 @@ impl FromSql for DateTime<Local> {
} }
impl ToSql for DateTime<Local> { impl ToSql for DateTime<Local> {
fn to_sql<W: Write+?Sized>(&self, type_: &Type, mut w: &mut W, info: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self,
-> Result<IsNull> { type_: &Type,
mut w: &mut W,
info: &SessionInfo)
-> Result<IsNull> {
self.with_timezone(&UTC).to_sql(type_, w, info) self.with_timezone(&UTC).to_sql(type_, w, info)
} }
@ -78,7 +89,9 @@ impl ToSql for DateTime<Local> {
} }
impl FromSql for DateTime<FixedOffset> { impl FromSql for DateTime<FixedOffset> {
fn from_sql<R: Read>(type_: &Type, raw: &mut R, info: &SessionInfo) fn from_sql<R: Read>(type_: &Type,
raw: &mut R,
info: &SessionInfo)
-> Result<DateTime<FixedOffset>> { -> Result<DateTime<FixedOffset>> {
let utc = try!(DateTime::<UTC>::from_sql(type_, raw, info)); let utc = try!(DateTime::<UTC>::from_sql(type_, raw, info));
Ok(utc.with_timezone(&FixedOffset::east(0))) Ok(utc.with_timezone(&FixedOffset::east(0)))
@ -88,8 +101,11 @@ impl FromSql for DateTime<FixedOffset> {
} }
impl ToSql for DateTime<FixedOffset> { impl ToSql for DateTime<FixedOffset> {
fn to_sql<W: Write+?Sized>(&self, type_: &Type, mut w: &mut W, info: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self,
-> Result<IsNull> { type_: &Type,
mut w: &mut W,
info: &SessionInfo)
-> Result<IsNull> {
self.with_timezone(&UTC).to_sql(type_, w, info) self.with_timezone(&UTC).to_sql(type_, w, info)
} }
@ -107,10 +123,14 @@ impl FromSql for NaiveDate {
} }
impl ToSql for NaiveDate { impl ToSql for NaiveDate {
fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) -> Result<IsNull> { fn to_sql<W: Write + ?Sized>(&self,
_: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
let jd = (*self - base().date()).num_days(); let jd = (*self - base().date()).num_days();
if jd > i32::max_value() as i64 || jd < i32::min_value() as i64 { if jd > i32::max_value() as i64 || jd < i32::min_value() as i64 {
let err: Box<error::Error+Sync+Send> = "value too large to transmit".into(); let err: Box<error::Error + Sync + Send> = "value too large to transmit".into();
return Err(Error::Conversion(err)); return Err(Error::Conversion(err));
} }
@ -132,12 +152,16 @@ impl FromSql for NaiveTime {
} }
impl ToSql for NaiveTime { impl ToSql for NaiveTime {
fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) -> Result<IsNull> { fn to_sql<W: Write + ?Sized>(&self,
_: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
let delta = *self - NaiveTime::from_hms(0, 0, 0); let delta = *self - NaiveTime::from_hms(0, 0, 0);
let time = match delta.num_microseconds() { let time = match delta.num_microseconds() {
Some(time) => time, Some(time) => time,
None => { None => {
let err: Box<error::Error+Sync+Send> = "value too large to transmit".into(); let err: Box<error::Error + Sync + Send> = "value too large to transmit".into();
return Err(Error::Conversion(err)); return Err(Error::Conversion(err));
} }
}; };

View File

@ -37,7 +37,7 @@ macro_rules! to_sql_checked {
if !<Self as $crate::types::ToSql>::accepts(ty) { if !<Self as $crate::types::ToSql>::accepts(ty) {
return Err($crate::error::Error::WrongType(ty.clone())); return Err($crate::error::Error::WrongType(ty.clone()));
} }
self.to_sql(ty, out, ctx) $crate::types::ToSql::to_sql(self, ty, out, ctx)
} }
} }
} }
@ -63,9 +63,7 @@ pub struct SessionInfo<'a> {
impl<'a> SessionInfoNew<'a> for SessionInfo<'a> { impl<'a> SessionInfoNew<'a> for SessionInfo<'a> {
fn new(conn: &'a InnerConnection) -> SessionInfo<'a> { fn new(conn: &'a InnerConnection) -> SessionInfo<'a> {
SessionInfo { SessionInfo { conn: conn }
conn: conn
}
} }
} }
@ -176,317 +174,317 @@ macro_rules! make_postgres_type {
// Values from pg_type.h // Values from pg_type.h
make_postgres_type! { make_postgres_type! {
#[doc="BOOL - boolean, 'true'/'false'"] /// BOOL - boolean, 'true'/'false'
16: "bool" => Bool: Kind::Simple, 16: "bool" => Bool: Kind::Simple,
#[doc="BYTEA - variable-length string, binary values escaped"] /// BYTEA - variable-length string, binary values escaped
17: "bytea" => Bytea: Kind::Simple, 17: "bytea" => Bytea: Kind::Simple,
#[doc="\"char\" - single character"] /// "char" - single character
18: "char" => Char: Kind::Simple, 18: "char" => Char: Kind::Simple,
#[doc="NAME - 63-byte type for storing system identifiers"] /// NAME - 63-byte type for storing system identifiers
19: "name" => Name: Kind::Simple, 19: "name" => Name: Kind::Simple,
#[doc="INT8/BIGINT - ~18 digit integer, 8-byte storage"] /// INT8/BIGINT - ~18 digit integer, 8-byte storage
20: "int8" => Int8: Kind::Simple, 20: "int8" => Int8: Kind::Simple,
#[doc="INT2/SMALLINT - -32 thousand to 32 thousand, 2-byte storage"] /// INT2/SMALLINT - -32 thousand to 32 thousand, 2-byte storage
21: "int2" => Int2: Kind::Simple, 21: "int2" => Int2: Kind::Simple,
#[doc="INT2VECTOR - array of int2, used in system tables"] /// INT2VECTOR - array of int2, used in system tables
22: "int2vector" => Int2Vector: Kind::Array(Type::Int2), 22: "int2vector" => Int2Vector: Kind::Array(Type::Int2),
#[doc="INT4/INT - -2 billion to 2 billion integer, 4-byte storage"] /// INT4/INT - -2 billion to 2 billion integer, 4-byte storage
23: "int4" => Int4: Kind::Simple, 23: "int4" => Int4: Kind::Simple,
#[doc="REGPROC - registered procedure"] /// REGPROC - registered procedure
24: "regproc" => Regproc: Kind::Simple, 24: "regproc" => Regproc: Kind::Simple,
#[doc="TEXT - variable-length string, no limit specified"] /// TEXT - variable-length string, no limit specified
25: "text" => Text: Kind::Simple, 25: "text" => Text: Kind::Simple,
#[doc="OID - object identifier(oid), maximum 4 billion"] /// OID - object identifier(oid), maximum 4 billion
26: "oid" => Oid: Kind::Simple, 26: "oid" => Oid: Kind::Simple,
#[doc="TID - (block, offset), physical location of tuple"] /// TID - (block, offset), physical location of tuple
27: "tid" => Tid: Kind::Simple, 27: "tid" => Tid: Kind::Simple,
#[doc="XID - transaction id"] /// XID - transaction id
28: "xid" => Xid: Kind::Simple, 28: "xid" => Xid: Kind::Simple,
#[doc="CID - command identifier type, sequence in transaction id"] /// CID - command identifier type, sequence in transaction id
29: "cid" => Cid: Kind::Simple, 29: "cid" => Cid: Kind::Simple,
#[doc="OIDVECTOR - array of oids, used in system tables"] /// OIDVECTOR - array of oids, used in system tables
30: "oidvector" => OidVector: Kind::Array(Type::Oid), 30: "oidvector" => OidVector: Kind::Array(Type::Oid),
#[doc="PG_TYPE"] /// PG_TYPE
71: "pg_type" => PgType: Kind::Simple, 71: "pg_type" => PgType: Kind::Simple,
#[doc="PG_ATTRIBUTE"] /// PG_ATTRIBUTE
75: "pg_attribute" => PgAttribute: Kind::Simple, 75: "pg_attribute" => PgAttribute: Kind::Simple,
#[doc="PG_PROC"] /// PG_PROC
81: "pg_proc" => PgProc: Kind::Simple, 81: "pg_proc" => PgProc: Kind::Simple,
#[doc="PG_CLASS"] /// PG_CLASS
83: "pg_class" => PgClass: Kind::Simple, 83: "pg_class" => PgClass: Kind::Simple,
#[doc="JSON"] /// JSON
114: "json" => Json: Kind::Simple, 114: "json" => Json: Kind::Simple,
#[doc="XML - XML content"] /// XML - XML content
142: "xml" => Xml: Kind::Simple, 142: "xml" => Xml: Kind::Simple,
#[doc="XML[]"] /// XML[]
143: "_xml" => XmlArray: Kind::Array(Type::Xml), 143: "_xml" => XmlArray: Kind::Array(Type::Xml),
#[doc="PG_NODE_TREE - string representing an internal node tree"] /// PG_NODE_TREE - string representing an internal node tree
194: "pg_node_tree" => PgNodeTree: Kind::Simple, 194: "pg_node_tree" => PgNodeTree: Kind::Simple,
#[doc="JSON[]"] /// JSON[]
199: "_json" => JsonArray: Kind::Array(Type::Json), 199: "_json" => JsonArray: Kind::Array(Type::Json),
#[doc="SMGR - storage manager"] /// SMGR - storage manager
210: "smgr" => Smgr: Kind::Simple, 210: "smgr" => Smgr: Kind::Simple,
#[doc="POINT - geometric point '(x, y)'"] /// POINT - geometric point '(x, y)'
600: "point" => Point: Kind::Simple, 600: "point" => Point: Kind::Simple,
#[doc="LSEG - geometric line segment '(pt1,pt2)'"] /// LSEG - geometric line segment '(pt1,pt2)'
601: "lseg" => Lseg: Kind::Simple, 601: "lseg" => Lseg: Kind::Simple,
#[doc="PATH - geometric path '(pt1,...)'"] /// PATH - geometric path '(pt1,...)'
602: "path" => Path: Kind::Simple, 602: "path" => Path: Kind::Simple,
#[doc="BOX - geometric box '(lower left,upper right)'"] /// BOX - geometric box '(lower left,upper right)'
603: "box" => Box: Kind::Simple, 603: "box" => Box: Kind::Simple,
#[doc="POLYGON - geometric polygon '(pt1,...)'"] /// POLYGON - geometric polygon '(pt1,...)'
604: "polygon" => Polygon: Kind::Simple, 604: "polygon" => Polygon: Kind::Simple,
#[doc="LINE - geometric line"] /// LINE - geometric line
628: "line" => Line: Kind::Simple, 628: "line" => Line: Kind::Simple,
#[doc="LINE[]"] /// LINE[]
629: "_line" => LineArray: Kind::Array(Type::Line), 629: "_line" => LineArray: Kind::Array(Type::Line),
#[doc="CIDR - network IP address/netmask, network address"] /// CIDR - network IP address/netmask, network address
650: "cidr" => Cidr: Kind::Simple, 650: "cidr" => Cidr: Kind::Simple,
#[doc="CIDR[]"] /// CIDR[]
651: "_cidr" => CidrArray: Kind::Array(Type::Cidr), 651: "_cidr" => CidrArray: Kind::Array(Type::Cidr),
#[doc="FLOAT4/REAL - single-precision floating point number, 4-byte storage"] /// FLOAT4/REAL - single-precision floating point number, 4-byte storage
700: "float4" => Float4: Kind::Simple, 700: "float4" => Float4: Kind::Simple,
#[doc="FLOAT8/DOUBLE PRECISION - double-precision floating point number, 8-byte storage"] /// FLOAT8/DOUBLE PRECISION - double-precision floating point number, 8-byte storage
701: "float8" => Float8: Kind::Simple, 701: "float8" => Float8: Kind::Simple,
#[doc="ABSTIME - absolute, limited-range date and time (Unix system time)"] /// ABSTIME - absolute, limited-range date and time (Unix system time)
702: "abstime" => Abstime: Kind::Simple, 702: "abstime" => Abstime: Kind::Simple,
#[doc="RELTIME - relative, limited-range date and time (Unix delta time)"] /// RELTIME - relative, limited-range date and time (Unix delta time)
703: "reltime" => Reltime: Kind::Simple, 703: "reltime" => Reltime: Kind::Simple,
#[doc="TINTERVAL - (abstime,abstime), time interval"] /// TINTERVAL - (abstime,abstime), time interval
704: "tinterval" => Tinterval: Kind::Simple, 704: "tinterval" => Tinterval: Kind::Simple,
#[doc="UNKNOWN"] /// UNKNOWN
705: "unknown" => Unknown: Kind::Simple, 705: "unknown" => Unknown: Kind::Simple,
#[doc="CIRCLE - geometric circle '(center,radius)'"] /// CIRCLE - geometric circle '(center,radius)'
718: "circle" => Circle: Kind::Simple, 718: "circle" => Circle: Kind::Simple,
#[doc="CIRCLE[]"] /// CIRCLE[]
719: "_circle" => CircleArray: Kind::Array(Type::Circle), 719: "_circle" => CircleArray: Kind::Array(Type::Circle),
#[doc="MONEY - monetary amounts, $d,ddd.cc"] /// MONEY - monetary amounts, $d,ddd.cc
790: "money" => Money: Kind::Simple, 790: "money" => Money: Kind::Simple,
#[doc="MONEY[]"] /// MONEY[]
791: "_money" => MoneyArray: Kind::Array(Type::Money), 791: "_money" => MoneyArray: Kind::Array(Type::Money),
#[doc="MACADDR - XX:XX:XX:XX:XX:XX, MAC address"] /// MACADDR - XX:XX:XX:XX:XX:XX, MAC address
829: "macaddr" => Macaddr: Kind::Simple, 829: "macaddr" => Macaddr: Kind::Simple,
#[doc="INET - IP address/netmask, host address, netmask optional"] /// INET - IP address/netmask, host address, netmask optional
869: "inet" => Inet: Kind::Simple, 869: "inet" => Inet: Kind::Simple,
#[doc="BOOL[]"] /// BOOL[]
1000: "_bool" => BoolArray: Kind::Array(Type::Bool), 1000: "_bool" => BoolArray: Kind::Array(Type::Bool),
#[doc="BYTEA[]"] /// BYTEA[]
1001: "_bytea" => ByteaArray: Kind::Array(Type::Bytea), 1001: "_bytea" => ByteaArray: Kind::Array(Type::Bytea),
#[doc="\"char\"[]"] /// "char"[]
1002: "_char" => CharArray: Kind::Array(Type::Char), 1002: "_char" => CharArray: Kind::Array(Type::Char),
#[doc="NAME[]"] /// NAME[]
1003: "_name" => NameArray: Kind::Array(Type::Name), 1003: "_name" => NameArray: Kind::Array(Type::Name),
#[doc="INT2[]"] /// INT2[]
1005: "_int2" => Int2Array: Kind::Array(Type::Int2), 1005: "_int2" => Int2Array: Kind::Array(Type::Int2),
#[doc="INT2VECTOR[]"] /// INT2VECTOR[]
1006: "_int2vector" => Int2VectorArray: Kind::Array(Type::Int2Vector), 1006: "_int2vector" => Int2VectorArray: Kind::Array(Type::Int2Vector),
#[doc="INT4[]"] /// INT4[]
1007: "_int4" => Int4Array: Kind::Array(Type::Int4), 1007: "_int4" => Int4Array: Kind::Array(Type::Int4),
#[doc="REGPROC[]"] /// REGPROC[]
1008: "_regproc" => RegprocArray: Kind::Array(Type::Regproc), 1008: "_regproc" => RegprocArray: Kind::Array(Type::Regproc),
#[doc="TEXT[]"] /// TEXT[]
1009: "_text" => TextArray: Kind::Array(Type::Text), 1009: "_text" => TextArray: Kind::Array(Type::Text),
#[doc="TID[]"] /// TID[]
1010: "_tid" => TidArray: Kind::Array(Type::Tid), 1010: "_tid" => TidArray: Kind::Array(Type::Tid),
#[doc="XID[]"] /// XID[]
1011: "_xid" => XidArray: Kind::Array(Type::Xid), 1011: "_xid" => XidArray: Kind::Array(Type::Xid),
#[doc="CID[]"] /// CID[]
1012: "_cid" => CidArray: Kind::Array(Type::Cid), 1012: "_cid" => CidArray: Kind::Array(Type::Cid),
#[doc="OIDVECTOR[]"] /// OIDVECTOR[]
1013: "_oidvector" => OidVectorArray: Kind::Array(Type::OidVector), 1013: "_oidvector" => OidVectorArray: Kind::Array(Type::OidVector),
#[doc="BPCHAR[]"] /// BPCHAR[]
1014: "_bpchar" => BpcharArray: Kind::Array(Type::Bpchar), 1014: "_bpchar" => BpcharArray: Kind::Array(Type::Bpchar),
#[doc="VARCHAR[]"] /// VARCHAR[]
1015: "_varchar" => VarcharArray: Kind::Array(Type::Varchar), 1015: "_varchar" => VarcharArray: Kind::Array(Type::Varchar),
#[doc="INT8[]"] /// INT8[]
1016: "_int8" => Int8Array: Kind::Array(Type::Int8), 1016: "_int8" => Int8Array: Kind::Array(Type::Int8),
#[doc="POINT[]"] /// POINT[]
1017: "_point" => PointArray: Kind::Array(Type::Point), 1017: "_point" => PointArray: Kind::Array(Type::Point),
#[doc="LSEG[]"] /// LSEG[]
1018: "_lseg" => LsegArray: Kind::Array(Type::Lseg), 1018: "_lseg" => LsegArray: Kind::Array(Type::Lseg),
#[doc="PATH[]"] /// PATH[]
1019: "_path" => PathArray: Kind::Array(Type::Path), 1019: "_path" => PathArray: Kind::Array(Type::Path),
#[doc="BOX[]"] /// BOX[]
1020: "_box" => BoxArray: Kind::Array(Type::Box), 1020: "_box" => BoxArray: Kind::Array(Type::Box),
#[doc="FLOAT4[]"] /// FLOAT4[]
1021: "_float4" => Float4Array: Kind::Array(Type::Float4), 1021: "_float4" => Float4Array: Kind::Array(Type::Float4),
#[doc="FLOAT8[]"] /// FLOAT8[]
1022: "_float8" => Float8Array: Kind::Array(Type::Float8), 1022: "_float8" => Float8Array: Kind::Array(Type::Float8),
#[doc="ABSTIME[]"] /// ABSTIME[]
1023: "_abstime" => AbstimeArray: Kind::Array(Type::Abstime), 1023: "_abstime" => AbstimeArray: Kind::Array(Type::Abstime),
#[doc="RELTIME[]"] /// RELTIME[]
1024: "_reltime" => ReltimeArray: Kind::Array(Type::Reltime), 1024: "_reltime" => ReltimeArray: Kind::Array(Type::Reltime),
#[doc="TINTERVAL[]"] /// TINTERVAL[]
1025: "_tinterval" => TintervalArray: Kind::Array(Type::Tinterval), 1025: "_tinterval" => TintervalArray: Kind::Array(Type::Tinterval),
#[doc="POLYGON[]"] /// POLYGON[]
1027: "_polygon" => PolygonArray: Kind::Array(Type::Polygon), 1027: "_polygon" => PolygonArray: Kind::Array(Type::Polygon),
#[doc="OID[]"] /// OID[]
1028: "_oid" => OidArray: Kind::Array(Type::Oid), 1028: "_oid" => OidArray: Kind::Array(Type::Oid),
#[doc="ACLITEM - access control list"] /// ACLITEM - access control list
1033: "aclitem" => Aclitem: Kind::Simple, 1033: "aclitem" => Aclitem: Kind::Simple,
#[doc="ACLITEM[]"] /// ACLITEM[]
1034: "_aclitem" => AclitemArray: Kind::Array(Type::Aclitem), 1034: "_aclitem" => AclitemArray: Kind::Array(Type::Aclitem),
#[doc="MACADDR[]"] /// MACADDR[]
1040: "_macaddr" => MacaddrArray: Kind::Array(Type::Macaddr), 1040: "_macaddr" => MacaddrArray: Kind::Array(Type::Macaddr),
#[doc="INET[]"] /// INET[]
1041: "_inet" => InetArray: Kind::Array(Type::Inet), 1041: "_inet" => InetArray: Kind::Array(Type::Inet),
#[doc="BPCHAR - char(length), blank-padded string, fixed storage length"] /// BPCHAR - char(length), blank-padded string, fixed storage length
1042: "bpchar" => Bpchar: Kind::Simple, 1042: "bpchar" => Bpchar: Kind::Simple,
#[doc="VARCHAR - varchar(length), non-blank-padded string, variable storage length"] /// VARCHAR - varchar(length), non-blank-padded string, variable storage length
1043: "varchar" => Varchar: Kind::Simple, 1043: "varchar" => Varchar: Kind::Simple,
#[doc="DATE - date"] /// DATE - date
1082: "date" => Date: Kind::Simple, 1082: "date" => Date: Kind::Simple,
#[doc="TIME - time of day"] /// TIME - time of day
1083: "time" => Time: Kind::Simple, 1083: "time" => Time: Kind::Simple,
#[doc="TIMESTAMP - date and time"] /// TIMESTAMP - date and time
1114: "timestamp" => Timestamp: Kind::Simple, 1114: "timestamp" => Timestamp: Kind::Simple,
#[doc="TIMESTAMP[]"] /// TIMESTAMP[]
1115: "_timestamp" => TimestampArray: Kind::Array(Type::Timestamp), 1115: "_timestamp" => TimestampArray: Kind::Array(Type::Timestamp),
#[doc="DATE[]"] /// DATE[]
1182: "_date" => DateArray: Kind::Array(Type::Date), 1182: "_date" => DateArray: Kind::Array(Type::Date),
#[doc="TIME[]"] /// TIME[]
1183: "_time" => TimeArray: Kind::Array(Type::Time), 1183: "_time" => TimeArray: Kind::Array(Type::Time),
#[doc="TIMESTAMPTZ - date and time with time zone"] /// TIMESTAMPTZ - date and time with time zone
1184: "timestamptz" => TimestampTZ: Kind::Simple, 1184: "timestamptz" => TimestampTZ: Kind::Simple,
#[doc="TIMESTAMPTZ[]"] /// TIMESTAMPTZ[]
1185: "_timestamptz" => TimestampTZArray: Kind::Array(Type::TimestampTZ), 1185: "_timestamptz" => TimestampTZArray: Kind::Array(Type::TimestampTZ),
#[doc="INTERVAL - @ &lt;number&gt; &lt;units&gt;, time interval"] /// INTERVAL - @ &lt;number&gt; &lt;units&gt;, time interval
1186: "interval" => Interval: Kind::Simple, 1186: "interval" => Interval: Kind::Simple,
#[doc="INTERVAL[]"] /// INTERVAL[]
1187: "_interval" => IntervalArray: Kind::Array(Type::Interval), 1187: "_interval" => IntervalArray: Kind::Array(Type::Interval),
#[doc="NUMERIC[]"] /// NUMERIC[]
1231: "_numeric" => NumericArray: Kind::Array(Type::Numeric), 1231: "_numeric" => NumericArray: Kind::Array(Type::Numeric),
#[doc="CSTRING[]"] /// CSTRING[]
1263: "_cstring" => CstringArray: Kind::Array(Type::Cstring), 1263: "_cstring" => CstringArray: Kind::Array(Type::Cstring),
#[doc="TIMETZ - time of day with time zone"] /// TIMETZ - time of day with time zone
1266: "timetz" => Timetz: Kind::Simple, 1266: "timetz" => Timetz: Kind::Simple,
#[doc="TIMETZ[]"] /// TIMETZ[]
1270: "_timetz" => TimetzArray: Kind::Array(Type::Timetz), 1270: "_timetz" => TimetzArray: Kind::Array(Type::Timetz),
#[doc="BIT - fixed-length bit string"] /// BIT - fixed-length bit string
1560: "bit" => Bit: Kind::Simple, 1560: "bit" => Bit: Kind::Simple,
#[doc="BIT[]"] /// BIT[]
1561: "_bit" => BitArray: Kind::Array(Type::Bit), 1561: "_bit" => BitArray: Kind::Array(Type::Bit),
#[doc="VARBIT - variable-length bit string"] /// VARBIT - variable-length bit string
1562: "varbit" => Varbit: Kind::Simple, 1562: "varbit" => Varbit: Kind::Simple,
#[doc="VARBIT[]"] /// VARBIT[]
1563: "_varbit" => VarbitArray: Kind::Array(Type::Varbit), 1563: "_varbit" => VarbitArray: Kind::Array(Type::Varbit),
#[doc="NUMERIC - numeric(precision, decimal), arbitrary precision number"] /// NUMERIC - numeric(precision, decimal), arbitrary precision number
1700: "numeric" => Numeric: Kind::Simple, 1700: "numeric" => Numeric: Kind::Simple,
#[doc="REFCURSOR - reference to cursor (portal name)"] /// REFCURSOR - reference to cursor (portal name)
1790: "refcursor" => Refcursor: Kind::Simple, 1790: "refcursor" => Refcursor: Kind::Simple,
#[doc="REFCURSOR[]"] /// REFCURSOR[]
2201: "_refcursor" => RefcursorArray: Kind::Array(Type::Refcursor), 2201: "_refcursor" => RefcursorArray: Kind::Array(Type::Refcursor),
#[doc="REGPROCEDURE - registered procedure (with args)"] /// REGPROCEDURE - registered procedure (with args)
2202: "regprocedure" => Regprocedure: Kind::Simple, 2202: "regprocedure" => Regprocedure: Kind::Simple,
#[doc="REGOPER - registered operator"] /// REGOPER - registered operator
2203: "regoper" => Regoper: Kind::Simple, 2203: "regoper" => Regoper: Kind::Simple,
#[doc="REGOPERATOR - registered operator (with args)"] /// REGOPERATOR - registered operator (with args)
2204: "regoperator" => Regoperator: Kind::Simple, 2204: "regoperator" => Regoperator: Kind::Simple,
#[doc="REGCLASS - registered class"] /// REGCLASS - registered class
2205: "regclass" => Regclass: Kind::Simple, 2205: "regclass" => Regclass: Kind::Simple,
#[doc="REGTYPE - registered type"] /// REGTYPE - registered type
2206: "regtype" => Regtype: Kind::Simple, 2206: "regtype" => Regtype: Kind::Simple,
#[doc="REGPROCEDURE[]"] /// REGPROCEDURE[]
2207: "_regprocedure" => RegprocedureArray: Kind::Array(Type::Regprocedure), 2207: "_regprocedure" => RegprocedureArray: Kind::Array(Type::Regprocedure),
#[doc="REGOPER[]"] /// REGOPER[]
2208: "_regoper" => RegoperArray: Kind::Array(Type::Regoper), 2208: "_regoper" => RegoperArray: Kind::Array(Type::Regoper),
#[doc="REGOPERATOR[]"] /// REGOPERATOR[]
2209: "_regoperator" => RegoperatorArray: Kind::Array(Type::Regoperator), 2209: "_regoperator" => RegoperatorArray: Kind::Array(Type::Regoperator),
#[doc="REGCLASS[]"] /// REGCLASS[]
2210: "_regclass" => RegclassArray: Kind::Array(Type::Regclass), 2210: "_regclass" => RegclassArray: Kind::Array(Type::Regclass),
#[doc="REGTYPE[]"] /// REGTYPE[]
2211: "_regtype" => RegtypeArray: Kind::Array(Type::Regtype), 2211: "_regtype" => RegtypeArray: Kind::Array(Type::Regtype),
#[doc="RECORD"] /// RECORD
2249: "record" => Record: Kind::Simple, 2249: "record" => Record: Kind::Simple,
#[doc="CSTRING"] /// CSTRING
2275: "cstring" => Cstring: Kind::Simple, 2275: "cstring" => Cstring: Kind::Simple,
#[doc="ANY"] /// ANY
2276: "any" => Any: Kind::Simple, 2276: "any" => Any: Kind::Simple,
#[doc="ANYARRAY"] /// ANYARRAY
2277: "anyarray" => AnyArray: Kind::Array(Type::Any), 2277: "anyarray" => AnyArray: Kind::Array(Type::Any),
#[doc="VOID"] /// VOID
2278: "void" => Void: Kind::Simple, 2278: "void" => Void: Kind::Simple,
#[doc="TRIGGER"] /// TRIGGER
2279: "trigger" => Trigger: Kind::Simple, 2279: "trigger" => Trigger: Kind::Simple,
#[doc="LANGUAGE_HANDLER"] /// LANGUAGE_HANDLER
2280: "language_handler" => LanguageHandler: Kind::Simple, 2280: "language_handler" => LanguageHandler: Kind::Simple,
#[doc="INTERNAL"] /// INTERNAL
2281: "internal" => Internal: Kind::Simple, 2281: "internal" => Internal: Kind::Simple,
#[doc="OPAQUE"] /// OPAQUE
2282: "opaque" => Opaque: Kind::Simple, 2282: "opaque" => Opaque: Kind::Simple,
#[doc="ANYELEMENT"] /// ANYELEMENT
2283: "anyelement" => Anyelement: Kind::Simple, 2283: "anyelement" => Anyelement: Kind::Simple,
#[doc="RECORD[]"] /// RECORD[]
2287: "_record" => RecordArray: Kind::Array(Type::Record), 2287: "_record" => RecordArray: Kind::Array(Type::Record),
#[doc="ANYNONARRAY"] /// ANYNONARRAY
2776: "anynonarray" => Anynonarray: Kind::Simple, 2776: "anynonarray" => Anynonarray: Kind::Simple,
#[doc="TXID_SNAPSHOT[]"] /// TXID_SNAPSHOT[]
2949: "_txid_snapshot" => TxidSnapshotArray: Kind::Array(Type::TxidSnapshot), 2949: "_txid_snapshot" => TxidSnapshotArray: Kind::Array(Type::TxidSnapshot),
#[doc="UUID - UUID datatype"] /// UUID - UUID datatype
2950: "uuid" => Uuid: Kind::Simple, 2950: "uuid" => Uuid: Kind::Simple,
#[doc="TXID_SNAPSHOT - txid snapshot"] /// TXID_SNAPSHOT - txid snapshot
2970: "txid_snapshot" => TxidSnapshot: Kind::Simple, 2970: "txid_snapshot" => TxidSnapshot: Kind::Simple,
#[doc="UUID[]"] /// UUID[]
2951: "_uuid" => UuidArray: Kind::Array(Type::Uuid), 2951: "_uuid" => UuidArray: Kind::Array(Type::Uuid),
#[doc="FDW_HANDLER"] /// FDW_HANDLER
3115: "fdw_handler" => FdwHandler: Kind::Simple, 3115: "fdw_handler" => FdwHandler: Kind::Simple,
#[doc="PG_LSN - PostgreSQL LSN datatype"] /// PG_LSN - PostgreSQL LSN datatype
3220: "pg_lsn" => PgLsn: Kind::Simple, 3220: "pg_lsn" => PgLsn: Kind::Simple,
#[doc="PG_LSN[]"] /// PG_LSN[]
3221: "_pg_lsn" => PgLsnArray: Kind::Array(Type::PgLsn), 3221: "_pg_lsn" => PgLsnArray: Kind::Array(Type::PgLsn),
#[doc="ANYENUM"] /// ANYENUM
3500: "anyenum" => Anyenum: Kind::Simple, 3500: "anyenum" => Anyenum: Kind::Simple,
#[doc="TSVECTOR - text representation for text search"] /// TSVECTOR - text representation for text search
3614: "tsvector" => Tsvector: Kind::Simple, 3614: "tsvector" => Tsvector: Kind::Simple,
#[doc="TSQUERY - query representation for text search"] /// TSQUERY - query representation for text search
3615: "tsquery" => Tsquery: Kind::Simple, 3615: "tsquery" => Tsquery: Kind::Simple,
#[doc="GTSVECTOR - GiST index internal text representation for text search"] /// GTSVECTOR - GiST index internal text representation for text search
3642: "gtsvector" => Gtsvector: Kind::Simple, 3642: "gtsvector" => Gtsvector: Kind::Simple,
#[doc="TSVECTOR[]"] /// TSVECTOR[]
3643: "_tsvector" => TsvectorArray: Kind::Array(Type::Tsvector), 3643: "_tsvector" => TsvectorArray: Kind::Array(Type::Tsvector),
#[doc="GTSVECTOR[]"] /// GTSVECTOR[]
3644: "_gtsvector" => GtsvectorArray: Kind::Array(Type::Gtsvector), 3644: "_gtsvector" => GtsvectorArray: Kind::Array(Type::Gtsvector),
#[doc="TSQUERY[]"] /// TSQUERY[]
3645: "_tsquery" => TsqueryArray: Kind::Array(Type::Tsquery), 3645: "_tsquery" => TsqueryArray: Kind::Array(Type::Tsquery),
#[doc="REGCONFIG - registered text search configuration"] /// REGCONFIG - registered text search configuration
3734: "regconfig" => Regconfig: Kind::Simple, 3734: "regconfig" => Regconfig: Kind::Simple,
#[doc="REGCONFIG[]"] /// REGCONFIG[]
3735: "_regconfig" => RegconfigArray: Kind::Array(Type::Regconfig), 3735: "_regconfig" => RegconfigArray: Kind::Array(Type::Regconfig),
#[doc="REGDICTIONARY - registered text search dictionary"] /// REGDICTIONARY - registered text search dictionary
3769: "regdictionary" => Regdictionary: Kind::Simple, 3769: "regdictionary" => Regdictionary: Kind::Simple,
#[doc="REGDICTIONARY[]"] /// REGDICTIONARY[]
3770: "_regdictionary" => RegdictionaryArray: Kind::Array(Type::Regdictionary), 3770: "_regdictionary" => RegdictionaryArray: Kind::Array(Type::Regdictionary),
#[doc="JSONB"] /// JSONB
3802: "jsonb" => Jsonb: Kind::Simple, 3802: "jsonb" => Jsonb: Kind::Simple,
#[doc="ANYRANGE"] /// ANYRANGE
3831: "anyrange" => Anyrange: Kind::Simple, 3831: "anyrange" => Anyrange: Kind::Simple,
#[doc="JSONB[]"] /// JSONB[]
3807: "_jsonb" => JsonbArray: Kind::Array(Type::Jsonb), 3807: "_jsonb" => JsonbArray: Kind::Array(Type::Jsonb),
#[doc="INT4RANGE - range of integers"] /// INT4RANGE - range of integers
3904: "int4range" => Int4Range: Kind::Range(Type::Int4), 3904: "int4range" => Int4Range: Kind::Range(Type::Int4),
#[doc="INT4RANGE[]"] /// INT4RANGE[]
3905: "_int4range" => Int4RangeArray: Kind::Array(Type::Int4Range), 3905: "_int4range" => Int4RangeArray: Kind::Array(Type::Int4Range),
#[doc="NUMRANGE - range of numerics"] /// NUMRANGE - range of numerics
3906: "numrange" => NumRange: Kind::Range(Type::Numeric), 3906: "numrange" => NumRange: Kind::Range(Type::Numeric),
#[doc="NUMRANGE[]"] /// NUMRANGE[]
3907: "_numrange" => NumRangeArray: Kind::Array(Type::NumRange), 3907: "_numrange" => NumRangeArray: Kind::Array(Type::NumRange),
#[doc="TSRANGE - range of timestamps without time zone"] /// TSRANGE - range of timestamps without time zone
3908: "tsrange" => TsRange: Kind::Range(Type::Timestamp), 3908: "tsrange" => TsRange: Kind::Range(Type::Timestamp),
#[doc="TSRANGE[]"] /// TSRANGE[]
3909: "_tsrange" => TsRangeArray: Kind::Array(Type::TsRange), 3909: "_tsrange" => TsRangeArray: Kind::Array(Type::TsRange),
#[doc="TSTZRANGE - range of timestamps with time zone"] /// TSTZRANGE - range of timestamps with time zone
3910: "tstzrange" => TstzRange: Kind::Range(Type::TimestampTZ), 3910: "tstzrange" => TstzRange: Kind::Range(Type::TimestampTZ),
#[doc="TSTZRANGE[]"] /// TSTZRANGE[]
3911: "_tstzrange" => TstzRangeArray: Kind::Array(Type::TstzRange), 3911: "_tstzrange" => TstzRangeArray: Kind::Array(Type::TstzRange),
#[doc="DATERANGE - range of dates"] /// DATERANGE - range of dates
3912: "daterange" => DateRange: Kind::Range(Type::Date), 3912: "daterange" => DateRange: Kind::Range(Type::Date),
#[doc="DATERANGE[]"] /// DATERANGE[]
3913: "_daterange" => DateRangeArray: Kind::Array(Type::DateRange), 3913: "_daterange" => DateRangeArray: Kind::Array(Type::DateRange),
#[doc="INT8RANGE - range of bigints"] /// INT8RANGE - range of bigints
3926: "int8range" => Int8Range: Kind::Range(Type::Int8), 3926: "int8range" => Int8Range: Kind::Range(Type::Int8),
#[doc="INT8RANGE[]"] /// INT8RANGE[]
3927: "_int8range" => Int8RangeArray: Kind::Array(Type::Int8Range), 3927: "_int8range" => Int8RangeArray: Kind::Array(Type::Int8Range),
#[doc="EVENT_TRIGGER"] /// EVENT_TRIGGER
3838: "event_trigger" => EventTrigger: Kind::Simple 3838: "event_trigger" => EventTrigger: Kind::Simple
} }
@ -596,7 +594,9 @@ impl error::Error for WasNull {
/// nullable Postgres value. /// nullable Postgres value.
pub trait FromSql: Sized { pub trait FromSql: Sized {
/// ### Deprecated /// ### Deprecated
fn from_sql_nullable<R: Read>(ty: &Type, raw: Option<&mut R>, ctx: &SessionInfo) fn from_sql_nullable<R: Read>(ty: &Type,
raw: Option<&mut R>,
ctx: &SessionInfo)
-> Result<Self> { -> Result<Self> {
match raw { match raw {
Some(raw) => FromSql::from_sql(ty, raw, ctx), Some(raw) => FromSql::from_sql(ty, raw, ctx),
@ -704,7 +704,9 @@ primitive_from!(f32, read_f32, Type::Float4);
primitive_from!(f64, read_f64, Type::Float8); primitive_from!(f64, read_f64, Type::Float8);
impl FromSql for HashMap<String, Option<String>> { impl FromSql for HashMap<String, Option<String>> {
fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) fn from_sql<R: Read>(_: &Type,
raw: &mut R,
_: &SessionInfo)
-> Result<HashMap<String, Option<String>>> { -> Result<HashMap<String, Option<String>>> {
let mut map = HashMap::new(); let mut map = HashMap::new();
@ -740,7 +742,7 @@ impl FromSql for HashMap<String, Option<String>> {
fn accepts(ty: &Type) -> bool { fn accepts(ty: &Type) -> bool {
match *ty { match *ty {
Type::Other(ref u) if u.name() == "hstore" => true, Type::Other(ref u) if u.name() == "hstore" => true,
_ => false _ => false,
} }
} }
} }
@ -811,7 +813,8 @@ pub trait ToSql: fmt::Debug {
/// `NULL`. If this is the case, implementations **must not** write /// `NULL`. If this is the case, implementations **must not** write
/// anything to `out`. /// anything to `out`.
fn to_sql<W: ?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result<IsNull> fn to_sql<W: ?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result<IsNull>
where Self: Sized, W: Write; where Self: Sized,
W: Write;
/// Determines if a value of this type can be converted to the specified /// Determines if a value of this type can be converted to the specified
/// Postgres `Type`. /// Postgres `Type`.
@ -821,34 +824,33 @@ pub trait ToSql: fmt::Debug {
/// ///
/// *All* implementations of this method should be generated by the /// *All* implementations of this method should be generated by the
/// `to_sql_checked!()` macro. /// `to_sql_checked!()` macro.
fn to_sql_checked(&self, ty: &Type, out: &mut Write, ctx: &SessionInfo) fn to_sql_checked(&self, ty: &Type, out: &mut Write, ctx: &SessionInfo) -> Result<IsNull>;
-> Result<IsNull>;
} }
impl<'a, T> ToSql for &'a T where T: ToSql { impl<'a, T> ToSql for &'a T where T: ToSql {
fn to_sql_checked(&self, ty: &Type, out: &mut Write, ctx: &SessionInfo) to_sql_checked!();
-> Result<IsNull> {
if !<&'a T as ToSql>::accepts(ty) {
return Err(Error::WrongType(ty.clone()));
}
self.to_sql(ty, out, ctx)
}
fn to_sql<W: Write + ?Sized>(&self,
fn to_sql<W: Write + ?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result<IsNull> { ty: &Type,
out: &mut W,
ctx: &SessionInfo)
-> Result<IsNull> {
(*self).to_sql(ty, out, ctx) (*self).to_sql(ty, out, ctx)
} }
fn accepts(ty: &Type) -> bool { T::accepts(ty) } fn accepts(ty: &Type) -> bool {
T::accepts(ty)
}
} }
impl<T: ToSql> ToSql for Option<T> { impl<T: ToSql> ToSql for Option<T> {
to_sql_checked!(); to_sql_checked!();
fn to_sql<W: Write+?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self,
-> Result<IsNull> { ty: &Type,
out: &mut W,
ctx: &SessionInfo)
-> Result<IsNull> {
match *self { match *self {
Some(ref val) => val.to_sql(ty, out, ctx), Some(ref val) => val.to_sql(ty, out, ctx),
None => Ok(IsNull::Yes), None => Ok(IsNull::Yes),
@ -863,8 +865,11 @@ impl<T: ToSql> ToSql for Option<T> {
impl ToSql for bool { impl ToSql for bool {
to_sql_checked!(); to_sql_checked!();
fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self,
-> Result<IsNull> { _: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
try!(w.write_u8(*self as u8)); try!(w.write_u8(*self as u8));
Ok(IsNull::No) Ok(IsNull::No)
} }
@ -873,17 +878,9 @@ impl ToSql for bool {
} }
impl<'a> ToSql for &'a [u8] { impl<'a> ToSql for &'a [u8] {
// FIXME should use to_sql_checked!() but blocked on rust-lang/rust#24308 to_sql_checked!();
fn to_sql_checked(&self, ty: &Type, out: &mut Write, ctx: &SessionInfo)
-> Result<IsNull> {
if !<&'a [u8] as ToSql>::accepts(ty) {
return Err(Error::WrongType(ty.clone()));
}
self.to_sql(ty, out, ctx)
}
fn to_sql<W: Write+?Sized>(&self, _: &Type, w: &mut W, _: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self, _: &Type, w: &mut W, _: &SessionInfo) -> Result<IsNull> {
-> Result<IsNull> {
try!(w.write_all(*self)); try!(w.write_all(*self));
Ok(IsNull::No) Ok(IsNull::No)
} }
@ -894,8 +891,7 @@ impl<'a> ToSql for &'a [u8] {
impl ToSql for Vec<u8> { impl ToSql for Vec<u8> {
to_sql_checked!(); to_sql_checked!();
fn to_sql<W: Write+?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
-> Result<IsNull> {
<&[u8] as ToSql>::to_sql(&&**self, ty, w, ctx) <&[u8] as ToSql>::to_sql(&&**self, ty, w, ctx)
} }
@ -905,17 +901,9 @@ impl ToSql for Vec<u8> {
} }
impl<'a> ToSql for &'a str { impl<'a> ToSql for &'a str {
// FIXME should use to_sql_checked!() but blocked on rust-lang/rust#24308 to_sql_checked!();
fn to_sql_checked(&self, ty: &Type, out: &mut Write, ctx: &SessionInfo)
-> Result<IsNull> {
if !<&'a str as ToSql>::accepts(ty) {
return Err(Error::WrongType(ty.clone()));
}
self.to_sql(ty, out, ctx)
}
fn to_sql<W: Write+?Sized>(&self, _: &Type, w: &mut W, _: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self, _: &Type, w: &mut W, _: &SessionInfo) -> Result<IsNull> {
-> Result<IsNull> {
try!(w.write_all(self.as_bytes())); try!(w.write_all(self.as_bytes()));
Ok(IsNull::No) Ok(IsNull::No)
} }
@ -932,8 +920,7 @@ impl<'a> ToSql for &'a str {
impl ToSql for String { impl ToSql for String {
to_sql_checked!(); to_sql_checked!();
fn to_sql<W: Write+?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
-> Result<IsNull> {
<&str as ToSql>::to_sql(&&**self, ty, w, ctx) <&str as ToSql>::to_sql(&&**self, ty, w, ctx)
} }
@ -945,8 +932,11 @@ impl ToSql for String {
impl ToSql for i8 { impl ToSql for i8 {
to_sql_checked!(); to_sql_checked!();
fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self,
-> Result<IsNull> { _: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
try!(w.write_i8(*self)); try!(w.write_i8(*self));
Ok(IsNull::No) Ok(IsNull::No)
} }
@ -980,8 +970,11 @@ to_primitive!(f64, write_f64, Type::Float8);
impl ToSql for HashMap<String, Option<String>> { impl ToSql for HashMap<String, Option<String>> {
to_sql_checked!(); to_sql_checked!();
fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self,
-> Result<IsNull> { _: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
try!(w.write_i32::<BigEndian>(try!(downcast(self.len())))); try!(w.write_i32::<BigEndian>(try!(downcast(self.len()))));
for (key, val) in self { for (key, val) in self {
@ -993,7 +986,7 @@ impl ToSql for HashMap<String, Option<String>> {
try!(w.write_i32::<BigEndian>(try!(downcast(val.len())))); try!(w.write_i32::<BigEndian>(try!(downcast(val.len()))));
try!(w.write_all(val.as_bytes())); try!(w.write_all(val.as_bytes()));
} }
None => try!(w.write_i32::<BigEndian>(-1)) None => try!(w.write_i32::<BigEndian>(-1)),
} }
} }
@ -1010,7 +1003,7 @@ impl ToSql for HashMap<String, Option<String>> {
fn downcast(len: usize) -> Result<i32> { fn downcast(len: usize) -> Result<i32> {
if len > i32::max_value() as usize { if len > i32::max_value() as usize {
let err: Box<error::Error+Sync+Send> = "value too large to transmit".into(); let err: Box<error::Error + Sync + Send> = "value too large to transmit".into();
Err(Error::Conversion(err)) Err(Error::Conversion(err))
} else { } else {
Ok(len as i32) Ok(len as i32)

View File

@ -12,7 +12,8 @@ impl FromSql for json::Json {
if let Type::Jsonb = *ty { if let Type::Jsonb = *ty {
// We only support version 1 of the jsonb binary format // We only support version 1 of the jsonb binary format
if try!(raw.read_u8()) != 1 { if try!(raw.read_u8()) != 1 {
let err: Box<error::Error+Sync+Send> = "unsupported JSONB encoding version".into(); let err: Box<error::Error + Sync + Send> = "unsupported JSONB encoding version"
.into();
return Err(Error::Conversion(err)); return Err(Error::Conversion(err));
} }
} }
@ -23,8 +24,11 @@ impl FromSql for json::Json {
} }
impl ToSql for json::Json { impl ToSql for json::Json {
fn to_sql<W: Write+?Sized>(&self, ty: &Type, mut out: &mut W, _: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self,
-> Result<IsNull> { ty: &Type,
mut out: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
if let Type::Jsonb = *ty { if let Type::Jsonb = *ty {
try!(out.write_u8(1)); try!(out.write_u8(1));
} }

View File

@ -14,7 +14,8 @@ impl FromSql for Value {
if let Type::Jsonb = *ty { if let Type::Jsonb = *ty {
// We only support version 1 of the jsonb binary format // We only support version 1 of the jsonb binary format
if try!(raw.read_u8()) != 1 { if try!(raw.read_u8()) != 1 {
let err: Box<error::Error+Sync+Send> = "unsupported JSONB encoding version".into(); let err: Box<error::Error + Sync + Send> = "unsupported JSONB encoding version"
.into();
return Err(Error::Conversion(err)); return Err(Error::Conversion(err));
} }
} }
@ -25,8 +26,11 @@ impl FromSql for Value {
} }
impl ToSql for Value { impl ToSql for Value {
fn to_sql<W: Write+?Sized>(&self, ty: &Type, mut out: &mut W, _: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self,
-> Result<IsNull> { ty: &Type,
mut out: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
if let Type::Jsonb = *ty { if let Type::Jsonb = *ty {
try!(out.write_u8(1)); try!(out.write_u8(1));
} }

View File

@ -14,7 +14,8 @@ impl FromSql for Value {
if let Type::Jsonb = *ty { if let Type::Jsonb = *ty {
// We only support version 1 of the jsonb binary format // We only support version 1 of the jsonb binary format
if try!(raw.read_u8()) != 1 { if try!(raw.read_u8()) != 1 {
let err: Box<error::Error+Sync+Send> = "unsupported JSONB encoding version".into(); let err: Box<error::Error + Sync + Send> = "unsupported JSONB encoding version"
.into();
return Err(Error::Conversion(err)); return Err(Error::Conversion(err));
} }
} }
@ -25,8 +26,11 @@ impl FromSql for Value {
} }
impl ToSql for Value { impl ToSql for Value {
fn to_sql<W: Write+?Sized>(&self, ty: &Type, mut out: &mut W, _: &SessionInfo) fn to_sql<W: Write + ?Sized>(&self,
-> Result<IsNull> { ty: &Type,
mut out: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
if let Type::Jsonb = *ty { if let Type::Jsonb = *ty {
try!(out.write_u8(1)); try!(out.write_u8(1));
} }

View File

@ -38,7 +38,11 @@ impl<'a, T: 'a + ToSql> ToSql for Slice<'a, T> {
self.to_sql(ty, out, ctx) self.to_sql(ty, out, ctx)
} }
fn to_sql<W: Write+?Sized>(&self, ty: &Type, mut w: &mut W, ctx: &SessionInfo) -> Result<IsNull> { fn to_sql<W: Write + ?Sized>(&self,
ty: &Type,
mut w: &mut W,
ctx: &SessionInfo)
-> Result<IsNull> {
let member_type = match ty.kind() { let member_type = match ty.kind() {
&Kind::Array(ref member) => member, &Kind::Array(ref member) => member,
_ => panic!("expected array type"), _ => panic!("expected array type"),

View File

@ -31,7 +31,11 @@ impl FromSql for Timespec {
} }
impl ToSql for Timespec { impl ToSql for Timespec {
fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) -> Result<IsNull> { fn to_sql<W: Write + ?Sized>(&self,
_: &Type,
mut w: &mut W,
_: &SessionInfo)
-> Result<IsNull> {
let t = (self.sec - TIME_SEC_CONVERSION) * USEC_PER_SEC + self.nsec as i64 / NSEC_PER_USEC; let t = (self.sec - TIME_SEC_CONVERSION) * USEC_PER_SEC + self.nsec as i64 / NSEC_PER_USEC;
try!(w.write_i64::<BigEndian>(t)); try!(w.write_i64::<BigEndian>(t));
Ok(IsNull::No) Ok(IsNull::No)

View File

@ -18,7 +18,7 @@ impl FromSql for Uuid {
} }
impl ToSql for Uuid { impl ToSql for Uuid {
fn to_sql<W: Write+?Sized>(&self, _: &Type, w: &mut W, _: &SessionInfo) -> Result<IsNull> { fn to_sql<W: Write + ?Sized>(&self, _: &Type, w: &mut W, _: &SessionInfo) -> Result<IsNull> {
try!(w.write_all(self.as_bytes())); try!(w.write_all(self.as_bytes()));
Ok(IsNull::No) Ok(IsNull::No)
} }

View File

@ -23,12 +23,12 @@ pub struct Url {
pub struct Path { pub struct Path {
pub path: String, pub path: String,
pub query: Query, pub query: Query,
pub fragment: Option<String> pub fragment: Option<String>,
} }
pub struct UserInfo { pub struct UserInfo {
pub user: String, pub user: String,
pub pass: Option<String> pub pass: Option<String>,
} }
pub type Query = Vec<(String, String)>; pub type Query = Vec<(String, String)>;
@ -47,7 +47,7 @@ impl Url {
user: user, user: user,
host: host, host: host,
port: port, port: port,
path: Path::new(path, query, fragment) path: Path::new(path, query, fragment),
} }
} }
@ -66,21 +66,18 @@ impl Url {
let (query, fragment) = try!(get_query_fragment(rest)); let (query, fragment) = try!(get_query_fragment(rest));
let url = Url::new(scheme.to_owned(), let url = Url::new(scheme.to_owned(),
userinfo, userinfo,
host.to_owned(), host.to_owned(),
port, port,
path, path,
query, query,
fragment); fragment);
Ok(url) Ok(url)
} }
} }
impl Path { impl Path {
pub fn new(path: String, pub fn new(path: String, query: Query, fragment: Option<String>) -> Path {
query: Query,
fragment: Option<String>)
-> Path {
Path { Path {
path: path, path: path,
query: query, query: query,
@ -94,14 +91,21 @@ impl Path {
// query and fragment // query and fragment
let (query, fragment) = try!(get_query_fragment(&rest)); let (query, fragment) = try!(get_query_fragment(&rest));
Ok(Path{ path: path, query: query, fragment: fragment }) Ok(Path {
path: path,
query: query,
fragment: fragment,
})
} }
} }
impl UserInfo { impl UserInfo {
#[inline] #[inline]
pub fn new(user: String, pass: Option<String>) -> UserInfo { pub fn new(user: String, pass: Option<String>) -> UserInfo {
UserInfo { user: user, pass: pass } UserInfo {
user: user,
pass: pass,
}
} }
} }
@ -121,28 +125,40 @@ fn decode_inner(c: &str, full_url: bool) -> DecodeResult<String> {
'%' => { '%' => {
let bytes = match (iter.next(), iter.next()) { let bytes = match (iter.next(), iter.next()) {
(Some(one), Some(two)) => [one, two], (Some(one), Some(two)) => [one, two],
_ => return Err(format!("Malformed input: found '%' \ _ => return Err(format!("Malformed input: found '%' without two \
without two trailing bytes")), trailing bytes")),
}; };
// Only decode some characters if full_url: // Only decode some characters if full_url:
match str::from_utf8(&bytes).unwrap().from_hex().unwrap()[0] as char { match str::from_utf8(&bytes).unwrap().from_hex().unwrap()[0] as char {
// gen-delims: // gen-delims:
':' | '/' | '?' | '#' | '[' | ']' | '@' | ':' |
'/' |
// sub-delims: '?' |
'!' | '$' | '&' | '"' | '(' | ')' | '*' | '#' |
'+' | ',' | ';' | '=' '[' |
if full_url => { ']' |
'@' |
'!' |
'$' |
'&' |
'"' |
'(' |
')' |
'*' |
'+' |
',' |
';' |
'=' if full_url => {
out.push('%'); out.push('%');
out.push(bytes[0] as char); out.push(bytes[0] as char);
out.push(bytes[1] as char); out.push(bytes[1] as char);
} }
ch => out.push(ch) ch => out.push(ch),
} }
} }
ch => out.push(ch) ch => out.push(ch),
}, },
None => return Ok(out), None => return Ok(out),
} }
@ -160,12 +176,11 @@ fn split_char_first(s: &str, c: char) -> (&str, &str) {
} }
fn query_from_str(rawquery: &str) -> DecodeResult<Query> { fn query_from_str(rawquery: &str) -> DecodeResult<Query> {
let mut query: Query = vec!(); let mut query: Query = vec![];
if !rawquery.is_empty() { if !rawquery.is_empty() {
for p in rawquery.split('&') { for p in rawquery.split('&') {
let (k, v) = split_char_first(p, '='); let (k, v) = split_char_first(p, '=');
query.push((try!(decode_component(k)), query.push((try!(decode_component(k)), try!(decode_component(v))));
try!(decode_component(v))));
} }
} }
@ -173,12 +188,14 @@ fn query_from_str(rawquery: &str) -> DecodeResult<Query> {
} }
pub fn get_scheme(rawurl: &str) -> DecodeResult<(&str, &str)> { pub fn get_scheme(rawurl: &str) -> DecodeResult<(&str, &str)> {
for (i,c) in rawurl.chars().enumerate() { for (i, c) in rawurl.chars().enumerate() {
let result = match c { let result = match c {
'A' ... 'Z' 'A'...'Z' |
| 'a' ... 'z' => continue, 'a'...'z' => continue,
'0' ... '9' | '+' | '-' | '.' => { '0'...'9' | '+' | '-' | '.' => {
if i != 0 { continue } if i != 0 {
continue;
}
Err("url: Scheme must begin with a letter.".to_owned()) Err("url: Scheme must begin with a letter.".to_owned())
} }
@ -186,7 +203,7 @@ pub fn get_scheme(rawurl: &str) -> DecodeResult<(&str, &str)> {
if i == 0 { if i == 0 {
Err("url: Scheme cannot be empty.".to_owned()) Err("url: Scheme cannot be empty.".to_owned())
} else { } else {
Ok((&rawurl[0..i], &rawurl[i+1..rawurl.len()])) Ok((&rawurl[0..i], &rawurl[i + 1..rawurl.len()]))
} }
} }
_ => Err("url: Invalid character in scheme.".to_owned()), _ => Err("url: Invalid character in scheme.".to_owned()),
@ -199,22 +216,21 @@ pub fn get_scheme(rawurl: &str) -> DecodeResult<(&str, &str)> {
} }
// returns userinfo, host, port, and unparsed part, or an error // returns userinfo, host, port, and unparsed part, or an error
fn get_authority(rawurl: &str) -> fn get_authority(rawurl: &str) -> DecodeResult<(Option<UserInfo>, &str, Option<u16>, &str)> {
DecodeResult<(Option<UserInfo>, &str, Option<u16>, &str)> {
enum State { enum State {
Start, // starting state Start, // starting state
PassHostPort, // could be in user or port PassHostPort, // could be in user or port
Ip6Port, // either in ipv6 host or port Ip6Port, // either in ipv6 host or port
Ip6Host, // are in an ipv6 host Ip6Host, // are in an ipv6 host
InHost, // are in a host - may be ipv6, but don't know yet InHost, // are in a host - may be ipv6, but don't know yet
InPort // are in port InPort, // are in port
} }
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
enum Input { enum Input {
Digit, // all digits Digit, // all digits
Hex, // digits and letters a-f Hex, // digits and letters a-f
Unreserved // all other legal characters Unreserved, // all other legal characters
} }
if !rawurl.starts_with("//") { if !rawurl.starts_with("//") {
@ -235,23 +251,35 @@ fn get_authority(rawurl: &str) ->
let mut begin = 2; let mut begin = 2;
let mut end = len; let mut end = len;
for (i,c) in rawurl.chars().enumerate() for (i, c) in rawurl.chars()
// ignore the leading '//' handled by early return .enumerate()
.skip(2) { .skip(2) {
// deal with input class first // deal with input class first
match c { match c {
'0' ... '9' => (), '0'...'9' => (),
'A' ... 'F' 'A'...'F' |
| 'a' ... 'f' => { 'a'...'f' => {
if input == Input::Digit { if input == Input::Digit {
input = Input::Hex; input = Input::Hex;
} }
} }
'G' ... 'Z' 'G'...'Z' |
| 'g' ... 'z' 'g'...'z' |
| '-' | '.' | '_' | '~' | '%' '-' |
| '&' |'\'' | '(' | ')' | '+' '.' |
| '!' | '*' | ',' | ';' | '=' => input = Input::Unreserved, '_' |
'~' |
'%' |
'&' |
'\'' |
'(' |
')' |
'+' |
'!' |
'*' |
',' |
';' |
'=' => input = Input::Unreserved,
':' | '@' | '?' | '#' | '/' => { ':' | '@' | '?' | '#' | '/' => {
// separators, don't change anything // separators, don't change anything
} }
@ -260,97 +288,96 @@ fn get_authority(rawurl: &str) ->
// now process states // now process states
match c { match c {
':' => { ':' => {
colon_count += 1; colon_count += 1;
match st { match st {
State::Start => { State::Start => {
pos = i; pos = i;
st = State::PassHostPort; st = State::PassHostPort;
} }
State::PassHostPort => { State::PassHostPort => {
// multiple colons means ipv6 address. // multiple colons means ipv6 address.
if input == Input::Unreserved { if input == Input::Unreserved {
return Err( return Err("Illegal characters in IPv6 address.".to_owned());
"Illegal characters in IPv6 address.".to_owned()); }
st = State::Ip6Host;
}
State::InHost => {
pos = i;
if input == Input::Unreserved {
// must be port
host = &rawurl[begin..i];
st = State::InPort;
} else {
// can't be sure whether this is an ipv6 address or a port
st = State::Ip6Port;
}
}
State::Ip6Port => {
if input == Input::Unreserved {
return Err("Illegal characters in authority.".to_owned());
}
st = State::Ip6Host;
}
State::Ip6Host => {
if colon_count > 7 {
host = &rawurl[begin..i];
pos = i;
st = State::InPort;
}
}
_ => return Err("Invalid ':' in authority.".to_owned()),
} }
st = State::Ip6Host; input = Input::Digit; // reset input class
}
State::InHost => {
pos = i;
if input == Input::Unreserved {
// must be port
host = &rawurl[begin..i];
st = State::InPort;
} else {
// can't be sure whether this is an ipv6 address or a port
st = State::Ip6Port;
}
}
State::Ip6Port => {
if input == Input::Unreserved {
return Err("Illegal characters in authority.".to_owned());
}
st = State::Ip6Host;
}
State::Ip6Host => {
if colon_count > 7 {
host = &rawurl[begin..i];
pos = i;
st = State::InPort;
}
}
_ => return Err("Invalid ':' in authority.".to_owned()),
} }
input = Input::Digit; // reset input class
}
'@' => { '@' => {
input = Input::Digit; // reset input class input = Input::Digit; // reset input class
colon_count = 0; // reset count colon_count = 0; // reset count
match st { match st {
State::Start => { State::Start => {
let user = try!(decode_component(&rawurl[begin..i])); let user = try!(decode_component(&rawurl[begin..i]));
userinfo = Some(UserInfo::new(user, None)); userinfo = Some(UserInfo::new(user, None));
st = State::InHost; st = State::InHost;
} }
State::PassHostPort => { State::PassHostPort => {
let user = try!(decode_component(&rawurl[begin..pos])); let user = try!(decode_component(&rawurl[begin..pos]));
let pass = try!(decode_component(&rawurl[pos+1..i])); let pass = try!(decode_component(&rawurl[pos + 1..i]));
userinfo = Some(UserInfo::new(user, Some(pass))); userinfo = Some(UserInfo::new(user, Some(pass)));
st = State::InHost; st = State::InHost;
} }
_ => return Err("Invalid '@' in authority.".to_owned()), _ => return Err("Invalid '@' in authority.".to_owned()),
}
begin = i + 1;
} }
begin = i+1;
}
'?' | '#' | '/' => { '?' | '#' | '/' => {
end = i; end = i;
break; break;
} }
_ => () _ => (),
} }
} }
// finish up // finish up
match st { match st {
State::Start => host = &rawurl[begin..end], State::Start => host = &rawurl[begin..end],
State::PassHostPort State::PassHostPort |
| State::Ip6Port => { State::Ip6Port => {
if input != Input::Digit { if input != Input::Digit {
return Err("Non-digit characters in port.".to_owned()); return Err("Non-digit characters in port.".to_owned());
}
host = &rawurl[begin..pos];
port = Some(&rawurl[pos + 1..end]);
} }
host = &rawurl[begin..pos]; State::Ip6Host |
port = Some(&rawurl[pos+1..end]); State::InHost => host = &rawurl[begin..end],
} State::InPort => {
State::Ip6Host if input != Input::Digit {
| State::InHost => host = &rawurl[begin..end], return Err("Non-digit characters in port.".to_owned());
State::InPort => { }
if input != Input::Digit { port = Some(&rawurl[pos + 1..end]);
return Err("Non-digit characters in port.".to_owned());
} }
port = Some(&rawurl[pos+1..end]);
}
} }
let rest = &rawurl[end..len]; let rest = &rawurl[end..len];
@ -359,8 +386,8 @@ fn get_authority(rawurl: &str) ->
None => None, None => None,
opt => match opt.and_then(|p| FromStr::from_str(p).ok()) { opt => match opt.and_then(|p| FromStr::from_str(p).ok()) {
None => return Err(format!("Failed to parse port: {:?}", port)), None => return Err(format!("Failed to parse port: {:?}", port)),
opt => opt opt => opt,
} },
}; };
Ok((userinfo, host, port, rest)) Ok((userinfo, host, port, rest))
@ -371,26 +398,39 @@ fn get_authority(rawurl: &str) ->
fn get_path(rawurl: &str, is_authority: bool) -> DecodeResult<(String, &str)> { fn get_path(rawurl: &str, is_authority: bool) -> DecodeResult<(String, &str)> {
let len = rawurl.len(); let len = rawurl.len();
let mut end = len; let mut end = len;
for (i,c) in rawurl.chars().enumerate() { for (i, c) in rawurl.chars().enumerate() {
match c { match c {
'A' ... 'Z' 'A'...'Z' |
| 'a' ... 'z' 'a'...'z' |
| '0' ... '9' '0'...'9' |
| '&' |'\'' | '(' | ')' | '.' '&' |
| '@' | ':' | '%' | '/' | '+' '\'' |
| '!' | '*' | ',' | ';' | '=' '(' |
| '_' | '-' | '~' => continue, ')' |
'?' | '#' => { '.' |
end = i; '@' |
break; ':' |
} '%' |
_ => return Err("Invalid character in path.".to_owned()) '/' |
'+' |
'!' |
'*' |
',' |
';' |
'=' |
'_' |
'-' |
'~' => continue,
'?' | '#' => {
end = i;
break;
}
_ => return Err("Invalid character in path.".to_owned()),
} }
} }
if is_authority && end != 0 && !rawurl.starts_with("/") { if is_authority && end != 0 && !rawurl.starts_with("/") {
Err("Non-empty path must begin with \ Err("Non-empty path must begin with '/' in presence of authority.".to_owned())
'/' in presence of authority.".to_owned())
} else { } else {
Ok((try!(decode_component(&rawurl[0..end])), &rawurl[end..len])) Ok((try!(decode_component(&rawurl[0..end])), &rawurl[end..len]))
} }
@ -403,12 +443,12 @@ fn get_query_fragment(rawurl: &str) -> DecodeResult<(Query, Option<String>)> {
// Parse the fragment if available // Parse the fragment if available
let fragment = match raw_fragment { let fragment = match raw_fragment {
"" => None, "" => None,
raw => Some(try!(decode_component(raw))) raw => Some(try!(decode_component(raw))),
}; };
match before_fragment.chars().next() { match before_fragment.chars().next() {
Some('?') => Ok((try!(query_from_str(&before_fragment[1..])), fragment)), Some('?') => Ok((try!(query_from_str(&before_fragment[1..])), fragment)),
None => Ok((vec!(), fragment)), None => Ok((vec![], fragment)),
_ => Err(format!("Query didn't start with '?': '{}..'", before_fragment)), _ => Err(format!("Query didn't start with '?': '{}..'", before_fragment)),
} }
} }
@ -426,4 +466,3 @@ impl FromStr for Path {
Path::parse(s) Path::parse(s)
} }
} }