Merge branch 'release-v0.10.2' into release
This commit is contained in:
commit
a402f7d3a6
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1 +0,0 @@
|
||||
*.rs eol=lf
|
@ -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
|
@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "postgres"
|
||||
version = "0.10.1"
|
||||
version = "0.10.2"
|
||||
authors = ["Steven Fackler <sfackler@gmail.com>"]
|
||||
license = "MIT"
|
||||
description = "A native PostgreSQL driver"
|
||||
repository = "https://github.com/sfackler/rust-postgres"
|
||||
documentation = "https://sfackler.github.io/rust-postgres/doc/v0.10.1/postgres"
|
||||
documentation = "https://sfackler.github.io/rust-postgres/doc/v0.10.2/postgres"
|
||||
readme = "README.md"
|
||||
keywords = ["database", "sql"]
|
||||
build = "build.rs"
|
||||
@ -31,7 +31,7 @@ phf = "0.7"
|
||||
rustc-serialize = "0.3"
|
||||
net2 = { version = "0.2", features = ["nightly"] }
|
||||
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_json = { version = "0.6", optional = true }
|
||||
time = { version = "0.1.14", optional = true }
|
||||
|
17
README.md
17
README.md
@ -1,7 +1,7 @@
|
||||
# Rust-Postgres
|
||||
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)
|
||||
|
||||
@ -198,16 +198,16 @@ types. The driver currently supports the following conversions:
|
||||
</tr>
|
||||
<tr>
|
||||
<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
|
||||
<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>)
|
||||
</td>
|
||||
<td>JSON, JSONB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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
|
||||
<a href="https://github.com/lifthrasiir/rust-chrono">chrono::NaiveDateTime</a>
|
||||
(<a href="#optional-features">optional</a>)
|
||||
@ -216,7 +216,7 @@ types. The driver currently supports the following conversions:
|
||||
</tr>
|
||||
<tr>
|
||||
<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<UTC></a>,
|
||||
<a href="https://github.com/lifthrasiir/rust-chrono">chrono::DateTime<Local></a>,
|
||||
and
|
||||
@ -241,7 +241,7 @@ types. The driver currently supports the following conversions:
|
||||
</tr>
|
||||
<tr>
|
||||
<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>)
|
||||
</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)
|
||||
support is provided optionally by the `rustc-serialize` feature, which adds
|
||||
`ToSql` and `FromSql` implementations for `rustc-serialize`'s `Json` type, and
|
||||
the `serde` feature, which adds implementations for `serde_json`'s `Value`
|
||||
type.
|
||||
the `serde_json` feature, which adds implementations for `serde_json`'s `Value`
|
||||
type. The `serde` feature provides implementations for the older
|
||||
`serde::json::Value` type.
|
||||
|
||||
### TIMESTAMP/TIMESTAMPTZ/DATE/TIME types
|
||||
|
||||
|
25
src/error.rs
25
src/error.rs
@ -48,10 +48,10 @@ impl DbErrorNew for DbError {
|
||||
None => match map.remove(&b'p') {
|
||||
Some(pos) => Some(ErrorPosition::Internal {
|
||||
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'),
|
||||
schema: map.remove(&b's'),
|
||||
@ -194,7 +194,7 @@ impl error::Error for DbError {
|
||||
#[derive(Debug)]
|
||||
pub enum ConnectError {
|
||||
/// An error creating `ConnectParams`.
|
||||
BadConnectParams(Box<error::Error+Sync+Send>),
|
||||
BadConnectParams(Box<error::Error + Sync + Send>),
|
||||
/// The `ConnectParams` was missing a user.
|
||||
MissingUser,
|
||||
/// An error from the Postgres server itself.
|
||||
@ -207,7 +207,7 @@ pub enum ConnectError {
|
||||
/// The Postgres server does not support SSL encryption.
|
||||
NoSslSupport,
|
||||
/// An error initializing the SSL session.
|
||||
SslError(Box<error::Error+Sync+Send>),
|
||||
SslError(Box<error::Error + Sync + Send>),
|
||||
/// An error communicating with the server.
|
||||
IoError(io::Error),
|
||||
}
|
||||
@ -220,7 +220,7 @@ impl fmt::Display for ConnectError {
|
||||
ConnectError::DbError(ref err) => write!(fmt, ": {}", err),
|
||||
ConnectError::SslError(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::MissingUser => "User missing in `ConnectParams`",
|
||||
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 => {
|
||||
"The server requested an unsupported authentication method"
|
||||
}
|
||||
@ -247,7 +248,7 @@ impl error::Error for ConnectError {
|
||||
ConnectError::DbError(ref err) => Some(&**err),
|
||||
ConnectError::SslError(ref err) => Some(&**err),
|
||||
ConnectError::IoError(ref err) => Some(err),
|
||||
_ => None
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -280,8 +281,8 @@ pub enum ErrorPosition {
|
||||
/// The byte position.
|
||||
position: u32,
|
||||
/// A query generated by the Postgres server.
|
||||
query: String
|
||||
}
|
||||
query: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// 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.
|
||||
InvalidColumn,
|
||||
/// 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 {
|
||||
@ -329,7 +330,7 @@ impl error::Error for Error {
|
||||
Error::DbError(ref err) => Some(&**err),
|
||||
Error::IoError(ref err) => Some(err),
|
||||
Error::Conversion(ref err) => Some(&**err),
|
||||
_ => None
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ pub trait NegotiateSsl {
|
||||
///
|
||||
/// The host portion of the connection parameters is provided for hostname
|
||||
/// verification.
|
||||
fn negotiate_ssl(&self, host: &str, stream: Stream)
|
||||
-> Result<Box<StreamWrapper>, Box<Error+Sync+Send>>;
|
||||
fn negotiate_ssl(&self,
|
||||
host: &str,
|
||||
stream: Stream)
|
||||
-> Result<Box<StreamWrapper>, Box<Error + Sync + Send>>;
|
||||
}
|
||||
|
@ -16,8 +16,10 @@ impl StreamWrapper for SslStream<Stream> {
|
||||
}
|
||||
|
||||
impl NegotiateSsl for SslContext {
|
||||
fn negotiate_ssl(&self, _: &str, stream: Stream)
|
||||
-> Result<Box<StreamWrapper>, Box<Error+Send+Sync>> {
|
||||
fn negotiate_ssl(&self,
|
||||
_: &str,
|
||||
stream: Stream)
|
||||
-> Result<Box<StreamWrapper>, Box<Error + Send + Sync>> {
|
||||
let stream = try!(SslStream::connect(self, stream));
|
||||
Ok(Box::new(stream))
|
||||
}
|
||||
|
405
src/lib.rs
405
src/lib.rs
@ -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)]
|
||||
|
||||
extern crate bufstream;
|
||||
@ -114,7 +114,7 @@ pub enum ConnectTarget {
|
||||
///
|
||||
/// Only available on Unix platforms with the `unix_socket` feature.
|
||||
#[cfg(feature = "unix_socket")]
|
||||
Unix(PathBuf)
|
||||
Unix(PathBuf),
|
||||
}
|
||||
|
||||
/// Authentication information.
|
||||
@ -148,17 +148,17 @@ pub struct ConnectParams {
|
||||
/// A trait implemented by types that can be converted into a `ConnectParams`.
|
||||
pub trait IntoConnectParams {
|
||||
/// 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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
Ok(url) => url.into_connect_params(),
|
||||
Err(err) => return Err(err.into()),
|
||||
@ -167,7 +167,7 @@ impl<'a> IntoConnectParams for &'a str {
|
||||
}
|
||||
|
||||
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 {
|
||||
host,
|
||||
port,
|
||||
@ -178,11 +178,11 @@ impl IntoConnectParams for Url {
|
||||
|
||||
#[cfg(feature = "unix_socket")]
|
||||
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)))
|
||||
}
|
||||
#[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())
|
||||
}
|
||||
|
||||
@ -194,7 +194,10 @@ impl IntoConnectParams for Url {
|
||||
};
|
||||
|
||||
let user = user.map(|url::UserInfo { user, pass }| {
|
||||
UserInfo { user: user, password: pass }
|
||||
UserInfo {
|
||||
user: user,
|
||||
password: pass,
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
/// `Connection::connect` for details of the `params` argument.
|
||||
///
|
||||
/// ## Example
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use postgres::{Connection, SslMode};
|
||||
@ -265,19 +268,21 @@ pub struct CancelData {
|
||||
/// thread::spawn(move || {
|
||||
/// conn.execute("SOME EXPENSIVE QUERY", &[]).unwrap();
|
||||
/// });
|
||||
/// # let _ =
|
||||
/// postgres::cancel_query(url, &SslMode::None, cancel_data);
|
||||
/// postgres::cancel_query(url, &SslMode::None, cancel_data).unwrap();
|
||||
/// ```
|
||||
pub fn cancel_query<T>(params: T, ssl: &SslMode, data: CancelData)
|
||||
-> result::Result<(), ConnectError>
|
||||
where T: IntoConnectParams {
|
||||
pub fn cancel_query<T>(params: T,
|
||||
ssl: &SslMode,
|
||||
data: CancelData)
|
||||
-> result::Result<(), ConnectError>
|
||||
where T: IntoConnectParams
|
||||
{
|
||||
let params = try!(params.into_connect_params().map_err(ConnectError::BadConnectParams));
|
||||
let mut socket = try!(priv_io::initialize_stream(¶ms, ssl));
|
||||
|
||||
try!(socket.write_message(&CancelRequest {
|
||||
code: message::CANCEL_CODE,
|
||||
process_id: data.process_id,
|
||||
secret_key: data.secret_key
|
||||
secret_key: data.secret_key,
|
||||
}));
|
||||
try!(socket.flush());
|
||||
|
||||
@ -291,7 +296,8 @@ fn bad_response() -> std_io::Error {
|
||||
|
||||
fn desynchronized() -> std_io::Error {
|
||||
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.
|
||||
@ -353,9 +359,9 @@ pub enum SslMode {
|
||||
/// The connection will not use SSL.
|
||||
None,
|
||||
/// 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.
|
||||
Require(Box<NegotiateSsl+std::marker::Sync+Send>),
|
||||
Require(Box<NegotiateSsl + std::marker::Sync + Send>),
|
||||
}
|
||||
|
||||
impl fmt::Debug for SslMode {
|
||||
@ -399,7 +405,8 @@ impl Drop for InnerConnection {
|
||||
|
||||
impl InnerConnection {
|
||||
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 stream = try!(priv_io::initialize_stream(¶ms, ssl));
|
||||
|
||||
@ -412,7 +419,10 @@ impl InnerConnection {
|
||||
next_stmt_id: 0,
|
||||
notice_handler: Box::new(LoggingNoticeHandler),
|
||||
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(),
|
||||
cached_statements: HashMap::new(),
|
||||
parameters: HashMap::new(),
|
||||
@ -432,9 +442,9 @@ impl InnerConnection {
|
||||
}
|
||||
|
||||
try!(conn.write_messages(&[StartupMessage {
|
||||
version: message::PROTOCOL_VERSION,
|
||||
parameters: &options
|
||||
}]));
|
||||
version: message::PROTOCOL_VERSION,
|
||||
parameters: &options,
|
||||
}]));
|
||||
|
||||
try!(conn.handle_auth(user));
|
||||
|
||||
@ -455,21 +465,22 @@ impl InnerConnection {
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn setup_typeinfo_query(&mut self) -> result::Result<(), ConnectError> {
|
||||
match self.raw_prepare(TYPEINFO_QUERY,
|
||||
"SELECT t.typname, t.typelem, r.rngsubtype, n.nspname \
|
||||
FROM pg_catalog.pg_type t \
|
||||
LEFT OUTER JOIN pg_catalog.pg_range r \
|
||||
ON r.rngtypid = t.oid \
|
||||
INNER JOIN pg_catalog.pg_namespace n \
|
||||
ON t.typnamespace = n.oid \
|
||||
LEFT OUTER JOIN pg_catalog.pg_range r ON \
|
||||
r.rngtypid = t.oid \
|
||||
INNER JOIN pg_catalog.pg_namespace n ON \
|
||||
t.typnamespace = n.oid \
|
||||
WHERE t.oid = $1") {
|
||||
Ok(..) => return Ok(()),
|
||||
Err(Error::IoError(e)) => return Err(ConnectError::IoError(e)),
|
||||
// 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(e)) => return Err(ConnectError::DbError(e)),
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
match self.raw_prepare(TYPEINFO_QUERY,
|
||||
@ -481,7 +492,7 @@ impl InnerConnection {
|
||||
Ok(..) => Ok(()),
|
||||
Err(Error::IoError(e)) => Err(ConnectError::IoError(e)),
|
||||
Err(Error::DbError(e)) => Err(ConnectError::DbError(e)),
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -505,12 +516,13 @@ impl InnerConnection {
|
||||
ParameterStatus { 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>> {
|
||||
debug_assert!(!self.desynchronized);
|
||||
loop {
|
||||
@ -523,7 +535,7 @@ impl InnerConnection {
|
||||
Some(ParameterStatus { 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 {
|
||||
pid: pid,
|
||||
channel: channel,
|
||||
payload: payload
|
||||
payload: payload,
|
||||
})
|
||||
}
|
||||
val => return Ok(val)
|
||||
val => return Ok(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -548,9 +560,7 @@ impl InnerConnection {
|
||||
AuthenticationOk => return Ok(()),
|
||||
AuthenticationCleartextPassword => {
|
||||
let pass = try!(user.password.ok_or(ConnectError::MissingPassword));
|
||||
try!(self.write_messages(&[PasswordMessage {
|
||||
password: &pass,
|
||||
}]));
|
||||
try!(self.write_messages(&[PasswordMessage { password: &pass }]));
|
||||
}
|
||||
AuthenticationMD5Password { salt } => {
|
||||
let pass = try!(user.password.ok_or(ConnectError::MissingPassword));
|
||||
@ -562,22 +572,20 @@ impl InnerConnection {
|
||||
let _ = hasher.input(output.as_bytes());
|
||||
let _ = hasher.input(&salt);
|
||||
let output = format!("md5{}", hasher.result_str());
|
||||
try!(self.write_messages(&[PasswordMessage {
|
||||
password: &output
|
||||
}]));
|
||||
try!(self.write_messages(&[PasswordMessage { password: &output }]));
|
||||
}
|
||||
AuthenticationKerberosV5
|
||||
| AuthenticationSCMCredential
|
||||
| AuthenticationGSS
|
||||
| AuthenticationSSPI => return Err(ConnectError::UnsupportedAuthentication),
|
||||
AuthenticationKerberosV5 |
|
||||
AuthenticationSCMCredential |
|
||||
AuthenticationGSS |
|
||||
AuthenticationSSPI => return Err(ConnectError::UnsupportedAuthentication),
|
||||
ErrorResponse { fields } => return DbError::new_connect(fields),
|
||||
_ => return Err(ConnectError::IoError(bad_response()))
|
||||
_ => return Err(ConnectError::IoError(bad_response())),
|
||||
}
|
||||
|
||||
match try!(self.read_message()) {
|
||||
AuthenticationOk => Ok(()),
|
||||
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>)> {
|
||||
debug!("preparing query with name `{}`: {}", stmt_name, query);
|
||||
|
||||
try!(self.write_messages(&[
|
||||
Parse {
|
||||
name: stmt_name,
|
||||
query: query,
|
||||
param_types: &[]
|
||||
},
|
||||
Describe {
|
||||
variant: b'S',
|
||||
name: stmt_name,
|
||||
},
|
||||
Sync]));
|
||||
try!(self.write_messages(&[Parse {
|
||||
name: stmt_name,
|
||||
query: query,
|
||||
param_types: &[],
|
||||
},
|
||||
Describe {
|
||||
variant: b'S',
|
||||
name: stmt_name,
|
||||
},
|
||||
Sync]));
|
||||
|
||||
match try!(self.read_message()) {
|
||||
ParseComplete => {}
|
||||
@ -617,7 +624,7 @@ impl InnerConnection {
|
||||
let raw_columns = match try!(self.read_message()) {
|
||||
RowDescription { descriptions } => descriptions,
|
||||
NoData => vec![],
|
||||
_ => bad_response!(self)
|
||||
_ => bad_response!(self),
|
||||
};
|
||||
|
||||
try!(self.wait_for_ready());
|
||||
@ -669,16 +676,15 @@ impl InnerConnection {
|
||||
}
|
||||
|
||||
fn close_statement(&mut self, name: &str, type_: u8) -> Result<()> {
|
||||
try!(self.write_messages(&[
|
||||
Close {
|
||||
variant: type_,
|
||||
name: name,
|
||||
},
|
||||
Sync]));
|
||||
try!(self.write_messages(&[Close {
|
||||
variant: type_,
|
||||
name: name,
|
||||
},
|
||||
Sync]));
|
||||
let resp = match try!(self.read_message()) {
|
||||
CloseComplete => Ok(()),
|
||||
ErrorResponse { fields } => DbError::new(fields),
|
||||
_ => bad_response!(self)
|
||||
_ => bad_response!(self),
|
||||
};
|
||||
try!(self.wait_for_ready());
|
||||
resp
|
||||
@ -699,57 +705,56 @@ impl InnerConnection {
|
||||
IsNull::Yes => None,
|
||||
IsNull::No => Some(buf),
|
||||
};
|
||||
try!(self.write_messages(&[
|
||||
Bind {
|
||||
portal: "",
|
||||
statement: TYPEINFO_QUERY,
|
||||
formats: &[1],
|
||||
values: &[value],
|
||||
result_formats: &[1]
|
||||
},
|
||||
Execute {
|
||||
portal: "",
|
||||
max_rows: 0,
|
||||
},
|
||||
Sync]));
|
||||
try!(self.write_messages(&[Bind {
|
||||
portal: "",
|
||||
statement: TYPEINFO_QUERY,
|
||||
formats: &[1],
|
||||
values: &[value],
|
||||
result_formats: &[1],
|
||||
},
|
||||
Execute {
|
||||
portal: "",
|
||||
max_rows: 0,
|
||||
},
|
||||
Sync]));
|
||||
match try!(self.read_message()) {
|
||||
BindComplete => {}
|
||||
ErrorResponse { fields } => {
|
||||
try!(self.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
}
|
||||
_ => bad_response!(self)
|
||||
_ => bad_response!(self),
|
||||
}
|
||||
let (name, elem_oid, rngsubtype, schema): (String, Oid, Option<Oid>, String) =
|
||||
match try!(self.read_message()) {
|
||||
DataRow { row } => {
|
||||
let ctx = SessionInfo::new(self);
|
||||
(try!(FromSql::from_sql_nullable(&Type::Name,
|
||||
row[0].as_ref().map(|r| &**r).as_mut(),
|
||||
&ctx)),
|
||||
try!(FromSql::from_sql_nullable(&Type::Oid,
|
||||
row[1].as_ref().map(|r| &**r).as_mut(),
|
||||
&ctx)),
|
||||
try!(FromSql::from_sql_nullable(&Type::Oid,
|
||||
row[2].as_ref().map(|r| &**r).as_mut(),
|
||||
&ctx)),
|
||||
try!(FromSql::from_sql_nullable(&Type::Name,
|
||||
row[3].as_ref().map(|r| &**r).as_mut(),
|
||||
&ctx)))
|
||||
}
|
||||
ErrorResponse { fields } => {
|
||||
try!(self.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
}
|
||||
_ => bad_response!(self)
|
||||
};
|
||||
match try!(self.read_message()) {
|
||||
DataRow { row } => {
|
||||
let ctx = SessionInfo::new(self);
|
||||
(try!(FromSql::from_sql_nullable(&Type::Name,
|
||||
row[0].as_ref().map(|r| &**r).as_mut(),
|
||||
&ctx)),
|
||||
try!(FromSql::from_sql_nullable(&Type::Oid,
|
||||
row[1].as_ref().map(|r| &**r).as_mut(),
|
||||
&ctx)),
|
||||
try!(FromSql::from_sql_nullable(&Type::Oid,
|
||||
row[2].as_ref().map(|r| &**r).as_mut(),
|
||||
&ctx)),
|
||||
try!(FromSql::from_sql_nullable(&Type::Name,
|
||||
row[3].as_ref().map(|r| &**r).as_mut(),
|
||||
&ctx)))
|
||||
}
|
||||
ErrorResponse { fields } => {
|
||||
try!(self.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
}
|
||||
_ => bad_response!(self),
|
||||
};
|
||||
match try!(self.read_message()) {
|
||||
CommandComplete { .. } => {}
|
||||
ErrorResponse { fields } => {
|
||||
try!(self.wait_for_ready());
|
||||
return DbError::new(fields);
|
||||
}
|
||||
_ => bad_response!(self)
|
||||
_ => bad_response!(self),
|
||||
}
|
||||
try!(self.wait_for_ready());
|
||||
|
||||
@ -758,7 +763,7 @@ impl InnerConnection {
|
||||
} else {
|
||||
match rngsubtype {
|
||||
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<()> {
|
||||
match try!(self.read_message()) {
|
||||
ReadyForQuery { .. } => Ok(()),
|
||||
_ => bad_response!(self)
|
||||
_ => bad_response!(self),
|
||||
}
|
||||
}
|
||||
|
||||
@ -788,16 +793,19 @@ impl InnerConnection {
|
||||
match try!(self.read_message()) {
|
||||
ReadyForQuery { .. } => break,
|
||||
DataRow { row } => {
|
||||
result.push(row.into_iter().map(|opt| {
|
||||
opt.map(|b| String::from_utf8_lossy(&b).into_owned())
|
||||
}).collect());
|
||||
result.push(row.into_iter()
|
||||
.map(|opt| {
|
||||
opt.map(|b| String::from_utf8_lossy(&b).into_owned())
|
||||
})
|
||||
.collect());
|
||||
}
|
||||
CopyInResponse { .. } => {
|
||||
try!(self.write_messages(&[
|
||||
CopyFail {
|
||||
message: "COPY queries cannot be directly executed",
|
||||
},
|
||||
Sync]));
|
||||
CopyInResponse { .. } |
|
||||
CopyOutResponse { .. } => {
|
||||
try!(self.write_messages(&[CopyFail {
|
||||
message: "COPY queries cannot be directly \
|
||||
executed",
|
||||
},
|
||||
Sync]));
|
||||
}
|
||||
ErrorResponse { fields } => {
|
||||
try!(self.wait_for_ready());
|
||||
@ -817,25 +825,26 @@ impl InnerConnection {
|
||||
}
|
||||
|
||||
fn _ensure_send() {
|
||||
fn _is_send<T: Send>() {}
|
||||
fn _is_send<T: Send>() {
|
||||
}
|
||||
_is_send::<Connection>();
|
||||
}
|
||||
|
||||
/// A connection to a Postgres database.
|
||||
pub struct Connection {
|
||||
conn: RefCell<InnerConnection>
|
||||
conn: RefCell<InnerConnection>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Connection {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let conn = self.conn.borrow();
|
||||
fmt.debug_struct("Connection")
|
||||
.field("cancel_data", &conn.cancel_data)
|
||||
.field("notifications", &conn.notifications.len())
|
||||
.field("transaction_depth", &conn.trans_depth)
|
||||
.field("desynchronized", &conn.desynchronized)
|
||||
.field("cached_statements", &conn.cached_statements.len())
|
||||
.finish()
|
||||
.field("cancel_data", &conn.cancel_data)
|
||||
.field("notifications", &conn.notifications.len())
|
||||
.field("transaction_depth", &conn.trans_depth)
|
||||
.field("desynchronized", &conn.desynchronized)
|
||||
.field("cached_statements", &conn.cached_statements.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@ -860,28 +869,27 @@ impl Connection {
|
||||
/// struct should be created manually and passed in. Note that Postgres
|
||||
/// does not support SSL over Unix sockets.
|
||||
///
|
||||
/// ## Examples
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use postgres::{Connection, SslMode};
|
||||
/// # fn f() -> Result<(), ::postgres::error::ConnectError> {
|
||||
/// use postgres::{Connection, SslMode};
|
||||
///
|
||||
/// let url = "postgresql://postgres:hunter2@localhost:2994/foodb";
|
||||
/// let conn = try!(Connection::connect(url, &SslMode::None));
|
||||
/// # Ok(()) };
|
||||
/// let conn = Connection::connect(url, &SslMode::None).unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use postgres::{Connection, SslMode};
|
||||
/// # fn f() -> Result<(), ::postgres::error::ConnectError> {
|
||||
/// use postgres::{Connection, SslMode};
|
||||
///
|
||||
/// let url = "postgresql://postgres@%2Frun%2Fpostgres";
|
||||
/// let conn = try!(Connection::connect(url, &SslMode::None));
|
||||
/// # Ok(()) };
|
||||
/// let conn = Connection::connect(url, &SslMode::None).unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use postgres::{Connection, UserInfo, ConnectParams, SslMode, ConnectTarget};
|
||||
/// use postgres::{Connection, UserInfo, ConnectParams, SslMode, ConnectTarget};
|
||||
///
|
||||
/// # #[cfg(feature = "unix_socket")]
|
||||
/// # fn f() -> Result<(), ::postgres::error::ConnectError> {
|
||||
/// # fn f() {
|
||||
/// # let some_crazy_path = Path::new("");
|
||||
/// let params = ConnectParams {
|
||||
/// target: ConnectTarget::Unix(some_crazy_path),
|
||||
@ -893,14 +901,13 @@ impl Connection {
|
||||
/// database: None,
|
||||
/// options: vec![],
|
||||
/// };
|
||||
/// let conn = try!(Connection::connect(params, &SslMode::None));
|
||||
/// # Ok(()) };
|
||||
/// let conn = Connection::connect(params, &SslMode::None).unwrap();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn connect<T>(params: T, ssl: &SslMode) -> result::Result<Connection, ConnectError>
|
||||
where T: IntoConnectParams {
|
||||
InnerConnection::connect(params, ssl).map(|conn| {
|
||||
Connection { conn: RefCell::new(conn) }
|
||||
})
|
||||
where T: IntoConnectParams
|
||||
{
|
||||
InnerConnection::connect(params, ssl).map(|conn| Connection { conn: RefCell::new(conn) })
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// not outlive that connection.
|
||||
///
|
||||
/// ## Example
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use postgres::{Connection, SslMode};
|
||||
/// # let x = 10i32;
|
||||
/// # let conn = Connection::connect("", &SslMode::None).unwrap();
|
||||
/// let maybe_stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1");
|
||||
/// let stmt = match maybe_stmt {
|
||||
/// Ok(stmt) => stmt,
|
||||
/// Err(err) => panic!("Error preparing statement: {:?}", err)
|
||||
/// };
|
||||
/// let stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1").unwrap();
|
||||
/// for row in stmt.query(&[&x]).unwrap() {
|
||||
/// let foo: String = row.get(0);
|
||||
/// println!("foo: {}", foo);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn prepare<'a>(&'a self, query: &str) -> Result<Statement<'a>> {
|
||||
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
|
||||
/// reducing the number of round trips to the Postgres backend.
|
||||
///
|
||||
/// ## Example
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use postgres::{Connection, SslMode};
|
||||
/// # fn f() -> postgres::Result<()> {
|
||||
/// # let x = 10i32;
|
||||
/// # let conn = Connection::connect("", &SslMode::None).unwrap();
|
||||
/// let stmt = try!(conn.prepare_cached("SELECT foo FROM bar WHERE baz = $1"));
|
||||
/// for row in try!(stmt.query(&[&x])) {
|
||||
/// println!("foo: {}", row.get::<_, String>(0));
|
||||
/// let stmt = conn.prepare_cached("SELECT foo FROM bar WHERE baz = $1").unwrap();
|
||||
/// for row in stmt.query(&[&x]).unwrap() {
|
||||
/// let foo: String = row.get(0);
|
||||
/// println!("foo: {}", foo);
|
||||
/// }
|
||||
/// # Ok(()) };
|
||||
/// ```
|
||||
pub fn prepare_cached<'a>(&'a self, query: &str) -> Result<Statement<'a>> {
|
||||
self.conn.borrow_mut().prepare_cached(query, self)
|
||||
@ -968,32 +976,30 @@ impl Connection {
|
||||
/// the connection for the duration of the transaction. The transaction
|
||||
/// is active until the `Transaction` object falls out of scope.
|
||||
///
|
||||
/// ## Note
|
||||
/// # Note
|
||||
/// A transaction will roll back by default. The `set_commit`,
|
||||
/// `set_rollback`, and `commit` methods alter this behavior.
|
||||
///
|
||||
/// ## Panics
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a transaction is already active.
|
||||
///
|
||||
/// ## Example
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use postgres::{Connection, SslMode};
|
||||
/// # fn foo() -> Result<(), postgres::error::Error> {
|
||||
/// # let conn = Connection::connect("", &SslMode::None).unwrap();
|
||||
/// let trans = try!(conn.transaction());
|
||||
/// try!(trans.execute("UPDATE foo SET bar = 10", &[]));
|
||||
/// let trans = conn.transaction().unwrap();
|
||||
/// trans.execute("UPDATE foo SET bar = 10", &[]).unwrap();
|
||||
/// // ...
|
||||
///
|
||||
/// try!(trans.commit());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// trans.commit().unwrap();
|
||||
/// ```
|
||||
pub fn transaction<'a>(&'a self) -> Result<Transaction<'a>> {
|
||||
let mut conn = self.conn.borrow_mut();
|
||||
check_desync!(conn);
|
||||
assert!(conn.trans_depth == 0, "`transaction` must be called on the active transaction");
|
||||
assert!(conn.trans_depth == 0,
|
||||
"`transaction` must be called on the active transaction");
|
||||
try!(conn.quick_query("BEGIN"));
|
||||
conn.trans_depth += 1;
|
||||
Ok(Transaction {
|
||||
@ -1006,7 +1012,9 @@ impl Connection {
|
||||
|
||||
/// 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.
|
||||
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.
|
||||
///
|
||||
/// This is a simple wrapper around `SHOW TRANSACTION ISOLATION LEVEL`.
|
||||
pub fn transaction_isolation(&self) -> Result<IsolationLevel> {
|
||||
let mut conn = self.conn.borrow_mut();
|
||||
check_desync!(conn);
|
||||
@ -1028,13 +1038,18 @@ impl Connection {
|
||||
///
|
||||
/// 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
|
||||
/// expected.
|
||||
pub fn execute(&self, query: &str, params: &[&ToSql]) -> Result<u64> {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -1045,33 +1060,32 @@ impl Connection {
|
||||
/// execution of batches of non-dynamic statements - for example, creation
|
||||
/// of a schema for a fresh database.
|
||||
///
|
||||
/// ## Warning
|
||||
/// # Warning
|
||||
///
|
||||
/// Prepared statements should be used for any SQL statement which contains
|
||||
/// user-specified data, as it provides functionality to safely embed that
|
||||
/// data in the statement. Do not form statements via string concatenation
|
||||
/// and feed them into this method.
|
||||
///
|
||||
/// ## Example
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use postgres::{Connection, Result};
|
||||
/// fn init_db(conn: &Connection) -> Result<()> {
|
||||
/// conn.batch_execute("
|
||||
/// CREATE TABLE person (
|
||||
/// id SERIAL PRIMARY KEY,
|
||||
/// name NOT NULL
|
||||
/// );
|
||||
/// # use postgres::{Connection, SslMode, Result};
|
||||
/// # let conn = Connection::connect("", &SslMode::None).unwrap();
|
||||
/// conn.batch_execute("
|
||||
/// CREATE TABLE person (
|
||||
/// id SERIAL PRIMARY KEY,
|
||||
/// name NOT NULL
|
||||
/// );
|
||||
///
|
||||
/// CREATE TABLE purchase (
|
||||
/// id SERIAL PRIMARY KEY,
|
||||
/// person INT NOT NULL REFERENCES person (id),
|
||||
/// time TIMESTAMPTZ NOT NULL,
|
||||
/// );
|
||||
/// CREATE TABLE purchase (
|
||||
/// id SERIAL PRIMARY KEY,
|
||||
/// person INT NOT NULL REFERENCES person (id),
|
||||
/// time TIMESTAMPTZ NOT NULL,
|
||||
/// );
|
||||
///
|
||||
/// CREATE INDEX ON purchase (time);
|
||||
/// ")
|
||||
/// }
|
||||
/// CREATE INDEX ON purchase (time);
|
||||
/// ").unwrap();
|
||||
/// ```
|
||||
pub fn batch_execute(&self, query: &str) -> Result<()> {
|
||||
self.conn.borrow_mut().quick_query(query).map(|_| ())
|
||||
@ -1133,9 +1147,9 @@ pub struct Transaction<'conn> {
|
||||
impl<'a> fmt::Debug for Transaction<'a> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Transaction")
|
||||
.field("commit", &self.commit.get())
|
||||
.field("depth", &self.depth)
|
||||
.finish()
|
||||
.field("commit", &self.commit.get())
|
||||
.field("depth", &self.depth)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1186,7 +1200,7 @@ impl<'conn> Transaction<'conn> {
|
||||
|
||||
/// Like `Connection::transaction`.
|
||||
///
|
||||
/// ## Panics
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if there is an active nested transaction.
|
||||
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);
|
||||
}
|
||||
CopyInResponse { .. } => {
|
||||
try!(conn.write_messages(&[
|
||||
CopyFail {
|
||||
message: "COPY queries cannot be directly executed",
|
||||
},
|
||||
Sync]));
|
||||
try!(conn.write_messages(&[CopyFail {
|
||||
message: "COPY queries cannot be directly executed",
|
||||
},
|
||||
Sync]));
|
||||
}
|
||||
CopyOutResponse { .. } => {
|
||||
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(
|
||||
std_io::ErrorKind::InvalidInput,
|
||||
"COPY queries cannot be directly executed")));
|
||||
return Err(Error::IoError(std_io::Error::new(std_io::ErrorKind::InvalidInput,
|
||||
"COPY queries cannot be directly \
|
||||
executed")));
|
||||
}
|
||||
_ => {
|
||||
conn.desynchronized = true;
|
||||
@ -1384,7 +1397,8 @@ trait LazyRowsNew<'trans, 'stmt> {
|
||||
row_limit: i32,
|
||||
more_rows: bool,
|
||||
finished: bool,
|
||||
trans: &'trans Transaction<'trans>) -> LazyRows<'trans, 'stmt>;
|
||||
trans: &'trans Transaction<'trans>)
|
||||
-> LazyRows<'trans, 'stmt>;
|
||||
}
|
||||
|
||||
trait SessionInfoNew<'a> {
|
||||
@ -1397,7 +1411,8 @@ trait StatementInternals<'conn> {
|
||||
param_types: Vec<Type>,
|
||||
columns: Vec<Column>,
|
||||
next_portal_id: Cell<u32>,
|
||||
finished: bool) -> Statement<'conn>;
|
||||
finished: bool)
|
||||
-> Statement<'conn>;
|
||||
|
||||
fn conn(&self) -> &'conn Connection;
|
||||
}
|
||||
|
95
src/md5.rs
95
src/md5.rs
@ -18,7 +18,7 @@ use std::iter::repeat;
|
||||
struct StepUp<T> {
|
||||
next: T,
|
||||
end: T,
|
||||
ammount: T
|
||||
ammount: T,
|
||||
}
|
||||
|
||||
impl <T> Iterator for StepUp<T> where
|
||||
@ -47,7 +47,7 @@ impl <T> RangeExt<T> for Range<T> where
|
||||
StepUp {
|
||||
next: self.start,
|
||||
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.
|
||||
fn read_u32v_le(dst: &mut[u32], input: &[u8]) {
|
||||
fn read_u32v_le(dst: &mut [u32], input: &[u8]) {
|
||||
assert!(dst.len() * 4 == input.len());
|
||||
unsafe {
|
||||
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
|
||||
/// 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);
|
||||
input = input.to_le();
|
||||
unsafe {
|
||||
@ -147,7 +147,7 @@ trait FixedBuffer {
|
||||
/// 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];
|
||||
|
||||
/// Get the current buffer.
|
||||
/// Get the current buffer.
|
||||
fn current_buffer<'s>(&'s mut self) -> &'s [u8];
|
||||
|
||||
/// Get the current position of the buffer.
|
||||
@ -247,14 +247,18 @@ struct FixedBuffer64 {
|
||||
buffer_idx: usize,
|
||||
}
|
||||
|
||||
impl Clone for FixedBuffer64 { fn clone(&self) -> FixedBuffer64 { *self } }
|
||||
impl Clone for FixedBuffer64 {
|
||||
fn clone(&self) -> FixedBuffer64 {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedBuffer64 {
|
||||
/// Create a new buffer
|
||||
fn new() -> FixedBuffer64 {
|
||||
FixedBuffer64 {
|
||||
buffer: [0u8; 64],
|
||||
buffer_idx: 0
|
||||
buffer_idx: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -266,7 +270,7 @@ struct Md5State {
|
||||
s0: u32,
|
||||
s1: u32,
|
||||
s2: u32,
|
||||
s3: u32
|
||||
s3: u32,
|
||||
}
|
||||
|
||||
impl Md5State {
|
||||
@ -275,7 +279,7 @@ impl Md5State {
|
||||
s0: 0x67452301,
|
||||
s1: 0xefcdab89,
|
||||
s2: 0x98badcfe,
|
||||
s3: 0x10325476
|
||||
s3: 0x10325476,
|
||||
}
|
||||
}
|
||||
|
||||
@ -341,8 +345,18 @@ impl Md5State {
|
||||
for i in (0..16).step_up(4) {
|
||||
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);
|
||||
c = op_g(c, 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);
|
||||
c = op_g(c,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -361,8 +375,18 @@ impl Md5State {
|
||||
for i in (0..16).step_up(4) {
|
||||
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);
|
||||
c = op_i(c, 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);
|
||||
c = op_i(c,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -374,28 +398,24 @@ impl Md5State {
|
||||
}
|
||||
|
||||
// Round 1 constants
|
||||
static C1: [u32; 16] = [
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821
|
||||
];
|
||||
static C1: [u32; 16] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a,
|
||||
0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821];
|
||||
|
||||
// Round 2 constants
|
||||
static C2: [u32; 16] = [
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a
|
||||
];
|
||||
static C2: [u32; 16] = [0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453,
|
||||
0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a];
|
||||
|
||||
// Round 3 constants
|
||||
static C3: [u32; 16] = [
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665
|
||||
];
|
||||
static C3: [u32; 16] = [0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9,
|
||||
0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665];
|
||||
|
||||
// Round 4 constants
|
||||
static C4: [u32; 16] = [
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
||||
];
|
||||
static C4: [u32; 16] = [0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
|
||||
0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391];
|
||||
|
||||
/// The MD5 Digest algorithm
|
||||
pub struct Md5 {
|
||||
@ -412,7 +432,7 @@ impl Md5 {
|
||||
length_bytes: 0,
|
||||
buffer: FixedBuffer64::new(),
|
||||
state: Md5State::new(),
|
||||
finished: false
|
||||
finished: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -422,8 +442,9 @@ impl Md5 {
|
||||
// 2^64 - ie: integer overflow is OK.
|
||||
self.length_bytes += input.len() as u64;
|
||||
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) {
|
||||
@ -436,7 +457,9 @@ impl Md5 {
|
||||
pub fn result(&mut self, out: &mut [u8]) {
|
||||
if !self.finished {
|
||||
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 >> 29) as u32);
|
||||
self_state.process_block(self.buffer.full_buffer());
|
||||
@ -449,12 +472,14 @@ impl Md5 {
|
||||
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 {
|
||||
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);
|
||||
buf[..].to_hex()
|
||||
}
|
||||
|
@ -20,14 +20,14 @@ pub enum BackendMessage {
|
||||
AuthenticationGSS,
|
||||
AuthenticationKerberosV5,
|
||||
AuthenticationMD5Password {
|
||||
salt: [u8; 4]
|
||||
salt: [u8; 4],
|
||||
},
|
||||
AuthenticationOk,
|
||||
AuthenticationSCMCredential,
|
||||
AuthenticationSSPI,
|
||||
BackendKeyData {
|
||||
process_id: u32,
|
||||
secret_key: u32
|
||||
secret_key: u32,
|
||||
},
|
||||
BindComplete,
|
||||
CloseComplete,
|
||||
@ -48,15 +48,15 @@ pub enum BackendMessage {
|
||||
column_formats: Vec<u16>,
|
||||
},
|
||||
DataRow {
|
||||
row: Vec<Option<Vec<u8>>>
|
||||
row: Vec<Option<Vec<u8>>>,
|
||||
},
|
||||
EmptyQueryResponse,
|
||||
ErrorResponse {
|
||||
fields: Vec<(u8, String)>
|
||||
fields: Vec<(u8, String)>,
|
||||
},
|
||||
NoData,
|
||||
NoticeResponse {
|
||||
fields: Vec<(u8, String)>
|
||||
fields: Vec<(u8, String)>,
|
||||
},
|
||||
NotificationResponse {
|
||||
pid: u32,
|
||||
@ -64,7 +64,7 @@ pub enum BackendMessage {
|
||||
payload: String,
|
||||
},
|
||||
ParameterDescription {
|
||||
types: Vec<Oid>
|
||||
types: Vec<Oid>,
|
||||
},
|
||||
ParameterStatus {
|
||||
parameter: String,
|
||||
@ -73,11 +73,11 @@ pub enum BackendMessage {
|
||||
ParseComplete,
|
||||
PortalSuspended,
|
||||
ReadyForQuery {
|
||||
_state: u8
|
||||
_state: u8,
|
||||
},
|
||||
RowDescription {
|
||||
descriptions: Vec<RowDescriptionEntry>
|
||||
}
|
||||
descriptions: Vec<RowDescriptionEntry>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct RowDescriptionEntry {
|
||||
@ -87,7 +87,7 @@ pub struct RowDescriptionEntry {
|
||||
pub type_oid: Oid,
|
||||
pub type_size: i16,
|
||||
pub type_modifier: i32,
|
||||
pub format: i16
|
||||
pub format: i16,
|
||||
}
|
||||
|
||||
pub enum FrontendMessage<'a> {
|
||||
@ -96,7 +96,7 @@ pub enum FrontendMessage<'a> {
|
||||
statement: &'a str,
|
||||
formats: &'a [i16],
|
||||
values: &'a [Option<Vec<u8>>],
|
||||
result_formats: &'a [i16]
|
||||
result_formats: &'a [i16],
|
||||
},
|
||||
CancelRequest {
|
||||
code: u32,
|
||||
@ -105,43 +105,43 @@ pub enum FrontendMessage<'a> {
|
||||
},
|
||||
Close {
|
||||
variant: u8,
|
||||
name: &'a str
|
||||
name: &'a str,
|
||||
},
|
||||
CopyData {
|
||||
data: &'a [u8],
|
||||
},
|
||||
CopyDone,
|
||||
CopyFail {
|
||||
message: &'a str
|
||||
message: &'a str,
|
||||
},
|
||||
Describe {
|
||||
variant: u8,
|
||||
name: &'a str
|
||||
name: &'a str,
|
||||
},
|
||||
Execute {
|
||||
portal: &'a str,
|
||||
max_rows: i32
|
||||
max_rows: i32,
|
||||
},
|
||||
Parse {
|
||||
name: &'a str,
|
||||
query: &'a str,
|
||||
param_types: &'a [Oid]
|
||||
param_types: &'a [Oid],
|
||||
},
|
||||
PasswordMessage {
|
||||
password: &'a str
|
||||
password: &'a str,
|
||||
},
|
||||
Query {
|
||||
query: &'a str
|
||||
query: &'a str,
|
||||
},
|
||||
SslRequest {
|
||||
code: u32
|
||||
code: u32,
|
||||
},
|
||||
StartupMessage {
|
||||
version: u32,
|
||||
parameters: &'a [(String, String)]
|
||||
parameters: &'a [(String, String)],
|
||||
},
|
||||
Sync,
|
||||
Terminate
|
||||
Terminate,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@ -158,7 +158,7 @@ impl<W: Write> WriteCStr for W {
|
||||
|
||||
#[doc(hidden)]
|
||||
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 {
|
||||
@ -285,8 +285,7 @@ impl<R: BufRead> ReadCStr for R {
|
||||
pub trait ReadMessage {
|
||||
fn read_message(&mut self) -> io::Result<BackendMessage>;
|
||||
|
||||
fn read_message_timeout(&mut self, timeout: Duration)
|
||||
-> io::Result<Option<BackendMessage>>;
|
||||
fn read_message_timeout(&mut self, timeout: Duration) -> io::Result<Option<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)
|
||||
}
|
||||
|
||||
fn read_message_timeout(&mut self, timeout: Duration)
|
||||
-> io::Result<Option<BackendMessage>> {
|
||||
fn read_message_timeout(&mut self, timeout: Duration) -> io::Result<Option<BackendMessage>> {
|
||||
try!(self.set_read_timeout(Some(timeout)));
|
||||
let ident = self.read_u8();
|
||||
try!(self.set_read_timeout(None));
|
||||
@ -328,16 +326,14 @@ impl<R: BufRead + ReadTimeout> ReadMessage for R {
|
||||
b'A' => NotificationResponse {
|
||||
pid: try!(rdr.read_u32::<BigEndian>()),
|
||||
channel: try!(rdr.read_cstr()),
|
||||
payload: try!(rdr.read_cstr())
|
||||
payload: try!(rdr.read_cstr()),
|
||||
},
|
||||
b'c' => BCopyDone,
|
||||
b'C' => CommandComplete { tag: try!(rdr.read_cstr()) },
|
||||
b'd' => {
|
||||
let mut data = vec![];
|
||||
try!(rdr.read_to_end(&mut data));
|
||||
BCopyData {
|
||||
data: data,
|
||||
}
|
||||
BCopyData { data: data }
|
||||
}
|
||||
b'D' => try!(read_data_row(&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'K' => BackendKeyData {
|
||||
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' => NoticeResponse { fields: try!(read_fields(&mut rdr)) },
|
||||
@ -374,7 +370,7 @@ impl<R: BufRead + ReadTimeout> ReadMessage for R {
|
||||
b's' => PortalSuspended,
|
||||
b'S' => ParameterStatus {
|
||||
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_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];
|
||||
try!(util::read_all(buf, &mut salt));
|
||||
AuthenticationMD5Password { salt: salt }
|
||||
},
|
||||
}
|
||||
6 => AuthenticationSCMCredential,
|
||||
7 => AuthenticationGSS,
|
||||
9 => AuthenticationSSPI,
|
||||
|
@ -20,14 +20,14 @@ pub struct Notification {
|
||||
|
||||
/// An iterator over asynchronous notifications.
|
||||
pub struct Notifications<'conn> {
|
||||
conn: &'conn Connection
|
||||
conn: &'conn Connection,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for Notifications<'a> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Notifications")
|
||||
.field("pending", &self.len())
|
||||
.finish()
|
||||
.field("pending", &self.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,9 +45,7 @@ impl<'conn> Notifications<'conn> {
|
||||
/// `None` if more notifications are received. However, those notifications
|
||||
/// will not be registered until the connection is used in some way.
|
||||
pub fn iter<'a>(&'a self) -> Iter<'a> {
|
||||
Iter {
|
||||
conn: self.conn,
|
||||
}
|
||||
Iter { conn: self.conn }
|
||||
}
|
||||
|
||||
/// Returns an iterator over notifications that blocks until one is
|
||||
@ -55,9 +53,7 @@ impl<'conn> Notifications<'conn> {
|
||||
///
|
||||
/// The iterator will never return `None`.
|
||||
pub fn blocking_iter<'a>(&'a self) -> BlockingIter<'a> {
|
||||
BlockingIter {
|
||||
conn: self.conn,
|
||||
}
|
||||
BlockingIter { conn: self.conn }
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
fn new(conn: &'conn Connection) -> Notifications<'conn> {
|
||||
Notifications {
|
||||
conn: conn,
|
||||
}
|
||||
Notifications { conn: conn }
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,11 +123,11 @@ impl<'a> Iterator for BlockingIter<'a> {
|
||||
Some(Ok(Notification {
|
||||
pid: pid,
|
||||
channel: channel,
|
||||
payload: payload
|
||||
payload: payload,
|
||||
}))
|
||||
}
|
||||
Err(err) => Some(Err(Error::IoError(err))),
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -164,12 +158,12 @@ impl<'a> Iterator for TimeoutIter<'a> {
|
||||
Some(Ok(Notification {
|
||||
pid: pid,
|
||||
channel: channel,
|
||||
payload: payload
|
||||
payload: payload,
|
||||
}))
|
||||
}
|
||||
Ok(None) => None,
|
||||
Err(err) => Some(Err(Error::IoError(err))),
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::os::windows::io::{AsRawSocket, RawSocket};
|
||||
|
||||
use {SslMode, ConnectParams, ConnectTarget};
|
||||
use error::{ConnectError};
|
||||
use error::ConnectError;
|
||||
use io::{NegotiateSsl, StreamWrapper};
|
||||
use message::{self, WriteMessage};
|
||||
use message::FrontendMessage::SslRequest;
|
||||
@ -28,7 +28,8 @@ pub trait ReadTimeout {
|
||||
impl ReadTimeout for BufStream<Box<StreamWrapper>> {
|
||||
fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
|
||||
match self.get_ref().get_ref().0 {
|
||||
InternalStream::Tcp(ref s) => <TcpStream as TcpStreamExt>::set_read_timeout(s, timeout),
|
||||
InternalStream::Tcp(ref s) =>
|
||||
<TcpStream as TcpStreamExt>::set_read_timeout(s, timeout),
|
||||
#[cfg(feature = "unix_socket")]
|
||||
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)
|
||||
-> Result<Box<StreamWrapper>, ConnectError> {
|
||||
pub fn initialize_stream(params: &ConnectParams,
|
||||
ssl: &SslMode)
|
||||
-> Result<Box<StreamWrapper>, ConnectError> {
|
||||
let mut socket = Stream(try!(open_socket(params)));
|
||||
|
||||
let (ssl_required, negotiator) = match *ssl {
|
||||
@ -161,11 +163,11 @@ pub fn initialize_stream(params: &ConnectParams, ssl: &SslMode)
|
||||
let host = match params.target {
|
||||
ConnectTarget::Tcp(ref host) => host,
|
||||
#[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) {
|
||||
Ok(stream) => Ok(stream),
|
||||
Err(err) => Err(ConnectError::SslError(err))
|
||||
Err(err) => Err(ConnectError::SslError(err)),
|
||||
}
|
||||
}
|
||||
|
86
src/rows.rs
86
src/rows.rs
@ -7,13 +7,7 @@ use std::borrow::Cow;
|
||||
use std::slice;
|
||||
use std::vec;
|
||||
|
||||
use {Result,
|
||||
Transaction,
|
||||
read_rows,
|
||||
DbErrorNew,
|
||||
SessionInfoNew,
|
||||
RowsNew,
|
||||
LazyRowsNew,
|
||||
use {Result, Transaction, read_rows, DbErrorNew, SessionInfoNew, RowsNew, LazyRowsNew,
|
||||
StatementInternals};
|
||||
use types::{FromSql, SessionInfo};
|
||||
use stmt::{Statement, Column};
|
||||
@ -38,9 +32,9 @@ impl<'a> RowsNew<'a> for Rows<'a> {
|
||||
impl<'a> fmt::Debug for Rows<'a> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Rows")
|
||||
.field("columns", &self.columns())
|
||||
.field("rows", &self.data.len())
|
||||
.finish()
|
||||
.field("columns", &self.columns())
|
||||
.field("rows", &self.data.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,7 +65,7 @@ impl<'stmt> Rows<'stmt> {
|
||||
pub fn iter<'a>(&'a self) -> Iter<'a> {
|
||||
Iter {
|
||||
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> {
|
||||
IntoIter {
|
||||
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.
|
||||
pub struct Row<'a> {
|
||||
stmt: &'a Statement<'a>,
|
||||
data: Cow<'a, [Option<Vec<u8>>]>
|
||||
data: Cow<'a, [Option<Vec<u8>>]>,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for Row<'a> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Row")
|
||||
.field("statement", self.stmt)
|
||||
.finish()
|
||||
.field("statement", self.stmt)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,14 +195,18 @@ impl<'a> Row<'a> {
|
||||
///
|
||||
/// Returns an `Error` value if the index does not reference a column or
|
||||
/// 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 ty = self.stmt.columns()[idx].type_();
|
||||
if !<T as FromSql>::accepts(ty) {
|
||||
return Err(Error::WrongType(ty.clone()));
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
@ -217,35 +215,41 @@ impl<'a> Row<'a> {
|
||||
/// A field can be accessed by the name or index of its column, though
|
||||
/// 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
|
||||
/// not compatible with the Postgres type.
|
||||
///
|
||||
/// ## Example
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use postgres::{Connection, SslMode};
|
||||
/// # let conn = Connection::connect("", &SslMode::None).unwrap();
|
||||
/// # let stmt = conn.prepare("").unwrap();
|
||||
/// # let mut result = stmt.query(&[]).unwrap();
|
||||
/// # let row = result.iter().next().unwrap();
|
||||
/// let foo: i32 = row.get(0);
|
||||
/// let bar: String = row.get("bar");
|
||||
/// let stmt = conn.prepare("SELECT foo, bar from BAZ").unwrap();
|
||||
/// for row in stmt.query(&[]).unwrap() {
|
||||
/// let foo: i32 = row.get(0);
|
||||
/// 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()) {
|
||||
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.
|
||||
///
|
||||
/// ## Panics
|
||||
/// # Panics
|
||||
///
|
||||
/// 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) {
|
||||
Some(idx) => self.data[idx].as_ref().map(|e| &**e),
|
||||
None => panic!("invalid index {:?}", idx),
|
||||
@ -303,7 +307,8 @@ impl<'trans, 'stmt> LazyRowsNew<'trans, 'stmt> for LazyRows<'trans, 'stmt> {
|
||||
row_limit: i32,
|
||||
more_rows: bool,
|
||||
finished: bool,
|
||||
trans: &'trans Transaction<'trans>) -> LazyRows<'trans, 'stmt> {
|
||||
trans: &'trans Transaction<'trans>)
|
||||
-> LazyRows<'trans, 'stmt> {
|
||||
LazyRows {
|
||||
stmt: stmt,
|
||||
data: data,
|
||||
@ -311,7 +316,7 @@ impl<'trans, 'stmt> LazyRowsNew<'trans, 'stmt> for LazyRows<'trans, 'stmt> {
|
||||
row_limit: row_limit,
|
||||
more_rows: more_rows,
|
||||
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> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("LazyRows")
|
||||
.field("name", &self.name)
|
||||
.field("row_limit", &self.row_limit)
|
||||
.field("remaining_rows", &self.data.len())
|
||||
.field("more_rows", &self.more_rows)
|
||||
.finish()
|
||||
.field("name", &self.name)
|
||||
.field("row_limit", &self.row_limit)
|
||||
.field("remaining_rows", &self.data.len())
|
||||
.field("more_rows", &self.more_rows)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,12 +350,11 @@ impl<'trans, 'stmt> LazyRows<'trans, 'stmt> {
|
||||
fn execute(&mut self) -> Result<()> {
|
||||
let mut conn = self.stmt.conn().conn.borrow_mut();
|
||||
|
||||
try!(conn.write_messages(&[
|
||||
Execute {
|
||||
portal: &self.name,
|
||||
max_rows: self.row_limit
|
||||
},
|
||||
Sync]));
|
||||
try!(conn.write_messages(&[Execute {
|
||||
portal: &self.name,
|
||||
max_rows: self.row_limit,
|
||||
},
|
||||
Sync]));
|
||||
read_rows(&mut conn, &mut self.data).map(|more_rows| self.more_rows = more_rows)
|
||||
}
|
||||
|
||||
|
140
src/stmt.rs
140
src/stmt.rs
@ -28,10 +28,10 @@ pub struct Statement<'conn> {
|
||||
impl<'a> fmt::Debug for Statement<'a> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Statement")
|
||||
.field("name", &self.name)
|
||||
.field("parameter_types", &self.param_types)
|
||||
.field("columns", &self.columns)
|
||||
.finish()
|
||||
.field("name", &self.name)
|
||||
.field("parameter_types", &self.param_types)
|
||||
.field("columns", &self.columns)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,7 +47,8 @@ impl<'conn> StatementInternals<'conn> for Statement<'conn> {
|
||||
param_types: Vec<Type>,
|
||||
columns: Vec<Column>,
|
||||
next_portal_id: Cell<u32>,
|
||||
finished: bool) -> Statement<'conn> {
|
||||
finished: bool)
|
||||
-> Statement<'conn> {
|
||||
Statement {
|
||||
conn: conn,
|
||||
name: name,
|
||||
@ -81,7 +82,9 @@ impl<'conn> Statement<'conn> {
|
||||
"expected {} parameters but got {}",
|
||||
self.param_types.len(),
|
||||
params.len());
|
||||
debug!("executing statement {} with parameters: {:?}", self.name, params);
|
||||
debug!("executing statement {} with parameters: {:?}",
|
||||
self.name,
|
||||
params);
|
||||
let mut values = vec![];
|
||||
for (param, ty) in params.iter().zip(self.param_types.iter()) {
|
||||
let mut buf = vec![];
|
||||
@ -89,21 +92,20 @@ impl<'conn> Statement<'conn> {
|
||||
IsNull::Yes => values.push(None),
|
||||
IsNull::No => values.push(Some(buf)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try!(conn.write_messages(&[
|
||||
Bind {
|
||||
portal: portal_name,
|
||||
statement: &self.name,
|
||||
formats: &[1],
|
||||
values: &values,
|
||||
result_formats: &[1]
|
||||
},
|
||||
Execute {
|
||||
portal: portal_name,
|
||||
max_rows: row_limit
|
||||
},
|
||||
Sync]));
|
||||
try!(conn.write_messages(&[Bind {
|
||||
portal: portal_name,
|
||||
statement: &self.name,
|
||||
formats: &[1],
|
||||
values: &values,
|
||||
result_formats: &[1],
|
||||
},
|
||||
Execute {
|
||||
portal: portal_name,
|
||||
max_rows: row_limit,
|
||||
},
|
||||
Sync]));
|
||||
|
||||
match try!(conn.read_message()) {
|
||||
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)> {
|
||||
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.
|
||||
///
|
||||
/// ## Panics
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the number of parameters provided does not match the number
|
||||
/// expected.
|
||||
///
|
||||
/// ## Example
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use postgres::{Connection, SslMode};
|
||||
@ -154,10 +159,8 @@ impl<'conn> Statement<'conn> {
|
||||
/// # let bar = 1i32;
|
||||
/// # let baz = true;
|
||||
/// let stmt = conn.prepare("UPDATE foo SET bar = $1 WHERE baz = $2").unwrap();
|
||||
/// match stmt.execute(&[&bar, &baz]) {
|
||||
/// Ok(count) => println!("{} row(s) updated", count),
|
||||
/// Err(err) => println!("Error executing query: {:?}", err)
|
||||
/// }
|
||||
/// let rows_updated = stmt.execute(&[&bar, &baz]).unwrap();
|
||||
/// println!("{} rows updated", rows_updated);
|
||||
/// ```
|
||||
pub fn execute(&self, params: &[&ToSql]) -> Result<u64> {
|
||||
check_desync!(self.conn);
|
||||
@ -181,11 +184,11 @@ impl<'conn> Statement<'conn> {
|
||||
break;
|
||||
}
|
||||
CopyInResponse { .. } => {
|
||||
try!(conn.write_messages(&[
|
||||
CopyFail {
|
||||
message: "COPY queries cannot be directly executed",
|
||||
},
|
||||
Sync]));
|
||||
try!(conn.write_messages(&[CopyFail {
|
||||
message: "COPY queries cannot be directly \
|
||||
executed",
|
||||
},
|
||||
Sync]));
|
||||
}
|
||||
CopyOutResponse { .. } => {
|
||||
loop {
|
||||
@ -214,32 +217,26 @@ impl<'conn> Statement<'conn> {
|
||||
|
||||
/// Executes the prepared statement, returning the resulting rows.
|
||||
///
|
||||
/// ## Panics
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the number of parameters provided does not match the number
|
||||
/// expected.
|
||||
///
|
||||
/// ## Example
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use postgres::{Connection, SslMode};
|
||||
/// # let conn = Connection::connect("", &SslMode::None).unwrap();
|
||||
/// let stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1").unwrap();
|
||||
/// # let baz = true;
|
||||
/// let rows = match stmt.query(&[&baz]) {
|
||||
/// Ok(rows) => rows,
|
||||
/// Err(err) => panic!("Error running query: {:?}", err)
|
||||
/// };
|
||||
/// for row in &rows {
|
||||
/// for row in stmt.query(&[&baz]).unwrap() {
|
||||
/// let foo: i32 = row.get("foo");
|
||||
/// println!("foo: {}", foo);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn query<'a>(&'a self, params: &[&ToSql]) -> Result<Rows<'a>> {
|
||||
check_desync!(self.conn);
|
||||
self.inner_query("", 0, params).map(|(buf, _)| {
|
||||
Rows::new(self, buf.into_iter().collect())
|
||||
})
|
||||
self.inner_query("", 0, params).map(|(buf, _)| Rows::new(self, buf.into_iter().collect()))
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// `lazy_query`.
|
||||
///
|
||||
/// ## Panics
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the provided `Transaction` is not associated with the same
|
||||
/// `Connection` as this `Statement`, if the `Transaction` is not
|
||||
@ -318,9 +315,10 @@ impl<'conn> Statement<'conn> {
|
||||
loop {
|
||||
match try!(conn.read_message()) {
|
||||
ReadyForQuery { .. } => {
|
||||
return Err(Error::IoError(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"called `copy_in` on a non-`COPY FROM STDIN` statement")));
|
||||
return Err(Error::IoError(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"called `copy_in` on a \
|
||||
non-`COPY FROM STDIN` \
|
||||
statement")));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -339,20 +337,15 @@ impl<'conn> Statement<'conn> {
|
||||
match fill_copy_buf(&mut buf, r, &info) {
|
||||
Ok(0) => break,
|
||||
Ok(len) => {
|
||||
try_desync!(info.conn, info.conn.stream.write_message(
|
||||
&CopyData {
|
||||
data: &buf[..len],
|
||||
}));
|
||||
try_desync!(info.conn,
|
||||
info.conn.stream.write_message(&CopyData { data: &buf[..len] }));
|
||||
}
|
||||
Err(err) => {
|
||||
try!(info.conn.write_messages(&[
|
||||
CopyFail {
|
||||
message: "",
|
||||
},
|
||||
CopyDone,
|
||||
Sync]));
|
||||
try!(info.conn.write_messages(&[CopyFail { message: "" }, CopyDone, Sync]));
|
||||
match try!(info.conn.read_message()) {
|
||||
ErrorResponse { .. } => { /* expected from the CopyFail */ }
|
||||
ErrorResponse { .. } => {
|
||||
// expected from the CopyFail
|
||||
}
|
||||
_ => {
|
||||
info.conn.desynchronized = true;
|
||||
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();
|
||||
/// let stmt = conn.prepare("COPY people TO STDOUT").unwrap();
|
||||
/// 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");
|
||||
/// ```
|
||||
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()) {
|
||||
CopyOutResponse { format, column_formats } => (format, column_formats),
|
||||
CopyInResponse { .. } => {
|
||||
try!(conn.write_messages(&[
|
||||
CopyFail {
|
||||
message: "",
|
||||
},
|
||||
CopyDone,
|
||||
Sync]));
|
||||
try!(conn.write_messages(&[CopyFail { message: "" }, CopyDone, Sync]));
|
||||
match try!(conn.read_message()) {
|
||||
ErrorResponse { .. } => { /* expected from the CopyFail */ }
|
||||
ErrorResponse { .. } => {
|
||||
// expected from the CopyFail
|
||||
}
|
||||
_ => {
|
||||
conn.desynchronized = true;
|
||||
return Err(Error::IoError(bad_response()));
|
||||
}
|
||||
}
|
||||
try!(conn.wait_for_ready());
|
||||
return Err(Error::IoError(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"called `copy_out` on a non-`COPY TO STDOUT` statement")));
|
||||
return Err(Error::IoError(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"called `copy_out` on a non-`COPY TO \
|
||||
STDOUT` statement")));
|
||||
}
|
||||
ErrorResponse { fields } => {
|
||||
try!(conn.wait_for_ready());
|
||||
@ -437,9 +427,10 @@ impl<'conn> Statement<'conn> {
|
||||
loop {
|
||||
match try!(conn.read_message()) {
|
||||
ReadyForQuery { .. } => {
|
||||
return Err(Error::IoError(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"called `copy_out` on a non-`COPY TO STDOUT` statement")));
|
||||
return Err(Error::IoError(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"called `copy_out` on a \
|
||||
non-`COPY TO STDOUT` \
|
||||
statement")));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -472,7 +463,7 @@ impl<'conn> Statement<'conn> {
|
||||
}
|
||||
}
|
||||
}
|
||||
BCopyDone => {},
|
||||
BCopyDone => {}
|
||||
CommandComplete { tag } => {
|
||||
count = util::parse_update_count(tag);
|
||||
break;
|
||||
@ -512,8 +503,7 @@ impl<'conn> Statement<'conn> {
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_copy_buf<R: ReadWithInfo>(buf: &mut [u8], r: &mut R, info: &CopyInfo)
|
||||
-> io::Result<usize> {
|
||||
fn fill_copy_buf<R: ReadWithInfo>(buf: &mut [u8], r: &mut R, info: &CopyInfo) -> io::Result<usize> {
|
||||
let mut nread = 0;
|
||||
while nread < buf.len() {
|
||||
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)]
|
||||
pub struct Column {
|
||||
name: String,
|
||||
type_: Type
|
||||
type_: Type,
|
||||
}
|
||||
|
||||
impl ColumnNew for Column {
|
||||
|
@ -3,7 +3,8 @@ extern crate chrono;
|
||||
use std::error;
|
||||
use std::io::prelude::*;
|
||||
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 error::Error;
|
||||
@ -23,11 +24,15 @@ impl FromSql 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() {
|
||||
Some(time) => time,
|
||||
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));
|
||||
}
|
||||
};
|
||||
@ -49,8 +54,11 @@ impl FromSql for DateTime<UTC> {
|
||||
}
|
||||
|
||||
impl ToSql for DateTime<UTC> {
|
||||
fn to_sql<W: Write+?Sized>(&self, type_: &Type, mut w: &mut W, info: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
fn to_sql<W: Write + ?Sized>(&self,
|
||||
type_: &Type,
|
||||
mut w: &mut W,
|
||||
info: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
self.naive_utc().to_sql(type_, w, info)
|
||||
}
|
||||
|
||||
@ -68,8 +76,11 @@ impl FromSql for DateTime<Local> {
|
||||
}
|
||||
|
||||
impl ToSql for DateTime<Local> {
|
||||
fn to_sql<W: Write+?Sized>(&self, type_: &Type, mut w: &mut W, info: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
fn to_sql<W: Write + ?Sized>(&self,
|
||||
type_: &Type,
|
||||
mut w: &mut W,
|
||||
info: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
self.with_timezone(&UTC).to_sql(type_, w, info)
|
||||
}
|
||||
|
||||
@ -78,7 +89,9 @@ impl ToSql for DateTime<Local> {
|
||||
}
|
||||
|
||||
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>> {
|
||||
let utc = try!(DateTime::<UTC>::from_sql(type_, raw, info));
|
||||
Ok(utc.with_timezone(&FixedOffset::east(0)))
|
||||
@ -88,8 +101,11 @@ impl FromSql for DateTime<FixedOffset> {
|
||||
}
|
||||
|
||||
impl ToSql for DateTime<FixedOffset> {
|
||||
fn to_sql<W: Write+?Sized>(&self, type_: &Type, mut w: &mut W, info: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
fn to_sql<W: Write + ?Sized>(&self,
|
||||
type_: &Type,
|
||||
mut w: &mut W,
|
||||
info: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
self.with_timezone(&UTC).to_sql(type_, w, info)
|
||||
}
|
||||
|
||||
@ -107,10 +123,14 @@ impl FromSql 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();
|
||||
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));
|
||||
}
|
||||
|
||||
@ -132,12 +152,16 @@ impl FromSql 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 time = match delta.num_microseconds() {
|
||||
Some(time) => time,
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
417
src/types/mod.rs
417
src/types/mod.rs
@ -37,7 +37,7 @@ macro_rules! to_sql_checked {
|
||||
if !<Self as $crate::types::ToSql>::accepts(ty) {
|
||||
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> {
|
||||
fn new(conn: &'a InnerConnection) -> SessionInfo<'a> {
|
||||
SessionInfo {
|
||||
conn: conn
|
||||
}
|
||||
SessionInfo { conn: conn }
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,317 +174,317 @@ macro_rules! make_postgres_type {
|
||||
|
||||
// Values from pg_type.h
|
||||
make_postgres_type! {
|
||||
#[doc="BOOL - boolean, 'true'/'false'"]
|
||||
/// BOOL - boolean, 'true'/'false'
|
||||
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,
|
||||
#[doc="\"char\" - single character"]
|
||||
/// "char" - single character
|
||||
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,
|
||||
#[doc="INT8/BIGINT - ~18 digit integer, 8-byte storage"]
|
||||
/// INT8/BIGINT - ~18 digit integer, 8-byte storage
|
||||
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,
|
||||
#[doc="INT2VECTOR - array of int2, used in system tables"]
|
||||
/// INT2VECTOR - array of int2, used in system tables
|
||||
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,
|
||||
#[doc="REGPROC - registered procedure"]
|
||||
/// REGPROC - registered procedure
|
||||
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,
|
||||
#[doc="OID - object identifier(oid), maximum 4 billion"]
|
||||
/// OID - object identifier(oid), maximum 4 billion
|
||||
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,
|
||||
#[doc="XID - transaction id"]
|
||||
/// XID - transaction id
|
||||
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,
|
||||
#[doc="OIDVECTOR - array of oids, used in system tables"]
|
||||
/// OIDVECTOR - array of oids, used in system tables
|
||||
30: "oidvector" => OidVector: Kind::Array(Type::Oid),
|
||||
#[doc="PG_TYPE"]
|
||||
/// PG_TYPE
|
||||
71: "pg_type" => PgType: Kind::Simple,
|
||||
#[doc="PG_ATTRIBUTE"]
|
||||
/// PG_ATTRIBUTE
|
||||
75: "pg_attribute" => PgAttribute: Kind::Simple,
|
||||
#[doc="PG_PROC"]
|
||||
/// PG_PROC
|
||||
81: "pg_proc" => PgProc: Kind::Simple,
|
||||
#[doc="PG_CLASS"]
|
||||
/// PG_CLASS
|
||||
83: "pg_class" => PgClass: Kind::Simple,
|
||||
#[doc="JSON"]
|
||||
/// JSON
|
||||
114: "json" => Json: Kind::Simple,
|
||||
#[doc="XML - XML content"]
|
||||
/// XML - XML content
|
||||
142: "xml" => Xml: Kind::Simple,
|
||||
#[doc="XML[]"]
|
||||
/// 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,
|
||||
#[doc="JSON[]"]
|
||||
/// JSON[]
|
||||
199: "_json" => JsonArray: Kind::Array(Type::Json),
|
||||
#[doc="SMGR - storage manager"]
|
||||
/// SMGR - storage manager
|
||||
210: "smgr" => Smgr: Kind::Simple,
|
||||
#[doc="POINT - geometric point '(x, y)'"]
|
||||
/// POINT - geometric point '(x, y)'
|
||||
600: "point" => Point: Kind::Simple,
|
||||
#[doc="LSEG - geometric line segment '(pt1,pt2)'"]
|
||||
/// LSEG - geometric line segment '(pt1,pt2)'
|
||||
601: "lseg" => Lseg: Kind::Simple,
|
||||
#[doc="PATH - geometric path '(pt1,...)'"]
|
||||
/// PATH - geometric path '(pt1,...)'
|
||||
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,
|
||||
#[doc="POLYGON - geometric polygon '(pt1,...)'"]
|
||||
/// POLYGON - geometric polygon '(pt1,...)'
|
||||
604: "polygon" => Polygon: Kind::Simple,
|
||||
#[doc="LINE - geometric line"]
|
||||
/// LINE - geometric line
|
||||
628: "line" => Line: Kind::Simple,
|
||||
#[doc="LINE[]"]
|
||||
/// 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,
|
||||
#[doc="CIDR[]"]
|
||||
/// 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,
|
||||
#[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,
|
||||
#[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,
|
||||
#[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,
|
||||
#[doc="TINTERVAL - (abstime,abstime), time interval"]
|
||||
/// TINTERVAL - (abstime,abstime), time interval
|
||||
704: "tinterval" => Tinterval: Kind::Simple,
|
||||
#[doc="UNKNOWN"]
|
||||
/// UNKNOWN
|
||||
705: "unknown" => Unknown: Kind::Simple,
|
||||
#[doc="CIRCLE - geometric circle '(center,radius)'"]
|
||||
/// CIRCLE - geometric circle '(center,radius)'
|
||||
718: "circle" => Circle: Kind::Simple,
|
||||
#[doc="CIRCLE[]"]
|
||||
/// 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,
|
||||
#[doc="MONEY[]"]
|
||||
/// 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,
|
||||
#[doc="INET - IP address/netmask, host address, netmask optional"]
|
||||
/// INET - IP address/netmask, host address, netmask optional
|
||||
869: "inet" => Inet: Kind::Simple,
|
||||
#[doc="BOOL[]"]
|
||||
/// BOOL[]
|
||||
1000: "_bool" => BoolArray: Kind::Array(Type::Bool),
|
||||
#[doc="BYTEA[]"]
|
||||
/// BYTEA[]
|
||||
1001: "_bytea" => ByteaArray: Kind::Array(Type::Bytea),
|
||||
#[doc="\"char\"[]"]
|
||||
/// "char"[]
|
||||
1002: "_char" => CharArray: Kind::Array(Type::Char),
|
||||
#[doc="NAME[]"]
|
||||
/// NAME[]
|
||||
1003: "_name" => NameArray: Kind::Array(Type::Name),
|
||||
#[doc="INT2[]"]
|
||||
/// INT2[]
|
||||
1005: "_int2" => Int2Array: Kind::Array(Type::Int2),
|
||||
#[doc="INT2VECTOR[]"]
|
||||
/// INT2VECTOR[]
|
||||
1006: "_int2vector" => Int2VectorArray: Kind::Array(Type::Int2Vector),
|
||||
#[doc="INT4[]"]
|
||||
/// INT4[]
|
||||
1007: "_int4" => Int4Array: Kind::Array(Type::Int4),
|
||||
#[doc="REGPROC[]"]
|
||||
/// REGPROC[]
|
||||
1008: "_regproc" => RegprocArray: Kind::Array(Type::Regproc),
|
||||
#[doc="TEXT[]"]
|
||||
/// TEXT[]
|
||||
1009: "_text" => TextArray: Kind::Array(Type::Text),
|
||||
#[doc="TID[]"]
|
||||
/// TID[]
|
||||
1010: "_tid" => TidArray: Kind::Array(Type::Tid),
|
||||
#[doc="XID[]"]
|
||||
/// XID[]
|
||||
1011: "_xid" => XidArray: Kind::Array(Type::Xid),
|
||||
#[doc="CID[]"]
|
||||
/// CID[]
|
||||
1012: "_cid" => CidArray: Kind::Array(Type::Cid),
|
||||
#[doc="OIDVECTOR[]"]
|
||||
/// OIDVECTOR[]
|
||||
1013: "_oidvector" => OidVectorArray: Kind::Array(Type::OidVector),
|
||||
#[doc="BPCHAR[]"]
|
||||
/// BPCHAR[]
|
||||
1014: "_bpchar" => BpcharArray: Kind::Array(Type::Bpchar),
|
||||
#[doc="VARCHAR[]"]
|
||||
/// VARCHAR[]
|
||||
1015: "_varchar" => VarcharArray: Kind::Array(Type::Varchar),
|
||||
#[doc="INT8[]"]
|
||||
/// INT8[]
|
||||
1016: "_int8" => Int8Array: Kind::Array(Type::Int8),
|
||||
#[doc="POINT[]"]
|
||||
/// POINT[]
|
||||
1017: "_point" => PointArray: Kind::Array(Type::Point),
|
||||
#[doc="LSEG[]"]
|
||||
/// LSEG[]
|
||||
1018: "_lseg" => LsegArray: Kind::Array(Type::Lseg),
|
||||
#[doc="PATH[]"]
|
||||
/// PATH[]
|
||||
1019: "_path" => PathArray: Kind::Array(Type::Path),
|
||||
#[doc="BOX[]"]
|
||||
/// BOX[]
|
||||
1020: "_box" => BoxArray: Kind::Array(Type::Box),
|
||||
#[doc="FLOAT4[]"]
|
||||
/// FLOAT4[]
|
||||
1021: "_float4" => Float4Array: Kind::Array(Type::Float4),
|
||||
#[doc="FLOAT8[]"]
|
||||
/// FLOAT8[]
|
||||
1022: "_float8" => Float8Array: Kind::Array(Type::Float8),
|
||||
#[doc="ABSTIME[]"]
|
||||
/// ABSTIME[]
|
||||
1023: "_abstime" => AbstimeArray: Kind::Array(Type::Abstime),
|
||||
#[doc="RELTIME[]"]
|
||||
/// RELTIME[]
|
||||
1024: "_reltime" => ReltimeArray: Kind::Array(Type::Reltime),
|
||||
#[doc="TINTERVAL[]"]
|
||||
/// TINTERVAL[]
|
||||
1025: "_tinterval" => TintervalArray: Kind::Array(Type::Tinterval),
|
||||
#[doc="POLYGON[]"]
|
||||
/// POLYGON[]
|
||||
1027: "_polygon" => PolygonArray: Kind::Array(Type::Polygon),
|
||||
#[doc="OID[]"]
|
||||
/// OID[]
|
||||
1028: "_oid" => OidArray: Kind::Array(Type::Oid),
|
||||
#[doc="ACLITEM - access control list"]
|
||||
/// ACLITEM - access control list
|
||||
1033: "aclitem" => Aclitem: Kind::Simple,
|
||||
#[doc="ACLITEM[]"]
|
||||
/// ACLITEM[]
|
||||
1034: "_aclitem" => AclitemArray: Kind::Array(Type::Aclitem),
|
||||
#[doc="MACADDR[]"]
|
||||
/// MACADDR[]
|
||||
1040: "_macaddr" => MacaddrArray: Kind::Array(Type::Macaddr),
|
||||
#[doc="INET[]"]
|
||||
/// 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,
|
||||
#[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,
|
||||
#[doc="DATE - date"]
|
||||
/// DATE - date
|
||||
1082: "date" => Date: Kind::Simple,
|
||||
#[doc="TIME - time of day"]
|
||||
/// TIME - time of day
|
||||
1083: "time" => Time: Kind::Simple,
|
||||
#[doc="TIMESTAMP - date and time"]
|
||||
/// TIMESTAMP - date and time
|
||||
1114: "timestamp" => Timestamp: Kind::Simple,
|
||||
#[doc="TIMESTAMP[]"]
|
||||
/// TIMESTAMP[]
|
||||
1115: "_timestamp" => TimestampArray: Kind::Array(Type::Timestamp),
|
||||
#[doc="DATE[]"]
|
||||
/// DATE[]
|
||||
1182: "_date" => DateArray: Kind::Array(Type::Date),
|
||||
#[doc="TIME[]"]
|
||||
/// 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,
|
||||
#[doc="TIMESTAMPTZ[]"]
|
||||
/// TIMESTAMPTZ[]
|
||||
1185: "_timestamptz" => TimestampTZArray: Kind::Array(Type::TimestampTZ),
|
||||
#[doc="INTERVAL - @ <number> <units>, time interval"]
|
||||
/// INTERVAL - @ <number> <units>, time interval
|
||||
1186: "interval" => Interval: Kind::Simple,
|
||||
#[doc="INTERVAL[]"]
|
||||
/// INTERVAL[]
|
||||
1187: "_interval" => IntervalArray: Kind::Array(Type::Interval),
|
||||
#[doc="NUMERIC[]"]
|
||||
/// NUMERIC[]
|
||||
1231: "_numeric" => NumericArray: Kind::Array(Type::Numeric),
|
||||
#[doc="CSTRING[]"]
|
||||
/// 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,
|
||||
#[doc="TIMETZ[]"]
|
||||
/// TIMETZ[]
|
||||
1270: "_timetz" => TimetzArray: Kind::Array(Type::Timetz),
|
||||
#[doc="BIT - fixed-length bit string"]
|
||||
/// BIT - fixed-length bit string
|
||||
1560: "bit" => Bit: Kind::Simple,
|
||||
#[doc="BIT[]"]
|
||||
/// BIT[]
|
||||
1561: "_bit" => BitArray: Kind::Array(Type::Bit),
|
||||
#[doc="VARBIT - variable-length bit string"]
|
||||
/// VARBIT - variable-length bit string
|
||||
1562: "varbit" => Varbit: Kind::Simple,
|
||||
#[doc="VARBIT[]"]
|
||||
/// 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,
|
||||
#[doc="REFCURSOR - reference to cursor (portal name)"]
|
||||
/// REFCURSOR - reference to cursor (portal name)
|
||||
1790: "refcursor" => Refcursor: Kind::Simple,
|
||||
#[doc="REFCURSOR[]"]
|
||||
/// REFCURSOR[]
|
||||
2201: "_refcursor" => RefcursorArray: Kind::Array(Type::Refcursor),
|
||||
#[doc="REGPROCEDURE - registered procedure (with args)"]
|
||||
/// REGPROCEDURE - registered procedure (with args)
|
||||
2202: "regprocedure" => Regprocedure: Kind::Simple,
|
||||
#[doc="REGOPER - registered operator"]
|
||||
/// REGOPER - registered operator
|
||||
2203: "regoper" => Regoper: Kind::Simple,
|
||||
#[doc="REGOPERATOR - registered operator (with args)"]
|
||||
/// REGOPERATOR - registered operator (with args)
|
||||
2204: "regoperator" => Regoperator: Kind::Simple,
|
||||
#[doc="REGCLASS - registered class"]
|
||||
/// REGCLASS - registered class
|
||||
2205: "regclass" => Regclass: Kind::Simple,
|
||||
#[doc="REGTYPE - registered type"]
|
||||
/// REGTYPE - registered type
|
||||
2206: "regtype" => Regtype: Kind::Simple,
|
||||
#[doc="REGPROCEDURE[]"]
|
||||
/// REGPROCEDURE[]
|
||||
2207: "_regprocedure" => RegprocedureArray: Kind::Array(Type::Regprocedure),
|
||||
#[doc="REGOPER[]"]
|
||||
/// REGOPER[]
|
||||
2208: "_regoper" => RegoperArray: Kind::Array(Type::Regoper),
|
||||
#[doc="REGOPERATOR[]"]
|
||||
/// REGOPERATOR[]
|
||||
2209: "_regoperator" => RegoperatorArray: Kind::Array(Type::Regoperator),
|
||||
#[doc="REGCLASS[]"]
|
||||
/// REGCLASS[]
|
||||
2210: "_regclass" => RegclassArray: Kind::Array(Type::Regclass),
|
||||
#[doc="REGTYPE[]"]
|
||||
/// REGTYPE[]
|
||||
2211: "_regtype" => RegtypeArray: Kind::Array(Type::Regtype),
|
||||
#[doc="RECORD"]
|
||||
/// RECORD
|
||||
2249: "record" => Record: Kind::Simple,
|
||||
#[doc="CSTRING"]
|
||||
/// CSTRING
|
||||
2275: "cstring" => Cstring: Kind::Simple,
|
||||
#[doc="ANY"]
|
||||
/// ANY
|
||||
2276: "any" => Any: Kind::Simple,
|
||||
#[doc="ANYARRAY"]
|
||||
/// ANYARRAY
|
||||
2277: "anyarray" => AnyArray: Kind::Array(Type::Any),
|
||||
#[doc="VOID"]
|
||||
/// VOID
|
||||
2278: "void" => Void: Kind::Simple,
|
||||
#[doc="TRIGGER"]
|
||||
/// TRIGGER
|
||||
2279: "trigger" => Trigger: Kind::Simple,
|
||||
#[doc="LANGUAGE_HANDLER"]
|
||||
/// LANGUAGE_HANDLER
|
||||
2280: "language_handler" => LanguageHandler: Kind::Simple,
|
||||
#[doc="INTERNAL"]
|
||||
/// INTERNAL
|
||||
2281: "internal" => Internal: Kind::Simple,
|
||||
#[doc="OPAQUE"]
|
||||
/// OPAQUE
|
||||
2282: "opaque" => Opaque: Kind::Simple,
|
||||
#[doc="ANYELEMENT"]
|
||||
/// ANYELEMENT
|
||||
2283: "anyelement" => Anyelement: Kind::Simple,
|
||||
#[doc="RECORD[]"]
|
||||
/// RECORD[]
|
||||
2287: "_record" => RecordArray: Kind::Array(Type::Record),
|
||||
#[doc="ANYNONARRAY"]
|
||||
/// ANYNONARRAY
|
||||
2776: "anynonarray" => Anynonarray: Kind::Simple,
|
||||
#[doc="TXID_SNAPSHOT[]"]
|
||||
/// TXID_SNAPSHOT[]
|
||||
2949: "_txid_snapshot" => TxidSnapshotArray: Kind::Array(Type::TxidSnapshot),
|
||||
#[doc="UUID - UUID datatype"]
|
||||
/// UUID - UUID datatype
|
||||
2950: "uuid" => Uuid: Kind::Simple,
|
||||
#[doc="TXID_SNAPSHOT - txid snapshot"]
|
||||
/// TXID_SNAPSHOT - txid snapshot
|
||||
2970: "txid_snapshot" => TxidSnapshot: Kind::Simple,
|
||||
#[doc="UUID[]"]
|
||||
/// UUID[]
|
||||
2951: "_uuid" => UuidArray: Kind::Array(Type::Uuid),
|
||||
#[doc="FDW_HANDLER"]
|
||||
/// FDW_HANDLER
|
||||
3115: "fdw_handler" => FdwHandler: Kind::Simple,
|
||||
#[doc="PG_LSN - PostgreSQL LSN datatype"]
|
||||
/// PG_LSN - PostgreSQL LSN datatype
|
||||
3220: "pg_lsn" => PgLsn: Kind::Simple,
|
||||
#[doc="PG_LSN[]"]
|
||||
/// PG_LSN[]
|
||||
3221: "_pg_lsn" => PgLsnArray: Kind::Array(Type::PgLsn),
|
||||
#[doc="ANYENUM"]
|
||||
/// ANYENUM
|
||||
3500: "anyenum" => Anyenum: Kind::Simple,
|
||||
#[doc="TSVECTOR - text representation for text search"]
|
||||
/// TSVECTOR - text representation for text search
|
||||
3614: "tsvector" => Tsvector: Kind::Simple,
|
||||
#[doc="TSQUERY - query representation for text search"]
|
||||
/// TSQUERY - query representation for text search
|
||||
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,
|
||||
#[doc="TSVECTOR[]"]
|
||||
/// TSVECTOR[]
|
||||
3643: "_tsvector" => TsvectorArray: Kind::Array(Type::Tsvector),
|
||||
#[doc="GTSVECTOR[]"]
|
||||
/// GTSVECTOR[]
|
||||
3644: "_gtsvector" => GtsvectorArray: Kind::Array(Type::Gtsvector),
|
||||
#[doc="TSQUERY[]"]
|
||||
/// TSQUERY[]
|
||||
3645: "_tsquery" => TsqueryArray: Kind::Array(Type::Tsquery),
|
||||
#[doc="REGCONFIG - registered text search configuration"]
|
||||
/// REGCONFIG - registered text search configuration
|
||||
3734: "regconfig" => Regconfig: Kind::Simple,
|
||||
#[doc="REGCONFIG[]"]
|
||||
/// REGCONFIG[]
|
||||
3735: "_regconfig" => RegconfigArray: Kind::Array(Type::Regconfig),
|
||||
#[doc="REGDICTIONARY - registered text search dictionary"]
|
||||
/// REGDICTIONARY - registered text search dictionary
|
||||
3769: "regdictionary" => Regdictionary: Kind::Simple,
|
||||
#[doc="REGDICTIONARY[]"]
|
||||
/// REGDICTIONARY[]
|
||||
3770: "_regdictionary" => RegdictionaryArray: Kind::Array(Type::Regdictionary),
|
||||
#[doc="JSONB"]
|
||||
/// JSONB
|
||||
3802: "jsonb" => Jsonb: Kind::Simple,
|
||||
#[doc="ANYRANGE"]
|
||||
/// ANYRANGE
|
||||
3831: "anyrange" => Anyrange: Kind::Simple,
|
||||
#[doc="JSONB[]"]
|
||||
/// JSONB[]
|
||||
3807: "_jsonb" => JsonbArray: Kind::Array(Type::Jsonb),
|
||||
#[doc="INT4RANGE - range of integers"]
|
||||
/// INT4RANGE - range of integers
|
||||
3904: "int4range" => Int4Range: Kind::Range(Type::Int4),
|
||||
#[doc="INT4RANGE[]"]
|
||||
/// INT4RANGE[]
|
||||
3905: "_int4range" => Int4RangeArray: Kind::Array(Type::Int4Range),
|
||||
#[doc="NUMRANGE - range of numerics"]
|
||||
/// NUMRANGE - range of numerics
|
||||
3906: "numrange" => NumRange: Kind::Range(Type::Numeric),
|
||||
#[doc="NUMRANGE[]"]
|
||||
/// 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),
|
||||
#[doc="TSRANGE[]"]
|
||||
/// 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),
|
||||
#[doc="TSTZRANGE[]"]
|
||||
/// TSTZRANGE[]
|
||||
3911: "_tstzrange" => TstzRangeArray: Kind::Array(Type::TstzRange),
|
||||
#[doc="DATERANGE - range of dates"]
|
||||
/// DATERANGE - range of dates
|
||||
3912: "daterange" => DateRange: Kind::Range(Type::Date),
|
||||
#[doc="DATERANGE[]"]
|
||||
/// DATERANGE[]
|
||||
3913: "_daterange" => DateRangeArray: Kind::Array(Type::DateRange),
|
||||
#[doc="INT8RANGE - range of bigints"]
|
||||
/// INT8RANGE - range of bigints
|
||||
3926: "int8range" => Int8Range: Kind::Range(Type::Int8),
|
||||
#[doc="INT8RANGE[]"]
|
||||
/// INT8RANGE[]
|
||||
3927: "_int8range" => Int8RangeArray: Kind::Array(Type::Int8Range),
|
||||
#[doc="EVENT_TRIGGER"]
|
||||
/// EVENT_TRIGGER
|
||||
3838: "event_trigger" => EventTrigger: Kind::Simple
|
||||
}
|
||||
|
||||
@ -596,7 +594,9 @@ impl error::Error for WasNull {
|
||||
/// nullable Postgres value.
|
||||
pub trait FromSql: Sized {
|
||||
/// ### 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> {
|
||||
match raw {
|
||||
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);
|
||||
|
||||
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>>> {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
@ -740,7 +742,7 @@ impl FromSql for HashMap<String, Option<String>> {
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
match *ty {
|
||||
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
|
||||
/// anything to `out`.
|
||||
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
|
||||
/// Postgres `Type`.
|
||||
@ -821,34 +824,33 @@ pub trait ToSql: fmt::Debug {
|
||||
///
|
||||
/// *All* implementations of this method should be generated by the
|
||||
/// `to_sql_checked!()` macro.
|
||||
fn to_sql_checked(&self, ty: &Type, out: &mut Write, ctx: &SessionInfo)
|
||||
-> Result<IsNull>;
|
||||
fn to_sql_checked(&self, ty: &Type, out: &mut Write, ctx: &SessionInfo) -> Result<IsNull>;
|
||||
}
|
||||
|
||||
impl<'a, T> ToSql for &'a T where T: ToSql {
|
||||
fn to_sql_checked(&self, ty: &Type, out: &mut Write, ctx: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
if !<&'a T as ToSql>::accepts(ty) {
|
||||
return Err(Error::WrongType(ty.clone()));
|
||||
}
|
||||
self.to_sql(ty, out, ctx)
|
||||
}
|
||||
to_sql_checked!();
|
||||
|
||||
|
||||
fn to_sql<W: Write + ?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
|
||||
fn to_sql<W: Write + ?Sized>(&self,
|
||||
ty: &Type,
|
||||
out: &mut W,
|
||||
ctx: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
(*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> {
|
||||
to_sql_checked!();
|
||||
|
||||
fn to_sql<W: Write+?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
fn to_sql<W: Write + ?Sized>(&self,
|
||||
ty: &Type,
|
||||
out: &mut W,
|
||||
ctx: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
match *self {
|
||||
Some(ref val) => val.to_sql(ty, out, ctx),
|
||||
None => Ok(IsNull::Yes),
|
||||
@ -863,8 +865,11 @@ impl<T: ToSql> ToSql for Option<T> {
|
||||
impl ToSql for bool {
|
||||
to_sql_checked!();
|
||||
|
||||
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> {
|
||||
try!(w.write_u8(*self as u8));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
@ -873,17 +878,9 @@ impl ToSql for bool {
|
||||
}
|
||||
|
||||
impl<'a> ToSql for &'a [u8] {
|
||||
// FIXME should use to_sql_checked!() but blocked on rust-lang/rust#24308
|
||||
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)
|
||||
}
|
||||
to_sql_checked!();
|
||||
|
||||
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));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
@ -894,8 +891,7 @@ impl<'a> ToSql for &'a [u8] {
|
||||
impl ToSql for Vec<u8> {
|
||||
to_sql_checked!();
|
||||
|
||||
fn to_sql<W: Write+?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
fn to_sql<W: Write + ?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
|
||||
<&[u8] as ToSql>::to_sql(&&**self, ty, w, ctx)
|
||||
}
|
||||
|
||||
@ -905,17 +901,9 @@ impl ToSql for Vec<u8> {
|
||||
}
|
||||
|
||||
impl<'a> ToSql for &'a str {
|
||||
// FIXME should use to_sql_checked!() but blocked on rust-lang/rust#24308
|
||||
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)
|
||||
}
|
||||
to_sql_checked!();
|
||||
|
||||
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()));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
@ -932,8 +920,7 @@ impl<'a> ToSql for &'a str {
|
||||
impl ToSql for String {
|
||||
to_sql_checked!();
|
||||
|
||||
fn to_sql<W: Write+?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
fn to_sql<W: Write + ?Sized>(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
|
||||
<&str as ToSql>::to_sql(&&**self, ty, w, ctx)
|
||||
}
|
||||
|
||||
@ -945,8 +932,11 @@ impl ToSql for String {
|
||||
impl ToSql for i8 {
|
||||
to_sql_checked!();
|
||||
|
||||
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> {
|
||||
try!(w.write_i8(*self));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
@ -980,8 +970,11 @@ to_primitive!(f64, write_f64, Type::Float8);
|
||||
impl ToSql for HashMap<String, Option<String>> {
|
||||
to_sql_checked!();
|
||||
|
||||
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> {
|
||||
try!(w.write_i32::<BigEndian>(try!(downcast(self.len()))));
|
||||
|
||||
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_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> {
|
||||
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))
|
||||
} else {
|
||||
Ok(len as i32)
|
||||
|
@ -12,7 +12,8 @@ impl FromSql for json::Json {
|
||||
if let Type::Jsonb = *ty {
|
||||
// We only support version 1 of the jsonb binary format
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -23,8 +24,11 @@ impl FromSql for json::Json {
|
||||
}
|
||||
|
||||
impl ToSql for json::Json {
|
||||
fn to_sql<W: Write+?Sized>(&self, ty: &Type, mut out: &mut W, _: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
fn to_sql<W: Write + ?Sized>(&self,
|
||||
ty: &Type,
|
||||
mut out: &mut W,
|
||||
_: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
if let Type::Jsonb = *ty {
|
||||
try!(out.write_u8(1));
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ impl FromSql for Value {
|
||||
if let Type::Jsonb = *ty {
|
||||
// We only support version 1 of the jsonb binary format
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -25,8 +26,11 @@ impl FromSql for Value {
|
||||
}
|
||||
|
||||
impl ToSql for Value {
|
||||
fn to_sql<W: Write+?Sized>(&self, ty: &Type, mut out: &mut W, _: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
fn to_sql<W: Write + ?Sized>(&self,
|
||||
ty: &Type,
|
||||
mut out: &mut W,
|
||||
_: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
if let Type::Jsonb = *ty {
|
||||
try!(out.write_u8(1));
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ impl FromSql for Value {
|
||||
if let Type::Jsonb = *ty {
|
||||
// We only support version 1 of the jsonb binary format
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -25,8 +26,11 @@ impl FromSql for Value {
|
||||
}
|
||||
|
||||
impl ToSql for Value {
|
||||
fn to_sql<W: Write+?Sized>(&self, ty: &Type, mut out: &mut W, _: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
fn to_sql<W: Write + ?Sized>(&self,
|
||||
ty: &Type,
|
||||
mut out: &mut W,
|
||||
_: &SessionInfo)
|
||||
-> Result<IsNull> {
|
||||
if let Type::Jsonb = *ty {
|
||||
try!(out.write_u8(1));
|
||||
}
|
||||
|
@ -38,7 +38,11 @@ impl<'a, T: 'a + ToSql> ToSql for Slice<'a, T> {
|
||||
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() {
|
||||
&Kind::Array(ref member) => member,
|
||||
_ => panic!("expected array type"),
|
||||
|
@ -31,7 +31,11 @@ impl FromSql 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;
|
||||
try!(w.write_i64::<BigEndian>(t));
|
||||
Ok(IsNull::No)
|
||||
|
@ -18,7 +18,7 @@ impl FromSql 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()));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
335
src/url.rs
335
src/url.rs
@ -23,12 +23,12 @@ pub struct Url {
|
||||
pub struct Path {
|
||||
pub path: String,
|
||||
pub query: Query,
|
||||
pub fragment: Option<String>
|
||||
pub fragment: Option<String>,
|
||||
}
|
||||
|
||||
pub struct UserInfo {
|
||||
pub user: String,
|
||||
pub pass: Option<String>
|
||||
pub pass: Option<String>,
|
||||
}
|
||||
|
||||
pub type Query = Vec<(String, String)>;
|
||||
@ -47,7 +47,7 @@ impl Url {
|
||||
user: user,
|
||||
host: host,
|
||||
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 url = Url::new(scheme.to_owned(),
|
||||
userinfo,
|
||||
host.to_owned(),
|
||||
port,
|
||||
path,
|
||||
query,
|
||||
fragment);
|
||||
userinfo,
|
||||
host.to_owned(),
|
||||
port,
|
||||
path,
|
||||
query,
|
||||
fragment);
|
||||
Ok(url)
|
||||
}
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn new(path: String,
|
||||
query: Query,
|
||||
fragment: Option<String>)
|
||||
-> Path {
|
||||
pub fn new(path: String, query: Query, fragment: Option<String>) -> Path {
|
||||
Path {
|
||||
path: path,
|
||||
query: query,
|
||||
@ -94,14 +91,21 @@ impl Path {
|
||||
// query and fragment
|
||||
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 {
|
||||
#[inline]
|
||||
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()) {
|
||||
(Some(one), Some(two)) => [one, two],
|
||||
_ => return Err(format!("Malformed input: found '%' \
|
||||
without two trailing bytes")),
|
||||
_ => return Err(format!("Malformed input: found '%' without two \
|
||||
trailing bytes")),
|
||||
};
|
||||
|
||||
// Only decode some characters if full_url:
|
||||
match str::from_utf8(&bytes).unwrap().from_hex().unwrap()[0] as char {
|
||||
// gen-delims:
|
||||
':' | '/' | '?' | '#' | '[' | ']' | '@' |
|
||||
|
||||
// sub-delims:
|
||||
'!' | '$' | '&' | '"' | '(' | ')' | '*' |
|
||||
'+' | ',' | ';' | '='
|
||||
if full_url => {
|
||||
':' |
|
||||
'/' |
|
||||
'?' |
|
||||
'#' |
|
||||
'[' |
|
||||
']' |
|
||||
'@' |
|
||||
'!' |
|
||||
'$' |
|
||||
'&' |
|
||||
'"' |
|
||||
'(' |
|
||||
')' |
|
||||
'*' |
|
||||
'+' |
|
||||
',' |
|
||||
';' |
|
||||
'=' if full_url => {
|
||||
out.push('%');
|
||||
out.push(bytes[0] 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),
|
||||
}
|
||||
@ -160,12 +176,11 @@ fn split_char_first(s: &str, c: char) -> (&str, &str) {
|
||||
}
|
||||
|
||||
fn query_from_str(rawquery: &str) -> DecodeResult<Query> {
|
||||
let mut query: Query = vec!();
|
||||
let mut query: Query = vec![];
|
||||
if !rawquery.is_empty() {
|
||||
for p in rawquery.split('&') {
|
||||
let (k, v) = split_char_first(p, '=');
|
||||
query.push((try!(decode_component(k)),
|
||||
try!(decode_component(v))));
|
||||
query.push((try!(decode_component(k)), 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)> {
|
||||
for (i,c) in rawurl.chars().enumerate() {
|
||||
for (i, c) in rawurl.chars().enumerate() {
|
||||
let result = match c {
|
||||
'A' ... 'Z'
|
||||
| 'a' ... 'z' => continue,
|
||||
'0' ... '9' | '+' | '-' | '.' => {
|
||||
if i != 0 { continue }
|
||||
'A'...'Z' |
|
||||
'a'...'z' => continue,
|
||||
'0'...'9' | '+' | '-' | '.' => {
|
||||
if i != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
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 {
|
||||
Err("url: Scheme cannot be empty.".to_owned())
|
||||
} 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()),
|
||||
@ -199,22 +216,21 @@ pub fn get_scheme(rawurl: &str) -> DecodeResult<(&str, &str)> {
|
||||
}
|
||||
|
||||
// returns userinfo, host, port, and unparsed part, or an error
|
||||
fn get_authority(rawurl: &str) ->
|
||||
DecodeResult<(Option<UserInfo>, &str, Option<u16>, &str)> {
|
||||
fn get_authority(rawurl: &str) -> DecodeResult<(Option<UserInfo>, &str, Option<u16>, &str)> {
|
||||
enum State {
|
||||
Start, // starting state
|
||||
PassHostPort, // could be in user or port
|
||||
Ip6Port, // either in ipv6 host or port
|
||||
Ip6Host, // are in an ipv6 host
|
||||
InHost, // are in a host - may be ipv6, but don't know yet
|
||||
InPort // are in port
|
||||
InPort, // are in port
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
enum Input {
|
||||
Digit, // all digits
|
||||
Hex, // digits and letters a-f
|
||||
Unreserved // all other legal characters
|
||||
Unreserved, // all other legal characters
|
||||
}
|
||||
|
||||
if !rawurl.starts_with("//") {
|
||||
@ -235,23 +251,35 @@ fn get_authority(rawurl: &str) ->
|
||||
let mut begin = 2;
|
||||
let mut end = len;
|
||||
|
||||
for (i,c) in rawurl.chars().enumerate()
|
||||
// ignore the leading '//' handled by early return
|
||||
.skip(2) {
|
||||
for (i, c) in rawurl.chars()
|
||||
.enumerate()
|
||||
.skip(2) {
|
||||
// deal with input class first
|
||||
match c {
|
||||
'0' ... '9' => (),
|
||||
'A' ... 'F'
|
||||
| 'a' ... 'f' => {
|
||||
'0'...'9' => (),
|
||||
'A'...'F' |
|
||||
'a'...'f' => {
|
||||
if input == Input::Digit {
|
||||
input = Input::Hex;
|
||||
}
|
||||
}
|
||||
'G' ... 'Z'
|
||||
| 'g' ... 'z'
|
||||
| '-' | '.' | '_' | '~' | '%'
|
||||
| '&' |'\'' | '(' | ')' | '+'
|
||||
| '!' | '*' | ',' | ';' | '=' => input = Input::Unreserved,
|
||||
'G'...'Z' |
|
||||
'g'...'z' |
|
||||
'-' |
|
||||
'.' |
|
||||
'_' |
|
||||
'~' |
|
||||
'%' |
|
||||
'&' |
|
||||
'\'' |
|
||||
'(' |
|
||||
')' |
|
||||
'+' |
|
||||
'!' |
|
||||
'*' |
|
||||
',' |
|
||||
';' |
|
||||
'=' => input = Input::Unreserved,
|
||||
':' | '@' | '?' | '#' | '/' => {
|
||||
// separators, don't change anything
|
||||
}
|
||||
@ -260,97 +288,96 @@ fn get_authority(rawurl: &str) ->
|
||||
|
||||
// now process states
|
||||
match c {
|
||||
':' => {
|
||||
colon_count += 1;
|
||||
match st {
|
||||
State::Start => {
|
||||
pos = i;
|
||||
st = State::PassHostPort;
|
||||
}
|
||||
State::PassHostPort => {
|
||||
// multiple colons means ipv6 address.
|
||||
if input == Input::Unreserved {
|
||||
return Err(
|
||||
"Illegal characters in IPv6 address.".to_owned());
|
||||
':' => {
|
||||
colon_count += 1;
|
||||
match st {
|
||||
State::Start => {
|
||||
pos = i;
|
||||
st = State::PassHostPort;
|
||||
}
|
||||
State::PassHostPort => {
|
||||
// multiple colons means ipv6 address.
|
||||
if input == Input::Unreserved {
|
||||
return Err("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;
|
||||
}
|
||||
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
|
||||
match st {
|
||||
State::Start => {
|
||||
let user = try!(decode_component(&rawurl[begin..i]));
|
||||
userinfo = Some(UserInfo::new(user, None));
|
||||
st = State::InHost;
|
||||
}
|
||||
State::PassHostPort => {
|
||||
let user = try!(decode_component(&rawurl[begin..pos]));
|
||||
let pass = try!(decode_component(&rawurl[pos+1..i]));
|
||||
userinfo = Some(UserInfo::new(user, Some(pass)));
|
||||
st = State::InHost;
|
||||
}
|
||||
_ => return Err("Invalid '@' in authority.".to_owned()),
|
||||
'@' => {
|
||||
input = Input::Digit; // reset input class
|
||||
colon_count = 0; // reset count
|
||||
match st {
|
||||
State::Start => {
|
||||
let user = try!(decode_component(&rawurl[begin..i]));
|
||||
userinfo = Some(UserInfo::new(user, None));
|
||||
st = State::InHost;
|
||||
}
|
||||
State::PassHostPort => {
|
||||
let user = try!(decode_component(&rawurl[begin..pos]));
|
||||
let pass = try!(decode_component(&rawurl[pos + 1..i]));
|
||||
userinfo = Some(UserInfo::new(user, Some(pass)));
|
||||
st = State::InHost;
|
||||
}
|
||||
_ => return Err("Invalid '@' in authority.".to_owned()),
|
||||
}
|
||||
begin = i + 1;
|
||||
}
|
||||
begin = i+1;
|
||||
}
|
||||
|
||||
'?' | '#' | '/' => {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
_ => ()
|
||||
'?' | '#' | '/' => {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// finish up
|
||||
match st {
|
||||
State::Start => host = &rawurl[begin..end],
|
||||
State::PassHostPort
|
||||
| State::Ip6Port => {
|
||||
if input != Input::Digit {
|
||||
return Err("Non-digit characters in port.".to_owned());
|
||||
State::Start => host = &rawurl[begin..end],
|
||||
State::PassHostPort |
|
||||
State::Ip6Port => {
|
||||
if input != Input::Digit {
|
||||
return Err("Non-digit characters in port.".to_owned());
|
||||
}
|
||||
host = &rawurl[begin..pos];
|
||||
port = Some(&rawurl[pos + 1..end]);
|
||||
}
|
||||
host = &rawurl[begin..pos];
|
||||
port = Some(&rawurl[pos+1..end]);
|
||||
}
|
||||
State::Ip6Host
|
||||
| State::InHost => host = &rawurl[begin..end],
|
||||
State::InPort => {
|
||||
if input != Input::Digit {
|
||||
return Err("Non-digit characters in port.".to_owned());
|
||||
State::Ip6Host |
|
||||
State::InHost => host = &rawurl[begin..end],
|
||||
State::InPort => {
|
||||
if input != Input::Digit {
|
||||
return Err("Non-digit characters in port.".to_owned());
|
||||
}
|
||||
port = Some(&rawurl[pos + 1..end]);
|
||||
}
|
||||
port = Some(&rawurl[pos+1..end]);
|
||||
}
|
||||
}
|
||||
|
||||
let rest = &rawurl[end..len];
|
||||
@ -359,8 +386,8 @@ fn get_authority(rawurl: &str) ->
|
||||
None => None,
|
||||
opt => match opt.and_then(|p| FromStr::from_str(p).ok()) {
|
||||
None => return Err(format!("Failed to parse port: {:?}", port)),
|
||||
opt => opt
|
||||
}
|
||||
opt => opt,
|
||||
},
|
||||
};
|
||||
|
||||
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)> {
|
||||
let len = rawurl.len();
|
||||
let mut end = len;
|
||||
for (i,c) in rawurl.chars().enumerate() {
|
||||
for (i, c) in rawurl.chars().enumerate() {
|
||||
match c {
|
||||
'A' ... 'Z'
|
||||
| 'a' ... 'z'
|
||||
| '0' ... '9'
|
||||
| '&' |'\'' | '(' | ')' | '.'
|
||||
| '@' | ':' | '%' | '/' | '+'
|
||||
| '!' | '*' | ',' | ';' | '='
|
||||
| '_' | '-' | '~' => continue,
|
||||
'?' | '#' => {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
_ => return Err("Invalid character in path.".to_owned())
|
||||
'A'...'Z' |
|
||||
'a'...'z' |
|
||||
'0'...'9' |
|
||||
'&' |
|
||||
'\'' |
|
||||
'(' |
|
||||
')' |
|
||||
'.' |
|
||||
'@' |
|
||||
':' |
|
||||
'%' |
|
||||
'/' |
|
||||
'+' |
|
||||
'!' |
|
||||
'*' |
|
||||
',' |
|
||||
';' |
|
||||
'=' |
|
||||
'_' |
|
||||
'-' |
|
||||
'~' => continue,
|
||||
'?' | '#' => {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
_ => return Err("Invalid character in path.".to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
if is_authority && end != 0 && !rawurl.starts_with("/") {
|
||||
Err("Non-empty path must begin with \
|
||||
'/' in presence of authority.".to_owned())
|
||||
Err("Non-empty path must begin with '/' in presence of authority.".to_owned())
|
||||
} else {
|
||||
Ok((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
|
||||
let fragment = match raw_fragment {
|
||||
"" => None,
|
||||
raw => Some(try!(decode_component(raw)))
|
||||
raw => Some(try!(decode_component(raw))),
|
||||
};
|
||||
|
||||
match before_fragment.chars().next() {
|
||||
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)),
|
||||
}
|
||||
}
|
||||
@ -426,4 +466,3 @@ impl FromStr for Path {
|
||||
Path::parse(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user