#![doc(html_root_url="https://sfackler.github.io/rust-postgres/doc/v0.9.3")]
#![warn(missing_docs)]
extern crate bufstream;
extern crate byteorder;
#[macro_use]
extern crate log;
extern crate phf;
extern crate rustc_serialize as serialize;
#[cfg(feature = "unix_socket")]
extern crate unix_socket;
extern crate debug_builders;
use bufstream::BufStream;
use md5::Md5;
use debug_builders::DebugStruct;
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
use std::collections::{VecDeque, HashMap};
use std::fmt;
use std::iter::IntoIterator;
use std::io as std_io;
use std::io::prelude::*;
use std::mem;
use std::result;
#[cfg(feature = "unix_socket")]
use std::path::PathBuf;
use error::{Error, ConnectError, SqlState, DbError};
use types::{ToSql, FromSql};
use io::{StreamWrapper, NegotiateSsl};
use types::{IsNull, Kind, Type, SessionInfo, Oid, Other, ReadWithInfo};
use message::BackendMessage::*;
use message::FrontendMessage::*;
use message::{FrontendMessage, BackendMessage, RowDescriptionEntry};
use message::{WriteMessage, ReadMessage};
use url::Url;
use rows::{Rows, LazyRows};
#[macro_use]
mod macros;
pub mod error;
pub mod io;
mod message;
mod priv_io;
mod url;
mod util;
pub mod types;
pub mod rows;
mod md5;
const TYPEINFO_QUERY: &'static str = "t";
pub type Result<T> = result::Result<T, Error>;
#[derive(Clone, Debug)]
pub enum ConnectTarget {
Tcp(String),
#[cfg(feature = "unix_socket")]
Unix(PathBuf)
}
#[derive(Clone, Debug)]
pub struct UserInfo {
pub user: String,
pub password: Option<String>,
}
#[derive(Clone, Debug)]
pub struct ConnectParams {
pub target: ConnectTarget,
pub port: Option<u16>,
pub user: Option<UserInfo>,
pub database: Option<String>,
pub options: Vec<(String, String)>,
}
pub trait IntoConnectParams {
fn into_connect_params(self) -> result::Result<ConnectParams, ConnectError>;
}
impl IntoConnectParams for ConnectParams {
fn into_connect_params(self) -> result::Result<ConnectParams, ConnectError> {
Ok(self)
}
}
impl<'a> IntoConnectParams for &'a str {
fn into_connect_params(self) -> result::Result<ConnectParams, ConnectError> {
match Url::parse(self) {
Ok(url) => url.into_connect_params(),
Err(err) => return Err(ConnectError::InvalidUrl(err)),
}
}
}
impl IntoConnectParams for Url {
fn into_connect_params(self) -> result::Result<ConnectParams, ConnectError> {
let Url {
host,
port,
user,
path: url::Path { mut path, query: options, .. },
..
} = self;
#[cfg(feature = "unix_socket")]
fn make_unix(maybe_path: String) -> result::Result<ConnectTarget, ConnectError> {
Ok(ConnectTarget::Unix(PathBuf::from(maybe_path)))
}
#[cfg(not(feature = "unix_socket"))]
fn make_unix(_: String) -> result::Result<ConnectTarget, ConnectError> {
Err(ConnectError::InvalidUrl("unix socket support requires the `unix_socket` feature"
.to_string()))
}
let maybe_path = try!(url::decode_component(&host).map_err(ConnectError::InvalidUrl));
let target = if maybe_path.starts_with("/") {
try!(make_unix(maybe_path))
} else {
ConnectTarget::Tcp(host)
};
let user = user.map(|url::UserInfo { user, pass }| {
UserInfo { user: user, password: pass }
});
let database = if path.is_empty() {
None
} else {
path.remove(0);
Some(path)
};
Ok(ConnectParams {
target: target,
port: port,
user: user,
database: database,
options: options,
})
}
}
pub trait HandleNotice: Send {
fn handle_notice(&mut self, notice: DbError);
}
#[derive(Copy, Clone, Debug)]
pub struct LoggingNoticeHandler;
impl HandleNotice for LoggingNoticeHandler {
fn handle_notice(&mut self, notice: DbError) {
info!("{}: {}", notice.severity(), notice.message());
}
}
#[derive(Clone, Debug)]
pub struct Notification {
pub pid: u32,
pub channel: String,
pub payload: String,
}
pub struct Notifications<'conn> {
conn: &'conn Connection
}
impl<'a> fmt::Debug for Notifications<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
DebugStruct::new(fmt, "Notifications")
.field("pending", &self.conn.conn.borrow().notifications.len())
.finish()
}
}
impl<'conn> Iterator for Notifications<'conn> {
type Item = Notification;
fn next(&mut self) -> Option<Notification> {
self.conn.conn.borrow_mut().notifications.pop_front()
}
}
impl<'conn> Notifications<'conn> {
pub fn next_block(&mut self) -> Result<Notification> {
if let Some(notification) = self.next() {
return Ok(notification);
}
let mut conn = self.conn.conn.borrow_mut();
check_desync!(conn);
match try!(conn.read_message_with_notification()) {
NotificationResponse { pid, channel, payload } => {
Ok(Notification {
pid: pid,
channel: channel,
payload: payload
})
}
_ => unreachable!()
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct CancelData {
pub process_id: u32,
pub secret_key: u32,
}
pub fn cancel_query<T>(params: T, ssl: &SslMode, data: CancelData)
-> result::Result<(), ConnectError>
where T: IntoConnectParams {
let params = try!(params.into_connect_params());
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
}));
try!(socket.flush());
Ok(())
}
fn bad_response() -> std_io::Error {
std_io::Error::new(std_io::ErrorKind::InvalidInput,
"the server returned an unexpected response")
}
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")
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IsolationLevel {
ReadUncommitted,
ReadCommitted,
RepeatableRead,
Serializable,
}
impl IsolationLevel {
fn to_set_query(&self) -> &'static str {
match *self {
IsolationLevel::ReadUncommitted => {
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"
}
IsolationLevel::ReadCommitted => {
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED"
}
IsolationLevel::RepeatableRead => {
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ"
}
IsolationLevel::Serializable => {
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE"
}
}
}
fn parse(raw: &str) -> Result<IsolationLevel> {
if raw.eq_ignore_ascii_case("READ UNCOMMITTED") {
Ok(IsolationLevel::ReadUncommitted)
} else if raw.eq_ignore_ascii_case("READ COMMITTED") {
Ok(IsolationLevel::ReadCommitted)
} else if raw.eq_ignore_ascii_case("REPEATABLE READ") {
Ok(IsolationLevel::RepeatableRead)
} else if raw.eq_ignore_ascii_case("SERIALIZABLE") {
Ok(IsolationLevel::Serializable)
} else {
Err(Error::IoError(bad_response()))
}
}
}
pub enum SslMode {
None,
Prefer(Box<NegotiateSsl+std::marker::Sync+Send>),
Require(Box<NegotiateSsl+std::marker::Sync+Send>),
}
impl fmt::Debug for SslMode {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
SslMode::None => fmt.write_str("None"),
SslMode::Prefer(..) => fmt.write_str("Prefer"),
SslMode::Require(..) => fmt.write_str("Require"),
}
}
}
#[derive(Clone)]
struct CachedStatement {
name: String,
param_types: Vec<Type>,
columns: Vec<Column>,
}
struct InnerConnection {
stream: BufStream<Box<StreamWrapper>>,
notice_handler: Box<HandleNotice>,
notifications: VecDeque<Notification>,
cancel_data: CancelData,
unknown_types: HashMap<Oid, Type>,
cached_statements: HashMap<String, CachedStatement>,
parameters: HashMap<String, String>,
next_stmt_id: u32,
trans_depth: u32,
desynchronized: bool,
finished: bool,
}
impl Drop for InnerConnection {
fn drop(&mut self) {
if !self.finished {
let _ = self.finish_inner();
}
}
}
impl InnerConnection {
fn connect<T>(params: T, ssl: &SslMode) -> result::Result<InnerConnection, ConnectError>
where T: IntoConnectParams {
let params = try!(params.into_connect_params());
let stream = try!(priv_io::initialize_stream(¶ms, ssl));
let ConnectParams { user, database, mut options, .. } = params;
let user = try!(user.ok_or(ConnectError::MissingUser));
let mut conn = InnerConnection {
stream: BufStream::new(stream),
next_stmt_id: 0,
notice_handler: Box::new(LoggingNoticeHandler),
notifications: VecDeque::new(),
cancel_data: CancelData { process_id: 0, secret_key: 0 },
unknown_types: HashMap::new(),
cached_statements: HashMap::new(),
parameters: HashMap::new(),
desynchronized: false,
finished: false,
trans_depth: 0,
};
options.push(("client_encoding".to_owned(), "UTF8".to_owned()));
options.push(("timezone".to_owned(), "GMT".to_owned()));
options.push(("user".to_owned(), user.user.clone()));
if let Some(database) = database {
options.push(("database".to_owned(), database));
}
try!(conn.write_messages(&[StartupMessage {
version: message::PROTOCOL_VERSION,
parameters: &options
}]));
try!(conn.handle_auth(user));
loop {
match try!(conn.read_message()) {
BackendKeyData { process_id, secret_key } => {
conn.cancel_data.process_id = process_id;
conn.cancel_data.secret_key = secret_key;
}
ReadyForQuery { .. } => break,
ErrorResponse { fields } => return DbError::new_connect(fields),
_ => return Err(ConnectError::IoError(bad_response())),
}
}
try!(conn.setup_typeinfo_query());
Ok(conn)
}
fn setup_typeinfo_query(&mut self) -> result::Result<(), ConnectError> {
match self.raw_prepare(TYPEINFO_QUERY,
"SELECT t.typname, t.typelem, r.rngsubtype \
FROM pg_catalog.pg_type t \
LEFT OUTER JOIN pg_catalog.pg_range r \
ON r.rngtypid = t.oid \
WHERE t.oid = $1") {
Ok(..) => return Ok(()),
Err(Error::IoError(e)) => return Err(ConnectError::IoError(e)),
Err(Error::DbError(ref e)) if e.code() == &SqlState::UndefinedTable => {}
Err(Error::DbError(e)) => return Err(ConnectError::DbError(e)),
_ => unreachable!()
}
match self.raw_prepare(TYPEINFO_QUERY,
"SELECT typname, typelem, NULL::OID \
FROM pg_catalog.pg_type \
WHERE oid = $1") {
Ok(..) => Ok(()),
Err(Error::IoError(e)) => Err(ConnectError::IoError(e)),
Err(Error::DbError(e)) => Err(ConnectError::DbError(e)),
_ => unreachable!()
}
}
fn write_messages(&mut self, messages: &[FrontendMessage]) -> std_io::Result<()> {
debug_assert!(!self.desynchronized);
for message in messages {
try_desync!(self, self.stream.write_message(message));
}
Ok(try_desync!(self, self.stream.flush()))
}
fn read_message_with_notification(&mut self) -> std_io::Result<BackendMessage> {
debug_assert!(!self.desynchronized);
loop {
match try_desync!(self, self.stream.read_message()) {
NoticeResponse { fields } => {
if let Ok(err) = DbError::new_raw(fields) {
self.notice_handler.handle_notice(err);
}
}
ParameterStatus { parameter, value } => {
self.parameters.insert(parameter, value);
}
val => return Ok(val)
}
}
}
fn read_message(&mut self) -> std_io::Result<BackendMessage> {
loop {
match try!(self.read_message_with_notification()) {
NotificationResponse { pid, channel, payload } => {
self.notifications.push_back(Notification {
pid: pid,
channel: channel,
payload: payload
})
}
val => return Ok(val)
}
}
}
fn handle_auth(&mut self, user: UserInfo) -> result::Result<(), ConnectError> {
match try!(self.read_message()) {
AuthenticationOk => return Ok(()),
AuthenticationCleartextPassword => {
let pass = try!(user.password.ok_or(ConnectError::MissingPassword));
try!(self.write_messages(&[PasswordMessage {
password: &pass,
}]));
}
AuthenticationMD5Password { salt } => {
let pass = try!(user.password.ok_or(ConnectError::MissingPassword));
let mut hasher = Md5::new();
let _ = hasher.input(pass.as_bytes());
let _ = hasher.input(user.user.as_bytes());
let output = hasher.result_str();
hasher.reset();
let _ = hasher.input(output.as_bytes());
let _ = hasher.input(&salt);
let output = format!("md5{}", hasher.result_str());
try!(self.write_messages(&[PasswordMessage {
password: &output
}]));
}
AuthenticationKerberosV5
| AuthenticationSCMCredential
| AuthenticationGSS
| AuthenticationSSPI => return Err(ConnectError::UnsupportedAuthentication),
ErrorResponse { fields } => return DbError::new_connect(fields),
_ => 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()))
}
}
fn set_notice_handler(&mut self, handler: Box<HandleNotice>) -> Box<HandleNotice> {
mem::replace(&mut self.notice_handler, handler)
}
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]));
match try!(self.read_message()) {
ParseComplete => {}
ErrorResponse { fields } => {
try!(self.wait_for_ready());
return DbError::new(fields);
}
_ => bad_response!(self),
}
let raw_param_types = match try!(self.read_message()) {
ParameterDescription { types } => types,
_ => bad_response!(self),
};
let raw_columns = match try!(self.read_message()) {
RowDescription { descriptions } => descriptions,
NoData => vec![],
_ => bad_response!(self)
};
try!(self.wait_for_ready());
let mut param_types = vec![];
for oid in raw_param_types {
param_types.push(try!(self.get_type(oid)));
}
let mut columns = vec![];
for RowDescriptionEntry { name, type_oid, .. } in raw_columns {
columns.push(Column {
name: name,
type_: try!(self.get_type(type_oid)),
});
}
Ok((param_types, columns))
}
fn make_stmt_name(&mut self) -> String {
let stmt_name = format!("s{}", self.next_stmt_id);
self.next_stmt_id += 1;
stmt_name
}
fn prepare<'a>(&mut self, query: &str, conn: &'a Connection) -> Result<Statement<'a>> {
let stmt_name = self.make_stmt_name();
let (param_types, columns) = try!(self.raw_prepare(&stmt_name, query));
Ok(Statement {
conn: conn,
name: stmt_name,
param_types: param_types,
columns: columns,
next_portal_id: Cell::new(0),
finished: false,
})
}
fn prepare_cached<'a>(&mut self, query: &str, conn: &'a Connection) -> Result<Statement<'a>> {
let stmt = self.cached_statements.get(query).cloned();
let CachedStatement { name, param_types, columns } = match stmt {
Some(stmt) => stmt,
None => {
let stmt_name = self.make_stmt_name();
let (param_types, columns) = try!(self.raw_prepare(&stmt_name, query));
let stmt = CachedStatement {
name: stmt_name,
param_types: param_types,
columns: columns,
};
self.cached_statements.insert(query.to_owned(), stmt.clone());
stmt
}
};
Ok(Statement {
conn: conn,
name: name,
param_types: param_types,
columns: columns,
next_portal_id: Cell::new(0),
finished: true, })
}
fn close_statement(&mut self, name: &str, type_: u8) -> Result<()> {
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)
};
try!(self.wait_for_ready());
resp
}
fn get_type(&mut self, oid: Oid) -> Result<Type> {
if let Some(ty) = Type::new(oid) {
return Ok(ty);
}
if let Some(ty) = self.unknown_types.get(&oid) {
return Ok(ty.clone());
}
let mut buf = vec![];
let value = match try!(oid.to_sql_checked(&Type::Oid, &mut buf, &SessionInfo::new(self))) {
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]));
match try!(self.read_message()) {
BindComplete => {}
ErrorResponse { fields } => {
try!(self.wait_for_ready());
return DbError::new(fields);
}
_ => bad_response!(self)
}
let (name, elem_oid, rngsubtype): (String, Oid, Option<Oid>) =
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)))
}
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)
}
try!(self.wait_for_ready());
let kind = if elem_oid != 0 {
Kind::Array(try!(self.get_type(elem_oid)))
} else {
match rngsubtype {
Some(oid) => Kind::Range(try!(self.get_type(oid))),
None => Kind::Simple
}
};
let type_ = Type::Other(Box::new(Other::new(name, oid, kind)));
self.unknown_types.insert(oid, type_.clone());
Ok(type_)
}
fn is_desynchronized(&self) -> bool {
self.desynchronized
}
fn wait_for_ready(&mut self) -> Result<()> {
match try!(self.read_message()) {
ReadyForQuery { .. } => Ok(()),
_ => bad_response!(self)
}
}
fn quick_query(&mut self, query: &str) -> Result<Vec<Vec<Option<String>>>> {
check_desync!(self);
debug!("executing query: {}", query);
try!(self.write_messages(&[Query { query: query }]));
let mut result = vec![];
loop {
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());
}
CopyInResponse { .. } => {
try!(self.write_messages(&[
CopyFail {
message: "COPY queries cannot be directly executed",
},
Sync]));
}
ErrorResponse { fields } => {
try!(self.wait_for_ready());
return DbError::new(fields);
}
_ => {}
}
}
Ok(result)
}
fn finish_inner(&mut self) -> Result<()> {
check_desync!(self);
try!(self.write_messages(&[Terminate]));
Ok(())
}
}
pub struct Connection {
conn: RefCell<InnerConnection>
}
impl fmt::Debug for Connection {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let conn = self.conn.borrow();
DebugStruct::new(fmt, "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()
}
}
impl Connection {
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) }
})
}
pub fn set_notice_handler(&self, handler: Box<HandleNotice>) -> Box<HandleNotice> {
self.conn.borrow_mut().set_notice_handler(handler)
}
pub fn notifications<'a>(&'a self) -> Notifications<'a> {
Notifications { conn: self }
}
pub fn prepare<'a>(&'a self, query: &str) -> Result<Statement<'a>> {
self.conn.borrow_mut().prepare(query, self)
}
pub fn prepare_cached<'a>(&'a self, query: &str) -> Result<Statement<'a>> {
self.conn.borrow_mut().prepare_cached(query, self)
}
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");
try!(conn.quick_query("BEGIN"));
conn.trans_depth += 1;
Ok(Transaction {
conn: self,
commit: Cell::new(false),
depth: 1,
finished: false,
})
}
pub fn set_transaction_isolation(&self, level: IsolationLevel) -> Result<()> {
self.batch_execute(level.to_set_query())
}
pub fn transaction_isolation(&self) -> Result<IsolationLevel> {
let mut conn = self.conn.borrow_mut();
check_desync!(conn);
let result = try!(conn.quick_query("SHOW TRANSACTION ISOLATION LEVEL"));
IsolationLevel::parse(result[0][0].as_ref().unwrap())
}
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 {
conn: self,
name: "".to_owned(),
param_types: param_types,
columns: columns,
next_portal_id: Cell::new(0),
finished: true, };
stmt.execute(params)
}
pub fn batch_execute(&self, query: &str) -> Result<()> {
self.conn.borrow_mut().quick_query(query).map(|_| ())
}
pub fn cancel_data(&self) -> CancelData {
self.conn.borrow().cancel_data
}
pub fn parameter(&self, param: &str) -> Option<String> {
self.conn.borrow().parameters.get(param).cloned()
}
pub fn is_desynchronized(&self) -> bool {
self.conn.borrow().is_desynchronized()
}
pub fn is_active(&self) -> bool {
self.conn.borrow().trans_depth == 0
}
pub fn finish(self) -> Result<()> {
let mut conn = self.conn.borrow_mut();
conn.finished = true;
conn.finish_inner()
}
}
pub struct Transaction<'conn> {
conn: &'conn Connection,
depth: u32,
commit: Cell<bool>,
finished: bool,
}
impl<'a> fmt::Debug for Transaction<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
DebugStruct::new(fmt, "Transaction")
.field("commit", &self.commit.get())
.field("depth", &self.depth)
.finish()
}
}
impl<'conn> Drop for Transaction<'conn> {
fn drop(&mut self) {
if !self.finished {
let _ = self.finish_inner();
}
}
}
impl<'conn> Transaction<'conn> {
fn finish_inner(&mut self) -> Result<()> {
let mut conn = self.conn.conn.borrow_mut();
debug_assert!(self.depth == conn.trans_depth);
let query = match (self.commit.get(), self.depth != 1) {
(false, true) => "ROLLBACK TO sp",
(false, false) => "ROLLBACK",
(true, true) => "RELEASE sp",
(true, false) => "COMMIT",
};
conn.trans_depth -= 1;
conn.quick_query(query).map(|_| ())
}
pub fn prepare(&self, query: &str) -> Result<Statement<'conn>> {
self.conn.prepare(query)
}
pub fn prepare_cached(&self, query: &str) -> Result<Statement<'conn>> {
self.conn.prepare_cached(query)
}
pub fn execute(&self, query: &str, params: &[&ToSql]) -> Result<u64> {
self.conn.execute(query, params)
}
pub fn batch_execute(&self, query: &str) -> Result<()> {
self.conn.batch_execute(query)
}
pub fn transaction<'a>(&'a self) -> Result<Transaction<'a>> {
let mut conn = self.conn.conn.borrow_mut();
check_desync!(conn);
assert!(conn.trans_depth == self.depth,
"`transaction` may only be called on the active transaction");
try!(conn.quick_query("SAVEPOINT sp"));
conn.trans_depth += 1;
Ok(Transaction {
conn: self.conn,
commit: Cell::new(false),
depth: self.depth + 1,
finished: false,
})
}
pub fn connection(&self) -> &'conn Connection {
self.conn
}
pub fn is_active(&self) -> bool {
self.conn.conn.borrow().trans_depth == self.depth
}
pub fn will_commit(&self) -> bool {
self.commit.get()
}
pub fn set_commit(&self) {
self.commit.set(true);
}
pub fn set_rollback(&self) {
self.commit.set(false);
}
pub fn commit(self) -> Result<()> {
self.set_commit();
self.finish()
}
pub fn finish(mut self) -> Result<()> {
self.finished = true;
self.finish_inner()
}
}
pub struct Statement<'conn> {
conn: &'conn Connection,
name: String,
param_types: Vec<Type>,
columns: Vec<Column>,
next_portal_id: Cell<u32>,
finished: bool,
}
impl<'a> fmt::Debug for Statement<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
DebugStruct::new(fmt, "Statement")
.field("name", &self.name)
.field("parameter_types", &self.param_types)
.field("columns", &self.columns)
.finish()
}
}
impl<'conn> Drop for Statement<'conn> {
fn drop(&mut self) {
let _ = self.finish_inner();
}
}
impl<'conn> Statement<'conn> {
fn finish_inner(&mut self) -> Result<()> {
if !self.finished {
self.finished = true;
let mut conn = self.conn.conn.borrow_mut();
check_desync!(conn);
conn.close_statement(&self.name, b'S')
} else {
Ok(())
}
}
fn inner_execute(&self, portal_name: &str, row_limit: i32, params: &[&ToSql]) -> Result<()> {
let mut conn = self.conn.conn.borrow_mut();
assert!(self.param_types().len() == params.len(),
"expected {} parameters but got {}",
self.param_types.len(),
params.len());
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![];
match try!(param.to_sql_checked(ty, &mut buf, &SessionInfo::new(&*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]));
match try!(conn.read_message()) {
BindComplete => Ok(()),
ErrorResponse { fields } => {
try!(conn.wait_for_ready());
DbError::new(fields)
}
_ => {
conn.desynchronized = true;
Err(Error::IoError(bad_response()))
}
}
}
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));
let mut buf = VecDeque::new();
let more_rows = try!(read_rows(&mut self.conn.conn.borrow_mut(), &mut buf));
Ok((buf, more_rows))
}
pub fn param_types(&self) -> &[Type] {
&self.param_types
}
pub fn columns(&self) -> &[Column] {
&self.columns
}
pub fn execute(&self, params: &[&ToSql]) -> Result<u64> {
check_desync!(self.conn);
try!(self.inner_execute("", 0, params));
let mut conn = self.conn.conn.borrow_mut();
let num;
loop {
match try!(conn.read_message()) {
DataRow { .. } => {}
ErrorResponse { fields } => {
try!(conn.wait_for_ready());
return DbError::new(fields);
}
CommandComplete { tag } => {
num = util::parse_update_count(tag);
break;
}
EmptyQueryResponse => {
num = 0;
break;
}
CopyInResponse { .. } => {
try!(conn.write_messages(&[
CopyFail {
message: "COPY queries cannot be directly executed",
},
Sync]));
}
_ => {
conn.desynchronized = true;
return Err(Error::IoError(bad_response()));
}
}
}
try!(conn.wait_for_ready());
Ok(num)
}
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())
})
}
pub fn lazy_query<'trans, 'stmt>(&'stmt self,
trans: &'trans Transaction,
params: &[&ToSql],
row_limit: i32)
-> Result<LazyRows<'trans, 'stmt>> {
assert!(self.conn as *const _ == trans.conn as *const _,
"the `Transaction` passed to `lazy_query` must be associated with the same \
`Connection` as the `Statement`");
let conn = self.conn.conn.borrow();
check_desync!(conn);
assert!(conn.trans_depth == trans.depth,
"`lazy_query` must be passed the active transaction");
drop(conn);
let id = self.next_portal_id.get();
self.next_portal_id.set(id + 1);
let portal_name = format!("{}p{}", self.name, id);
self.inner_query(&portal_name, row_limit, params).map(move |(data, more_rows)| {
LazyRows::new(self, data, portal_name, row_limit, more_rows, false, trans)
})
}
pub fn copy_in<R: ReadWithInfo>(&self, params: &[&ToSql], r: &mut R) -> Result<u64> {
try!(self.inner_execute("", 0, params));
let mut conn = self.conn.conn.borrow_mut();
match try!(conn.read_message()) {
CopyInResponse { .. } => {}
_ => {
loop {
match try!(conn.read_message()) {
ReadyForQuery { .. } => {
return Err(Error::IoError(std_io::Error::new(
std_io::ErrorKind::InvalidInput,
"called `copy_in` on a non-`COPY FROM STDIN` statement")));
}
_ => {}
}
}
}
}
let mut buf = [0; 16 * 1024];
loop {
match fill_copy_buf(&mut buf, r, &SessionInfo::new(&conn)) {
Ok(0) => break,
Ok(len) => {
try_desync!(conn, conn.stream.write_message(
&CopyData {
data: &buf[..len],
}));
}
Err(err) => {
try!(conn.write_messages(&[
CopyFail {
message: "",
},
CopyDone,
Sync]));
match try!(conn.read_message()) {
ErrorResponse { .. } => { }
_ => {
conn.desynchronized = true;
return Err(Error::IoError(bad_response()));
}
}
try!(conn.wait_for_ready());
return Err(Error::IoError(err));
}
}
}
try!(conn.write_messages(&[CopyDone, Sync]));
let num = match try!(conn.read_message()) {
CommandComplete { tag } => util::parse_update_count(tag),
ErrorResponse { fields } => {
try!(conn.wait_for_ready());
return DbError::new(fields);
}
_ => {
conn.desynchronized = true;
return Err(Error::IoError(bad_response()));
}
};
try!(conn.wait_for_ready());
Ok(num)
}
pub fn finish(mut self) -> Result<()> {
self.finish_inner()
}
}
fn fill_copy_buf<R: ReadWithInfo>(buf: &mut [u8], r: &mut R, info: &SessionInfo)
-> std_io::Result<usize> {
let mut nread = 0;
while nread < buf.len() {
match r.read_with_info(&mut buf[nread..], info) {
Ok(0) => break,
Ok(n) => nread += n,
Err(ref e) if e.kind() == std_io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(nread)
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Column {
name: String,
type_: Type
}
impl Column {
pub fn name(&self) -> &str {
&self.name
}
pub fn type_(&self) -> &Type {
&self.type_
}
}
fn read_rows(conn: &mut InnerConnection, buf: &mut VecDeque<Vec<Option<Vec<u8>>>>) -> Result<bool> {
let more_rows;
loop {
match try!(conn.read_message()) {
EmptyQueryResponse | CommandComplete { .. } => {
more_rows = false;
break;
}
PortalSuspended => {
more_rows = true;
break;
}
DataRow { row } => buf.push_back(row),
ErrorResponse { fields } => {
try!(conn.wait_for_ready());
return DbError::new(fields);
}
CopyInResponse { .. } => {
try!(conn.write_messages(&[
CopyFail {
message: "COPY queries cannot be directly executed",
},
Sync]));
}
_ => {
conn.desynchronized = true;
return Err(Error::IoError(bad_response()));
}
}
}
try!(conn.wait_for_ready());
Ok(more_rows)
}
pub trait GenericConnection {
fn prepare<'a>(&'a self, query: &str) -> Result<Statement<'a>>;
fn prepare_cached<'a>(&'a self, query: &str) -> Result<Statement<'a>>;
fn execute(&self, query: &str, params: &[&ToSql]) -> Result<u64>;
fn transaction<'a>(&'a self) -> Result<Transaction<'a>>;
fn batch_execute(&self, query: &str) -> Result<()>;
fn is_active(&self) -> bool;
}
impl GenericConnection for Connection {
fn prepare<'a>(&'a self, query: &str) -> Result<Statement<'a>> {
self.prepare(query)
}
fn prepare_cached<'a>(&'a self, query: &str) -> Result<Statement<'a>> {
self.prepare_cached(query)
}
fn execute(&self, query: &str, params: &[&ToSql]) -> Result<u64> {
self.execute(query, params)
}
fn transaction<'a>(&'a self) -> Result<Transaction<'a>> {
self.transaction()
}
fn batch_execute(&self, query: &str) -> Result<()> {
self.batch_execute(query)
}
fn is_active(&self) -> bool {
self.is_active()
}
}
impl<'a> GenericConnection for Transaction<'a> {
fn prepare<'b>(&'b self, query: &str) -> Result<Statement<'b>> {
self.prepare(query)
}
fn prepare_cached<'b>(&'b self, query: &str) -> Result<Statement<'b>> {
self.prepare_cached(query)
}
fn execute(&self, query: &str, params: &[&ToSql]) -> Result<u64> {
self.execute(query, params)
}
fn transaction<'b>(&'b self) -> Result<Transaction<'b>> {
self.transaction()
}
fn batch_execute(&self, query: &str) -> Result<()> {
self.batch_execute(query)
}
fn is_active(&self) -> bool {
self.is_active()
}
}
trait OtherNew {
fn new(name: String, oid: Oid, kind: Kind) -> Other;
}
trait DbErrorNew {
fn new_raw(fields: Vec<(u8, String)>) -> result::Result<DbError, ()>;
fn new_connect<T>(fields: Vec<(u8, String)>) -> result::Result<T, ConnectError>;
fn new<T>(fields: Vec<(u8, String)>) -> Result<T>;
}
trait TypeNew {
fn new(oid: Oid) -> Option<Type>;
}
trait RowsNew<'a> {
fn new(stmt: &'a Statement<'a>, data: Vec<Vec<Option<Vec<u8>>>>) -> Rows<'a>;
}
trait LazyRowsNew<'trans, 'stmt> {
fn new(stmt: &'stmt Statement<'stmt>,
data: VecDeque<Vec<Option<Vec<u8>>>>,
name: String,
row_limit: i32,
more_rows: bool,
finished: bool,
trans: &'trans Transaction<'trans>) -> LazyRows<'trans, 'stmt>;
}
trait SessionInfoNew<'a> {
fn new(conn: &'a InnerConnection) -> SessionInfo<'a>;
}