2013-09-30 05:03:11 +00:00
|
|
|
/*!
|
|
|
|
Rust-Postgres is a pure-Rust frontend for the popular PostgreSQL database. It
|
|
|
|
exposes a high level interface in the vein of JDBC or Go's `database/sql`
|
|
|
|
package.
|
|
|
|
|
2014-03-10 17:06:40 +00:00
|
|
|
```rust,no_run
|
2014-02-15 23:03:35 +00:00
|
|
|
extern crate postgres;
|
2014-03-09 06:01:24 +00:00
|
|
|
extern crate time;
|
2013-10-01 07:01:54 +00:00
|
|
|
|
2014-03-09 06:01:24 +00:00
|
|
|
use time::Timespec;
|
2013-09-30 05:03:11 +00:00
|
|
|
|
2014-03-26 04:01:38 +00:00
|
|
|
use postgres::{PostgresConnection, NoSsl};
|
2013-09-30 05:03:11 +00:00
|
|
|
use postgres::types::ToSql;
|
|
|
|
|
|
|
|
struct Person {
|
|
|
|
id: i32,
|
|
|
|
name: ~str,
|
2013-10-01 07:01:54 +00:00
|
|
|
time_created: Timespec,
|
2014-04-08 03:02:05 +00:00
|
|
|
data: Option<Vec<u8>>
|
2013-09-30 05:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
2013-11-11 01:59:25 +00:00
|
|
|
let conn = PostgresConnection::connect("postgres://postgres@localhost",
|
2014-03-28 04:08:22 +00:00
|
|
|
&NoSsl).unwrap();
|
2013-09-30 05:03:11 +00:00
|
|
|
|
2013-12-29 03:24:52 +00:00
|
|
|
conn.execute("CREATE TABLE person (
|
2013-10-01 07:01:54 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
name VARCHAR NOT NULL,
|
|
|
|
time_created TIMESTAMP NOT NULL,
|
|
|
|
data BYTEA
|
2014-03-28 04:08:22 +00:00
|
|
|
)", []).unwrap();
|
2013-09-30 05:03:11 +00:00
|
|
|
let me = Person {
|
|
|
|
id: 0,
|
|
|
|
name: ~"Steven",
|
2013-10-01 07:01:54 +00:00
|
|
|
time_created: time::get_time(),
|
2013-09-30 05:03:11 +00:00
|
|
|
data: None
|
|
|
|
};
|
2013-12-29 03:24:52 +00:00
|
|
|
conn.execute("INSERT INTO person (name, time_created, data)
|
2013-09-30 05:03:11 +00:00
|
|
|
VALUES ($1, $2, $3)",
|
2013-12-29 03:24:52 +00:00
|
|
|
[&me.name as &ToSql, &me.time_created as &ToSql,
|
2014-03-28 04:08:22 +00:00
|
|
|
&me.data as &ToSql]).unwrap();
|
2013-09-30 05:03:11 +00:00
|
|
|
|
2014-03-28 04:08:22 +00:00
|
|
|
let stmt = conn.prepare("SELECT id, name, time_created, data FROM person")
|
|
|
|
.unwrap();
|
|
|
|
for row in stmt.query([]).unwrap() {
|
2013-09-30 05:03:11 +00:00
|
|
|
let person = Person {
|
2013-12-03 03:53:40 +00:00
|
|
|
id: row[1],
|
|
|
|
name: row[2],
|
|
|
|
time_created: row[3],
|
|
|
|
data: row[4]
|
2013-09-30 05:03:11 +00:00
|
|
|
};
|
2013-10-01 07:01:54 +00:00
|
|
|
println!("Found person {}", person.name);
|
2013-09-30 05:03:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
*/
|
|
|
|
|
2014-03-29 21:33:11 +00:00
|
|
|
#![crate_id="github.com/sfackler/rust-postgres#postgres:0.0"]
|
|
|
|
#![crate_type="rlib"]
|
|
|
|
#![crate_type="dylib"]
|
|
|
|
#![doc(html_root_url="http://www.rust-ci.org/sfackler/rust-postgres/doc")]
|
2013-10-04 06:21:27 +00:00
|
|
|
|
2014-03-29 21:33:11 +00:00
|
|
|
#![warn(missing_doc)]
|
2013-09-30 02:47:30 +00:00
|
|
|
|
2014-03-29 21:33:11 +00:00
|
|
|
#![feature(macro_rules, struct_variant, phase)]
|
2013-10-08 04:11:54 +00:00
|
|
|
|
2014-02-15 23:03:35 +00:00
|
|
|
extern crate collections;
|
|
|
|
extern crate openssl;
|
|
|
|
extern crate serialize;
|
|
|
|
extern crate sync;
|
2014-02-22 07:18:39 +00:00
|
|
|
extern crate time;
|
2014-04-01 06:51:42 +00:00
|
|
|
extern crate phf;
|
2014-03-16 04:35:44 +00:00
|
|
|
extern crate url;
|
2014-03-18 04:05:04 +00:00
|
|
|
#[phase(syntax, link)]
|
|
|
|
extern crate log;
|
2014-04-01 06:51:42 +00:00
|
|
|
extern crate uuid;
|
2014-04-16 06:49:41 +00:00
|
|
|
#[phase(syntax)]
|
|
|
|
extern crate phf_mac;
|
2013-08-04 02:17:32 +00:00
|
|
|
|
2014-02-24 06:32:14 +00:00
|
|
|
use collections::{Deque, HashMap, RingBuf};
|
2014-03-16 04:35:44 +00:00
|
|
|
use url::{UserInfo, Url};
|
2014-01-03 01:13:52 +00:00
|
|
|
use openssl::crypto::hash::{MD5, Hasher};
|
2013-12-29 02:10:05 +00:00
|
|
|
use openssl::ssl::{SslStream, SslContext};
|
2014-02-14 03:26:52 +00:00
|
|
|
use serialize::hex::ToHex;
|
2014-02-08 02:17:40 +00:00
|
|
|
use std::cell::{Cell, RefCell};
|
2014-02-26 08:40:57 +00:00
|
|
|
use std::from_str::FromStr;
|
2014-02-07 04:58:47 +00:00
|
|
|
use std::io::{BufferedStream, IoResult};
|
2014-02-12 07:42:46 +00:00
|
|
|
use std::io::net;
|
2013-11-13 05:33:52 +00:00
|
|
|
use std::io::net::ip::{Port, SocketAddr};
|
|
|
|
use std::io::net::tcp::TcpStream;
|
2014-04-18 21:29:51 +00:00
|
|
|
use std::io::net::unix::UnixStream;
|
2014-02-12 07:42:46 +00:00
|
|
|
use std::mem;
|
2013-12-04 07:48:46 +00:00
|
|
|
use std::str;
|
2014-01-13 07:34:03 +00:00
|
|
|
use std::task;
|
2014-04-01 04:35:09 +00:00
|
|
|
use std::fmt;
|
2013-08-04 02:17:32 +00:00
|
|
|
|
2014-02-12 07:42:46 +00:00
|
|
|
use error::{DnsError,
|
|
|
|
InvalidUrl,
|
|
|
|
MissingPassword,
|
|
|
|
MissingUser,
|
|
|
|
NoSslSupport,
|
2014-02-07 04:58:47 +00:00
|
|
|
PgConnectDbError,
|
|
|
|
PgConnectStreamError,
|
2014-02-12 07:42:46 +00:00
|
|
|
PgDbError,
|
2014-03-30 00:01:41 +00:00
|
|
|
PgInvalidColumn,
|
2014-02-12 07:42:46 +00:00
|
|
|
PgStreamDesynchronized,
|
|
|
|
PgStreamError,
|
2014-03-28 04:39:03 +00:00
|
|
|
PgWrongParamCount,
|
2014-01-14 05:04:19 +00:00
|
|
|
PostgresConnectError,
|
2014-02-12 07:42:46 +00:00
|
|
|
PostgresDbError,
|
|
|
|
PostgresError,
|
2014-01-14 05:04:19 +00:00
|
|
|
SocketError,
|
|
|
|
SslError,
|
2014-03-26 04:01:38 +00:00
|
|
|
UnsupportedAuthentication,
|
|
|
|
PgWrongConnection};
|
2014-02-12 07:42:46 +00:00
|
|
|
use message::{AuthenticationCleartextPassword,
|
|
|
|
AuthenticationGSS,
|
2014-01-14 05:04:19 +00:00
|
|
|
AuthenticationKerberosV5,
|
|
|
|
AuthenticationMD5Password,
|
2014-02-12 07:42:46 +00:00
|
|
|
AuthenticationOk,
|
2014-01-14 05:04:19 +00:00
|
|
|
AuthenticationSCMCredential,
|
|
|
|
AuthenticationSSPI,
|
|
|
|
BackendKeyData,
|
2014-02-12 07:42:46 +00:00
|
|
|
BackendMessage,
|
2014-01-14 05:04:19 +00:00
|
|
|
BindComplete,
|
2014-03-26 04:01:38 +00:00
|
|
|
CommandComplete,
|
2014-01-14 05:04:19 +00:00
|
|
|
DataRow,
|
|
|
|
EmptyQueryResponse,
|
|
|
|
ErrorResponse,
|
|
|
|
NoData,
|
|
|
|
NoticeResponse,
|
|
|
|
NotificationResponse,
|
|
|
|
ParameterDescription,
|
|
|
|
ParameterStatus,
|
|
|
|
ParseComplete,
|
|
|
|
PortalSuspended,
|
|
|
|
ReadyForQuery,
|
2014-03-26 04:01:38 +00:00
|
|
|
RowDescription,
|
|
|
|
RowDescriptionEntry};
|
|
|
|
use message::{Bind,
|
|
|
|
CancelRequest,
|
|
|
|
Close,
|
2014-01-14 05:04:19 +00:00
|
|
|
Describe,
|
2014-03-26 04:01:38 +00:00
|
|
|
Execute,
|
2014-02-12 07:42:46 +00:00
|
|
|
FrontendMessage,
|
2014-01-14 05:04:19 +00:00
|
|
|
Parse,
|
|
|
|
PasswordMessage,
|
|
|
|
Query,
|
|
|
|
SslRequest,
|
2014-02-12 07:42:46 +00:00
|
|
|
StartupMessage,
|
2014-01-14 05:04:19 +00:00
|
|
|
Sync,
|
|
|
|
Terminate};
|
2014-02-16 02:59:16 +00:00
|
|
|
use message::{WriteMessage, ReadMessage};
|
2014-03-26 04:01:38 +00:00
|
|
|
use types::{Oid, PostgresType, ToSql, FromSql, PgUnknownType};
|
2013-07-25 07:10:18 +00:00
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
macro_rules! try_pg_conn(
|
2014-02-07 06:59:33 +00:00
|
|
|
($e:expr) => (
|
|
|
|
match $e {
|
|
|
|
Ok(ok) => ok,
|
|
|
|
Err(err) => return Err(PgConnectStreamError(err))
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
macro_rules! try_pg(
|
2014-02-07 06:59:33 +00:00
|
|
|
($e:expr) => (
|
|
|
|
match $e {
|
|
|
|
Ok(ok) => ok,
|
|
|
|
Err(err) => return Err(PgStreamError(err))
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
macro_rules! try_desync(
|
2014-02-10 06:45:26 +00:00
|
|
|
($e:expr) => (
|
|
|
|
match $e {
|
|
|
|
Ok(ok) => ok,
|
|
|
|
Err(err) => {
|
|
|
|
self.desynchronized = true;
|
|
|
|
return Err(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
macro_rules! check_desync(
|
2014-04-08 03:52:41 +00:00
|
|
|
($e:expr) => ({
|
|
|
|
if $e.canary() != CANARY {
|
|
|
|
fail!("PostgresConnection use after free. See mozilla/rust#13246.");
|
|
|
|
}
|
2014-02-10 06:45:26 +00:00
|
|
|
if $e.is_desynchronized() {
|
|
|
|
return Err(PgStreamDesynchronized);
|
|
|
|
}
|
2014-04-08 03:52:41 +00:00
|
|
|
})
|
2014-02-10 06:45:26 +00:00
|
|
|
)
|
|
|
|
|
2014-02-16 02:59:16 +00:00
|
|
|
pub mod error;
|
|
|
|
pub mod pool;
|
|
|
|
mod message;
|
|
|
|
pub mod types;
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test;
|
|
|
|
|
2013-10-21 00:32:14 +00:00
|
|
|
static DEFAULT_PORT: Port = 5432;
|
2014-04-08 03:52:41 +00:00
|
|
|
static CANARY: u32 = 0xdeadbeef;
|
2013-10-21 00:32:14 +00:00
|
|
|
|
2014-04-03 04:26:41 +00:00
|
|
|
/// A typedef of the result returned by many methods.
|
|
|
|
pub type PostgresResult<T> = Result<T, PostgresError>;
|
|
|
|
|
2013-09-30 02:12:20 +00:00
|
|
|
/// Trait for types that can handle Postgres notice messages
|
2013-09-12 02:57:26 +00:00
|
|
|
pub trait PostgresNoticeHandler {
|
2013-09-30 02:12:20 +00:00
|
|
|
/// Handle a Postgres notice message
|
2013-09-12 02:57:26 +00:00
|
|
|
fn handle(&mut self, notice: PostgresDbError);
|
|
|
|
}
|
|
|
|
|
2013-09-30 05:22:10 +00:00
|
|
|
/// A notice handler which logs at the `info` level.
|
2013-09-30 02:12:20 +00:00
|
|
|
///
|
|
|
|
/// This is the default handler used by a `PostgresConnection`.
|
2013-09-12 02:57:26 +00:00
|
|
|
pub struct DefaultNoticeHandler;
|
|
|
|
|
|
|
|
impl PostgresNoticeHandler for DefaultNoticeHandler {
|
|
|
|
fn handle(&mut self, notice: PostgresDbError) {
|
2013-10-21 04:06:12 +00:00
|
|
|
info!("{}: {}", notice.severity, notice.message);
|
2013-09-12 02:57:26 +00:00
|
|
|
}
|
2013-08-28 06:23:36 +00:00
|
|
|
}
|
|
|
|
|
2013-10-15 05:41:03 +00:00
|
|
|
/// An asynchronous notification
|
|
|
|
pub struct PostgresNotification {
|
|
|
|
/// The process ID of the notifying backend process
|
2014-04-03 05:56:16 +00:00
|
|
|
pub pid: i32,
|
2013-10-15 05:41:03 +00:00
|
|
|
/// The name of the channel that the notify has been raised on
|
2014-04-03 05:56:16 +00:00
|
|
|
pub channel: ~str,
|
2013-10-15 05:41:03 +00:00
|
|
|
/// The "payload" string passed from the notifying process
|
2014-04-03 05:56:16 +00:00
|
|
|
pub payload: ~str,
|
2013-10-15 05:41:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// An iterator over asynchronous notifications
|
2014-02-03 05:09:48 +00:00
|
|
|
pub struct PostgresNotifications<'conn> {
|
2014-04-01 06:51:42 +00:00
|
|
|
conn: &'conn PostgresConnection
|
2013-10-15 05:41:03 +00:00
|
|
|
}
|
|
|
|
|
2014-03-26 04:50:59 +00:00
|
|
|
impl<'conn> Iterator<PostgresNotification> for PostgresNotifications<'conn> {
|
2013-10-15 05:41:03 +00:00
|
|
|
/// Returns the oldest pending notification or `None` if there are none.
|
|
|
|
///
|
|
|
|
/// # Note
|
|
|
|
///
|
|
|
|
/// `next` may return `Some` notification after returning `None` if a new
|
|
|
|
/// notification was received.
|
|
|
|
fn next(&mut self) -> Option<PostgresNotification> {
|
2014-03-23 04:20:22 +00:00
|
|
|
self.conn.conn.borrow_mut().notifications.pop_front()
|
2013-10-15 05:41:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-21 00:32:14 +00:00
|
|
|
/// Contains information necessary to cancel queries for a session
|
|
|
|
pub struct PostgresCancelData {
|
|
|
|
/// The process ID of the session
|
2014-04-03 05:56:16 +00:00
|
|
|
pub process_id: i32,
|
2013-10-21 00:32:14 +00:00
|
|
|
/// The secret key for the session
|
2014-04-03 05:56:16 +00:00
|
|
|
pub secret_key: i32,
|
2013-10-21 00:32:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempts to cancel an in-progress query.
|
|
|
|
///
|
|
|
|
/// The backend provides no information about whether a cancellation attempt
|
|
|
|
/// was successful or not. An error will only be returned if the driver was
|
|
|
|
/// unable to connect to the database.
|
|
|
|
///
|
|
|
|
/// A `PostgresCancelData` object can be created via
|
|
|
|
/// `PostgresConnection::cancel_data`. The object can cancel any query made on
|
|
|
|
/// that connection.
|
2014-03-09 22:22:20 +00:00
|
|
|
///
|
2014-03-31 02:21:51 +00:00
|
|
|
/// Only the host and port of the URL are used.
|
|
|
|
///
|
2014-03-09 22:22:20 +00:00
|
|
|
/// # Example
|
|
|
|
///
|
2014-03-10 17:06:40 +00:00
|
|
|
/// ```rust,no_run
|
2014-03-09 22:22:20 +00:00
|
|
|
/// # use postgres::{PostgresConnection, NoSsl};
|
|
|
|
/// # let url = "";
|
2014-03-28 04:08:22 +00:00
|
|
|
/// let conn = PostgresConnection::connect(url, &NoSsl).unwrap();
|
2014-03-09 22:22:20 +00:00
|
|
|
/// let cancel_data = conn.cancel_data();
|
|
|
|
/// spawn(proc() {
|
2014-03-28 04:08:22 +00:00
|
|
|
/// conn.execute("SOME EXPENSIVE QUERY", []).unwrap();
|
2014-03-09 22:22:20 +00:00
|
|
|
/// });
|
|
|
|
/// # let _ =
|
|
|
|
/// postgres::cancel_query(url, &NoSsl, cancel_data);
|
|
|
|
/// ```
|
2013-11-11 02:13:32 +00:00
|
|
|
pub fn cancel_query(url: &str, ssl: &SslMode, data: PostgresCancelData)
|
2013-12-06 07:20:50 +00:00
|
|
|
-> Result<(), PostgresConnectError> {
|
2013-11-29 18:30:03 +00:00
|
|
|
let Url { host, port, .. }: Url = match FromStr::from_str(url) {
|
2013-10-21 00:32:14 +00:00
|
|
|
Some(url) => url,
|
2013-12-06 07:20:50 +00:00
|
|
|
None => return Err(InvalidUrl)
|
2013-10-21 00:32:14 +00:00
|
|
|
};
|
|
|
|
let port = match port {
|
|
|
|
Some(port) => FromStr::from_str(port).unwrap(),
|
|
|
|
None => DEFAULT_PORT
|
|
|
|
};
|
|
|
|
|
2013-11-10 04:58:38 +00:00
|
|
|
let mut socket = match initialize_stream(host, port, ssl) {
|
2013-10-21 00:32:14 +00:00
|
|
|
Ok(socket) => socket,
|
2013-12-06 07:20:50 +00:00
|
|
|
Err(err) => return Err(err)
|
2013-10-21 00:32:14 +00:00
|
|
|
};
|
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
try_pg_conn!(socket.write_message(&CancelRequest {
|
2013-10-21 00:32:14 +00:00
|
|
|
code: message::CANCEL_CODE,
|
|
|
|
process_id: data.process_id,
|
|
|
|
secret_key: data.secret_key
|
2014-02-07 06:59:33 +00:00
|
|
|
}));
|
2014-03-24 00:34:50 +00:00
|
|
|
try_pg_conn!(socket.flush());
|
2013-10-21 00:32:14 +00:00
|
|
|
|
2013-12-06 07:20:50 +00:00
|
|
|
Ok(())
|
2013-10-21 00:32:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn open_socket(host: &str, port: Port)
|
|
|
|
-> Result<TcpStream, PostgresConnectError> {
|
2014-02-07 04:58:47 +00:00
|
|
|
let addrs = match net::get_host_addresses(host) {
|
|
|
|
Ok(addrs) => addrs,
|
2014-03-31 02:21:51 +00:00
|
|
|
Err(err) => return Err(DnsError(err))
|
2013-10-21 00:32:14 +00:00
|
|
|
};
|
|
|
|
|
2014-03-31 02:21:51 +00:00
|
|
|
let mut err = None;
|
2014-02-07 04:58:47 +00:00
|
|
|
for &addr in addrs.iter() {
|
|
|
|
match TcpStream::connect(SocketAddr { ip: addr, port: port }) {
|
|
|
|
Ok(socket) => return Ok(socket),
|
2014-03-31 02:21:51 +00:00
|
|
|
Err(e) => err = Some(e)
|
2013-10-21 00:32:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-31 02:21:51 +00:00
|
|
|
Err(SocketError(err.unwrap()))
|
2013-10-21 00:32:14 +00:00
|
|
|
}
|
|
|
|
|
2014-04-18 21:29:51 +00:00
|
|
|
fn open_unix(path: &Path) -> Result<UnixStream, PostgresConnectError> {
|
|
|
|
match UnixStream::connect(path) {
|
|
|
|
Ok(unix) => Ok(unix),
|
|
|
|
Err(err) => Err(SocketError(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-11 02:13:32 +00:00
|
|
|
fn initialize_stream(host: &str, port: Port, ssl: &SslMode)
|
2013-11-10 04:58:38 +00:00
|
|
|
-> Result<InternalStream, PostgresConnectError> {
|
|
|
|
let mut socket = match open_socket(host, port) {
|
|
|
|
Ok(socket) => socket,
|
|
|
|
Err(err) => return Err(err)
|
|
|
|
};
|
|
|
|
|
2014-04-03 05:56:16 +00:00
|
|
|
let (ssl_required, ctx) = match *ssl {
|
2014-04-10 04:28:20 +00:00
|
|
|
NoSsl => return Ok(NormalStream(socket)),
|
2014-04-03 05:56:16 +00:00
|
|
|
PreferSsl(ref ctx) => (false, ctx),
|
|
|
|
RequireSsl(ref ctx) => (true, ctx)
|
2013-11-10 04:58:38 +00:00
|
|
|
};
|
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
try_pg_conn!(socket.write_message(&SslRequest { code: message::SSL_CODE }));
|
|
|
|
try_pg_conn!(socket.flush());
|
2013-11-10 04:58:38 +00:00
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
if try_pg_conn!(socket.read_u8()) == 'N' as u8 {
|
2013-11-10 06:30:20 +00:00
|
|
|
if ssl_required {
|
|
|
|
return Err(NoSslSupport);
|
|
|
|
} else {
|
2014-04-10 04:28:20 +00:00
|
|
|
return Ok(NormalStream(socket));
|
2013-11-10 06:30:20 +00:00
|
|
|
}
|
2013-11-10 04:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
match SslStream::try_new(ctx, socket) {
|
2014-04-10 04:28:20 +00:00
|
|
|
Ok(stream) => Ok(SslStream(stream)),
|
2013-11-10 04:58:38 +00:00
|
|
|
Err(err) => Err(SslError(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-18 21:29:51 +00:00
|
|
|
fn initialize_unix(path: &Path)
|
|
|
|
-> Result<InternalStream, PostgresConnectError> {
|
|
|
|
match open_unix(path) {
|
|
|
|
Ok(unix) => Ok(NormalUnix(unix)),
|
|
|
|
Err(err) => Err(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-10 03:01:06 +00:00
|
|
|
enum InternalStream {
|
2014-04-10 04:28:20 +00:00
|
|
|
NormalStream(TcpStream),
|
2014-04-18 21:29:51 +00:00
|
|
|
SslStream(SslStream<TcpStream>),
|
|
|
|
NormalUnix(UnixStream)
|
2013-11-10 03:01:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Reader for InternalStream {
|
2014-02-07 04:58:47 +00:00
|
|
|
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
2013-11-10 03:01:06 +00:00
|
|
|
match *self {
|
2014-04-10 04:28:20 +00:00
|
|
|
NormalStream(ref mut s) => s.read(buf),
|
2014-04-18 21:29:51 +00:00
|
|
|
SslStream(ref mut s) => s.read(buf),
|
|
|
|
NormalUnix(ref mut s) => s.read(buf)
|
2013-11-10 03:01:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Writer for InternalStream {
|
2014-02-07 04:58:47 +00:00
|
|
|
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
2013-11-10 03:01:06 +00:00
|
|
|
match *self {
|
2014-04-10 04:28:20 +00:00
|
|
|
NormalStream(ref mut s) => s.write(buf),
|
2014-04-18 21:29:51 +00:00
|
|
|
SslStream(ref mut s) => s.write(buf),
|
|
|
|
NormalUnix(ref mut s) => s.write(buf)
|
2013-11-10 03:01:06 +00:00
|
|
|
}
|
|
|
|
}
|
2013-12-05 06:52:45 +00:00
|
|
|
|
2014-02-07 04:58:47 +00:00
|
|
|
fn flush(&mut self) -> IoResult<()> {
|
2013-12-05 06:52:45 +00:00
|
|
|
match *self {
|
2014-04-10 04:28:20 +00:00
|
|
|
NormalStream(ref mut s) => s.flush(),
|
2014-04-18 21:29:51 +00:00
|
|
|
SslStream(ref mut s) => s.flush(),
|
|
|
|
NormalUnix(ref mut s) => s.flush()
|
2013-12-05 06:52:45 +00:00
|
|
|
}
|
|
|
|
}
|
2013-11-10 03:01:06 +00:00
|
|
|
}
|
|
|
|
|
2013-09-20 04:06:45 +00:00
|
|
|
struct InnerPostgresConnection {
|
2013-11-10 03:01:06 +00:00
|
|
|
stream: BufferedStream<InternalStream>,
|
2013-12-06 07:20:50 +00:00
|
|
|
next_stmt_id: uint,
|
2014-03-29 21:33:11 +00:00
|
|
|
notice_handler: ~PostgresNoticeHandler:Send,
|
2013-10-15 05:41:03 +00:00
|
|
|
notifications: RingBuf<PostgresNotification>,
|
2013-12-04 08:18:28 +00:00
|
|
|
cancel_data: PostgresCancelData,
|
|
|
|
unknown_types: HashMap<Oid, ~str>,
|
2014-02-10 06:45:26 +00:00
|
|
|
desynchronized: bool,
|
2014-02-10 07:55:16 +00:00
|
|
|
finished: bool,
|
2014-04-08 03:52:41 +00:00
|
|
|
canary: u32,
|
2013-08-29 06:19:53 +00:00
|
|
|
}
|
|
|
|
|
2013-09-20 04:06:45 +00:00
|
|
|
impl Drop for InnerPostgresConnection {
|
2013-09-18 05:06:47 +00:00
|
|
|
fn drop(&mut self) {
|
2014-02-10 07:55:16 +00:00
|
|
|
if !self.finished {
|
2014-04-07 06:37:17 +00:00
|
|
|
let _ = self.finish_inner();
|
2014-02-10 07:55:16 +00:00
|
|
|
}
|
2013-08-29 06:19:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-20 04:06:45 +00:00
|
|
|
impl InnerPostgresConnection {
|
2014-04-03 04:26:41 +00:00
|
|
|
fn connect(url: &str, ssl: &SslMode) -> Result<InnerPostgresConnection,
|
|
|
|
PostgresConnectError> {
|
2013-08-27 04:19:24 +00:00
|
|
|
let Url {
|
|
|
|
host,
|
|
|
|
port,
|
|
|
|
user,
|
2014-04-12 02:45:06 +00:00
|
|
|
path,
|
2013-12-04 06:32:54 +00:00
|
|
|
query: mut args,
|
2013-11-29 18:30:03 +00:00
|
|
|
..
|
2013-08-27 04:19:24 +00:00
|
|
|
}: Url = match FromStr::from_str(url) {
|
|
|
|
Some(url) => url,
|
|
|
|
None => return Err(InvalidUrl)
|
|
|
|
};
|
2013-12-04 06:32:54 +00:00
|
|
|
|
2013-08-27 04:19:24 +00:00
|
|
|
let user = match user {
|
|
|
|
Some(user) => user,
|
|
|
|
None => return Err(MissingUser)
|
|
|
|
};
|
|
|
|
|
2013-09-08 21:26:34 +00:00
|
|
|
let port = match port {
|
|
|
|
Some(port) => FromStr::from_str(port).unwrap(),
|
2013-10-21 00:32:14 +00:00
|
|
|
None => DEFAULT_PORT
|
2013-09-08 21:26:34 +00:00
|
|
|
};
|
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
let stream = try!(initialize_stream(host, port, ssl));
|
2013-08-22 05:52:15 +00:00
|
|
|
|
2014-04-18 21:29:51 +00:00
|
|
|
let conn = InnerPostgresConnection {
|
2013-11-10 04:58:38 +00:00
|
|
|
stream: BufferedStream::new(stream),
|
2013-09-20 04:06:45 +00:00
|
|
|
next_stmt_id: 0,
|
2014-03-29 21:33:11 +00:00
|
|
|
notice_handler: ~DefaultNoticeHandler,
|
2013-10-15 05:41:03 +00:00
|
|
|
notifications: RingBuf::new(),
|
2013-12-04 08:18:28 +00:00
|
|
|
cancel_data: PostgresCancelData { process_id: 0, secret_key: 0 },
|
|
|
|
unknown_types: HashMap::new(),
|
2014-02-10 06:45:26 +00:00
|
|
|
desynchronized: false,
|
2014-02-10 07:55:16 +00:00
|
|
|
finished: false,
|
2014-04-08 03:52:41 +00:00
|
|
|
canary: CANARY,
|
2013-08-18 03:42:40 +00:00
|
|
|
};
|
|
|
|
|
2013-09-06 03:49:19 +00:00
|
|
|
args.push((~"client_encoding", ~"UTF8"));
|
2013-09-09 04:33:41 +00:00
|
|
|
// Postgres uses the value of TimeZone as the time zone for TIMESTAMP
|
|
|
|
// WITH TIME ZONE values. Timespec converts to GMT internally.
|
|
|
|
args.push((~"TimeZone", ~"GMT"));
|
2013-08-27 05:06:53 +00:00
|
|
|
// We have to clone here since we need the user again for auth
|
2013-08-27 04:19:24 +00:00
|
|
|
args.push((~"user", user.user.clone()));
|
|
|
|
if !path.is_empty() {
|
2013-10-05 03:26:52 +00:00
|
|
|
// path contains the leading /
|
2014-04-12 02:57:09 +00:00
|
|
|
let (_, path) = path.slice_shift_char();
|
|
|
|
args.push((~"database", path.to_owned()));
|
2013-08-04 02:17:32 +00:00
|
|
|
}
|
2014-04-18 21:29:51 +00:00
|
|
|
|
|
|
|
InnerPostgresConnection::connect_finish(conn, args, user)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn connect_unix(socket_dir: &Path, port: Port, user: UserInfo, database: ~str)
|
|
|
|
-> Result<InnerPostgresConnection, PostgresConnectError> {
|
|
|
|
let mut socket = socket_dir.clone();
|
|
|
|
socket.push(format!(".s.PGSQL.{}", port));
|
|
|
|
|
|
|
|
let stream = try!(initialize_unix(&socket));
|
|
|
|
|
|
|
|
let conn = InnerPostgresConnection {
|
|
|
|
stream: BufferedStream::new(stream),
|
|
|
|
next_stmt_id: 0,
|
|
|
|
notice_handler: ~DefaultNoticeHandler,
|
|
|
|
notifications: RingBuf::new(),
|
|
|
|
cancel_data: PostgresCancelData { process_id: 0, secret_key: 0 },
|
|
|
|
unknown_types: HashMap::new(),
|
|
|
|
desynchronized: false,
|
|
|
|
finished: false,
|
|
|
|
canary: CANARY,
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut args = Vec::new();
|
|
|
|
|
|
|
|
args.push((~"client_encoding", ~"UTF8"));
|
|
|
|
// Postgres uses the value of TimeZone as the time zone for TIMESTAMP
|
|
|
|
// WITH TIME ZONE values. Timespec converts to GMT internally.
|
|
|
|
args.push((~"TimeZone", ~"GMT"));
|
|
|
|
// We have to clone here since we need the user again for auth
|
|
|
|
args.push((~"user", user.user.clone()));
|
|
|
|
args.push((~"database", database));
|
|
|
|
|
|
|
|
InnerPostgresConnection::connect_finish(conn, args, user)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn connect_finish(mut conn: InnerPostgresConnection, args: Vec<(~str, ~str)>, user: UserInfo)
|
|
|
|
-> Result<InnerPostgresConnection, PostgresConnectError> {
|
2014-03-24 00:34:50 +00:00
|
|
|
try_pg_conn!(conn.write_messages([StartupMessage {
|
2013-09-09 05:40:08 +00:00
|
|
|
version: message::PROTOCOL_VERSION,
|
2013-08-29 03:25:15 +00:00
|
|
|
parameters: args.as_slice()
|
2014-02-07 04:58:47 +00:00
|
|
|
}]));
|
2013-08-26 05:08:37 +00:00
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
try!(conn.handle_auth(user));
|
2013-08-04 02:17:32 +00:00
|
|
|
|
2013-08-22 05:52:15 +00:00
|
|
|
loop {
|
2014-03-24 00:34:50 +00:00
|
|
|
match try_pg_conn!(conn.read_message()) {
|
2013-10-21 00:32:14 +00:00
|
|
|
BackendKeyData { process_id, secret_key } => {
|
|
|
|
conn.cancel_data.process_id = process_id;
|
|
|
|
conn.cancel_data.secret_key = secret_key;
|
|
|
|
}
|
2013-11-29 18:30:03 +00:00
|
|
|
ReadyForQuery { .. } => break,
|
2013-10-05 03:26:52 +00:00
|
|
|
ErrorResponse { fields } =>
|
2014-02-07 04:58:47 +00:00
|
|
|
return Err(PgConnectDbError(PostgresDbError::new(fields))),
|
2013-10-25 05:30:34 +00:00
|
|
|
_ => unreachable!()
|
2013-09-09 03:35:24 +00:00
|
|
|
}
|
2013-08-05 00:48:48 +00:00
|
|
|
}
|
2013-08-23 05:24:14 +00:00
|
|
|
|
2013-08-27 04:19:24 +00:00
|
|
|
Ok(conn)
|
2013-08-05 00:48:48 +00:00
|
|
|
}
|
|
|
|
|
2014-04-01 06:51:42 +00:00
|
|
|
fn write_messages(&mut self, messages: &[FrontendMessage])
|
|
|
|
-> IoResult<()> {
|
2014-02-10 06:45:26 +00:00
|
|
|
assert!(!self.desynchronized);
|
2013-11-10 04:58:38 +00:00
|
|
|
for message in messages.iter() {
|
2014-03-24 00:34:50 +00:00
|
|
|
try_desync!(self.stream.write_message(message));
|
2013-07-25 07:10:18 +00:00
|
|
|
}
|
2014-03-24 00:34:50 +00:00
|
|
|
Ok(try_desync!(self.stream.flush()))
|
2013-08-22 06:41:26 +00:00
|
|
|
}
|
2013-08-04 02:17:32 +00:00
|
|
|
|
2014-02-07 04:58:47 +00:00
|
|
|
fn read_message(&mut self) -> IoResult<BackendMessage> {
|
2014-02-10 06:45:26 +00:00
|
|
|
assert!(!self.desynchronized);
|
2013-09-09 03:35:24 +00:00
|
|
|
loop {
|
2014-03-24 00:34:50 +00:00
|
|
|
match try_desync!(self.stream.read_message()) {
|
2014-02-10 06:45:26 +00:00
|
|
|
NoticeResponse { fields } =>
|
2013-09-20 04:06:45 +00:00
|
|
|
self.notice_handler.handle(PostgresDbError::new(fields)),
|
2014-02-10 06:45:26 +00:00
|
|
|
NotificationResponse { pid, channel, payload } =>
|
2013-10-15 05:41:03 +00:00
|
|
|
self.notifications.push_back(PostgresNotification {
|
|
|
|
pid: pid,
|
|
|
|
channel: channel,
|
|
|
|
payload: payload
|
|
|
|
}),
|
2014-02-10 06:45:26 +00:00
|
|
|
ParameterStatus { parameter, value } =>
|
2014-03-19 04:00:06 +00:00
|
|
|
debug!("Parameter {} = {}", parameter, value),
|
2014-02-10 06:45:26 +00:00
|
|
|
val => return Ok(val)
|
2013-09-09 03:35:24 +00:00
|
|
|
}
|
2013-08-04 05:21:16 +00:00
|
|
|
}
|
2013-08-22 06:41:26 +00:00
|
|
|
}
|
|
|
|
|
2014-04-03 04:26:41 +00:00
|
|
|
fn handle_auth(&mut self, user: UserInfo)
|
|
|
|
-> Result<(), PostgresConnectError> {
|
2014-03-24 00:34:50 +00:00
|
|
|
match try_pg_conn!(self.read_message()) {
|
2014-02-07 04:58:47 +00:00
|
|
|
AuthenticationOk => return Ok(()),
|
2013-08-27 04:19:24 +00:00
|
|
|
AuthenticationCleartextPassword => {
|
|
|
|
let pass = match user.pass {
|
|
|
|
Some(pass) => pass,
|
2014-02-07 04:58:47 +00:00
|
|
|
None => return Err(MissingPassword)
|
2013-08-27 04:19:24 +00:00
|
|
|
};
|
2014-04-01 06:51:42 +00:00
|
|
|
try_pg_conn!(self.write_messages([PasswordMessage {
|
|
|
|
password: pass
|
|
|
|
}]));
|
2013-09-09 03:35:24 +00:00
|
|
|
}
|
2013-11-06 06:04:12 +00:00
|
|
|
AuthenticationMD5Password { salt } => {
|
|
|
|
let UserInfo { user, pass } = user;
|
|
|
|
let pass = match pass {
|
|
|
|
Some(pass) => pass,
|
2014-02-07 04:58:47 +00:00
|
|
|
None => return Err(MissingPassword)
|
2013-11-06 06:04:12 +00:00
|
|
|
};
|
|
|
|
let input = pass + user;
|
2014-01-03 01:13:52 +00:00
|
|
|
let hasher = Hasher::new(MD5);
|
|
|
|
hasher.update(input.as_bytes());
|
|
|
|
let output = hasher.final().to_hex();
|
|
|
|
let hasher = Hasher::new(MD5);
|
|
|
|
hasher.update(output.as_bytes());
|
|
|
|
hasher.update(salt);
|
|
|
|
let output = "md5" + hasher.final().to_hex();
|
2014-03-24 00:34:50 +00:00
|
|
|
try_pg_conn!(self.write_messages([PasswordMessage {
|
2014-04-01 06:51:42 +00:00
|
|
|
password: output.as_slice()
|
|
|
|
}]));
|
2013-11-06 06:04:12 +00:00
|
|
|
}
|
|
|
|
AuthenticationKerberosV5
|
2013-09-12 05:02:32 +00:00
|
|
|
| AuthenticationSCMCredential
|
|
|
|
| AuthenticationGSS
|
2014-02-07 04:58:47 +00:00
|
|
|
| AuthenticationSSPI => return Err(UnsupportedAuthentication),
|
2013-10-05 02:26:57 +00:00
|
|
|
ErrorResponse { fields } =>
|
2014-02-07 04:58:47 +00:00
|
|
|
return Err(PgConnectDbError(PostgresDbError::new(fields))),
|
2013-10-25 05:30:34 +00:00
|
|
|
_ => unreachable!()
|
2013-09-09 03:35:24 +00:00
|
|
|
}
|
2013-08-27 04:19:24 +00:00
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
match try_pg_conn!(self.read_message()) {
|
2014-02-07 04:58:47 +00:00
|
|
|
AuthenticationOk => Ok(()),
|
2013-08-29 04:24:43 +00:00
|
|
|
ErrorResponse { fields } =>
|
2014-02-07 04:58:47 +00:00
|
|
|
Err(PgConnectDbError(PostgresDbError::new(fields))),
|
2013-10-25 05:30:34 +00:00
|
|
|
_ => unreachable!()
|
2013-09-09 03:35:24 +00:00
|
|
|
}
|
2013-08-26 05:08:37 +00:00
|
|
|
}
|
|
|
|
|
2014-03-29 21:33:11 +00:00
|
|
|
fn set_notice_handler(&mut self, handler: ~PostgresNoticeHandler:Send)
|
|
|
|
-> ~PostgresNoticeHandler:Send {
|
2014-02-11 03:20:53 +00:00
|
|
|
mem::replace(&mut self.notice_handler, handler)
|
2013-08-27 05:06:53 +00:00
|
|
|
}
|
|
|
|
|
2014-03-28 04:08:22 +00:00
|
|
|
fn prepare<'a>(&mut self, query: &str, conn: &'a PostgresConnection)
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<PostgresStatement<'a>> {
|
2014-04-10 04:28:20 +00:00
|
|
|
let stmt_name = format!("s{}", self.next_stmt_id);
|
2013-09-20 04:06:45 +00:00
|
|
|
self.next_stmt_id += 1;
|
2013-08-05 00:48:48 +00:00
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
try_pg!(self.write_messages([
|
2013-11-10 04:58:38 +00:00
|
|
|
Parse {
|
2013-09-02 17:27:09 +00:00
|
|
|
name: stmt_name,
|
|
|
|
query: query,
|
2014-03-30 23:19:04 +00:00
|
|
|
param_types: []
|
2013-09-02 17:27:09 +00:00
|
|
|
},
|
2013-11-10 04:58:38 +00:00
|
|
|
Describe {
|
2013-09-02 17:27:09 +00:00
|
|
|
variant: 'S' as u8,
|
|
|
|
name: stmt_name
|
|
|
|
},
|
2014-02-07 04:58:47 +00:00
|
|
|
Sync]));
|
2013-08-22 06:41:26 +00:00
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
match try_pg!(self.read_message()) {
|
2013-10-25 05:30:34 +00:00
|
|
|
ParseComplete => {}
|
2013-08-29 05:33:27 +00:00
|
|
|
ErrorResponse { fields } => {
|
2014-02-22 07:18:39 +00:00
|
|
|
try!(self.wait_for_ready());
|
2014-02-07 04:58:47 +00:00
|
|
|
return Err(PgDbError(PostgresDbError::new(fields)));
|
2013-09-04 03:07:10 +00:00
|
|
|
}
|
2013-10-25 05:30:34 +00:00
|
|
|
_ => unreachable!()
|
2013-09-09 03:35:24 +00:00
|
|
|
}
|
2013-08-22 06:41:26 +00:00
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
let mut param_types: Vec<PostgresType> = match try_pg!(self.read_message()) {
|
2013-09-05 04:26:43 +00:00
|
|
|
ParameterDescription { types } =>
|
2013-12-07 23:39:44 +00:00
|
|
|
types.iter().map(|ty| PostgresType::from_oid(*ty)).collect(),
|
2013-10-25 05:30:34 +00:00
|
|
|
_ => unreachable!()
|
2013-09-09 03:35:24 +00:00
|
|
|
};
|
2013-08-22 06:41:26 +00:00
|
|
|
|
2014-03-24 00:34:50 +00:00
|
|
|
let mut result_desc: Vec<ResultDescription> = match try_pg!(self.read_message()) {
|
2013-12-03 08:00:28 +00:00
|
|
|
RowDescription { descriptions } =>
|
2013-12-03 07:04:44 +00:00
|
|
|
descriptions.move_iter().map(|desc| {
|
2014-04-03 05:56:16 +00:00
|
|
|
let RowDescriptionEntry { name, type_oid, .. } = desc;
|
|
|
|
ResultDescription {
|
|
|
|
name: name,
|
|
|
|
ty: PostgresType::from_oid(type_oid)
|
|
|
|
}
|
|
|
|
}).collect(),
|
2014-03-15 05:33:24 +00:00
|
|
|
NoData => Vec::new(),
|
2013-10-25 05:30:34 +00:00
|
|
|
_ => unreachable!()
|
2013-09-09 03:35:24 +00:00
|
|
|
};
|
2013-08-17 22:09:26 +00:00
|
|
|
|
2014-02-22 07:18:39 +00:00
|
|
|
try!(self.wait_for_ready());
|
2013-08-22 06:41:26 +00:00
|
|
|
|
2013-12-04 08:18:28 +00:00
|
|
|
// now that the connection is ready again, get unknown type names
|
2014-03-30 23:19:04 +00:00
|
|
|
try!(self.set_type_names(param_types.mut_iter()));
|
|
|
|
try!(self.set_type_names(result_desc.mut_iter().map(|d| &mut d.ty)));
|
2013-12-04 08:18:28 +00:00
|
|
|
|
2014-03-26 04:01:38 +00:00
|
|
|
Ok(PostgresStatement {
|
|
|
|
conn: conn,
|
|
|
|
name: stmt_name,
|
|
|
|
param_types: param_types,
|
|
|
|
result_desc: result_desc,
|
|
|
|
next_portal_id: Cell::new(0),
|
|
|
|
finished: Cell::new(false),
|
|
|
|
})
|
2013-08-22 06:41:26 +00:00
|
|
|
}
|
|
|
|
|
2014-03-30 23:19:04 +00:00
|
|
|
fn set_type_names<'a, I: Iterator<&'a mut PostgresType>>(&mut self, mut it: I)
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<()> {
|
2014-03-30 23:19:04 +00:00
|
|
|
for ty in it {
|
|
|
|
match *ty {
|
|
|
|
PgUnknownType { oid, ref mut name } =>
|
|
|
|
*name = try!(self.get_type_name(oid)),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
2014-02-10 07:55:16 +00:00
|
|
|
}
|
|
|
|
|
2014-04-03 04:26:41 +00:00
|
|
|
fn get_type_name(&mut self, oid: Oid) -> PostgresResult<~str> {
|
2013-12-04 08:18:28 +00:00
|
|
|
match self.unknown_types.find(&oid) {
|
2014-02-07 04:58:47 +00:00
|
|
|
Some(name) => return Ok(name.clone()),
|
2013-12-04 08:18:28 +00:00
|
|
|
None => {}
|
|
|
|
}
|
2014-04-03 05:56:16 +00:00
|
|
|
let name = try!(self.quick_query(format!("SELECT typname FROM pg_type \
|
|
|
|
WHERE oid={}", oid)))
|
2014-03-15 05:33:24 +00:00
|
|
|
.move_iter().next().unwrap().move_iter().next().unwrap().unwrap();
|
2013-12-04 08:18:28 +00:00
|
|
|
self.unknown_types.insert(oid, name.clone());
|
2014-02-07 04:58:47 +00:00
|
|
|
Ok(name)
|
2013-12-04 08:18:28 +00:00
|
|
|
}
|
|
|
|
|
2014-03-30 23:19:04 +00:00
|
|
|
fn is_desynchronized(&self) -> bool {
|
|
|
|
self.desynchronized
|
|
|
|
}
|
|
|
|
|
2014-04-08 03:52:41 +00:00
|
|
|
fn canary(&self) -> u32 {
|
|
|
|
self.canary
|
|
|
|
}
|
|
|
|
|
2014-04-03 04:26:41 +00:00
|
|
|
fn wait_for_ready(&mut self) -> PostgresResult<()> {
|
2014-03-24 00:34:50 +00:00
|
|
|
match try_pg!(self.read_message()) {
|
2014-02-07 04:58:47 +00:00
|
|
|
ReadyForQuery { .. } => Ok(()),
|
2013-10-25 05:30:34 +00:00
|
|
|
_ => unreachable!()
|
2013-09-20 04:06:45 +00:00
|
|
|
}
|
|
|
|
}
|
2013-12-04 07:48:46 +00:00
|
|
|
|
2014-02-07 04:58:47 +00:00
|
|
|
fn quick_query(&mut self, query: &str)
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<Vec<Vec<Option<~str>>>> {
|
2014-02-10 07:55:16 +00:00
|
|
|
check_desync!(self);
|
2014-03-24 00:34:50 +00:00
|
|
|
try_pg!(self.write_messages([Query { query: query }]));
|
2013-12-04 07:48:46 +00:00
|
|
|
|
2014-03-15 05:33:24 +00:00
|
|
|
let mut result = Vec::new();
|
2013-12-04 07:48:46 +00:00
|
|
|
loop {
|
2014-03-24 00:34:50 +00:00
|
|
|
match try_pg!(self.read_message()) {
|
2013-12-04 07:48:46 +00:00
|
|
|
ReadyForQuery { .. } => break,
|
|
|
|
DataRow { row } =>
|
2014-04-08 03:02:05 +00:00
|
|
|
// FIXME
|
2013-12-04 07:48:46 +00:00
|
|
|
result.push(row.move_iter().map(|opt|
|
2014-04-08 03:02:05 +00:00
|
|
|
opt.map(|b| str::from_utf8(b.as_slice()).unwrap().to_owned()))
|
2014-01-23 03:07:44 +00:00
|
|
|
.collect()),
|
2014-02-12 07:09:27 +00:00
|
|
|
ErrorResponse { fields } => {
|
2014-02-22 07:18:39 +00:00
|
|
|
try!(self.wait_for_ready());
|
2014-02-12 07:09:27 +00:00
|
|
|
return Err(PgDbError(PostgresDbError::new(fields)));
|
|
|
|
}
|
2013-12-04 07:48:46 +00:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
2014-02-07 04:58:47 +00:00
|
|
|
Ok(result)
|
2013-12-04 07:48:46 +00:00
|
|
|
}
|
2014-02-10 07:55:16 +00:00
|
|
|
|
2014-04-03 04:26:41 +00:00
|
|
|
fn finish_inner(&mut self) -> PostgresResult<()> {
|
2014-02-10 07:55:16 +00:00
|
|
|
check_desync!(self);
|
2014-04-08 03:52:41 +00:00
|
|
|
self.canary = 0;
|
2014-03-24 00:34:50 +00:00
|
|
|
Ok(try_pg!(self.write_messages([Terminate])))
|
2014-02-10 07:55:16 +00:00
|
|
|
}
|
2013-09-20 04:06:45 +00:00
|
|
|
}
|
|
|
|
|
2013-09-30 02:12:20 +00:00
|
|
|
/// A connection to a Postgres database.
|
2013-10-01 07:40:46 +00:00
|
|
|
pub struct PostgresConnection {
|
2014-04-01 06:51:42 +00:00
|
|
|
conn: RefCell<InnerPostgresConnection>
|
2013-10-01 07:40:46 +00:00
|
|
|
}
|
2013-09-20 04:06:45 +00:00
|
|
|
|
|
|
|
impl PostgresConnection {
|
2014-03-28 04:08:22 +00:00
|
|
|
/// Creates a new connection to a Postgres database.
|
2013-09-30 02:12:20 +00:00
|
|
|
///
|
|
|
|
/// The URL should be provided in the normal format:
|
|
|
|
///
|
2014-03-09 06:01:24 +00:00
|
|
|
/// ```notrust
|
2013-09-30 02:12:20 +00:00
|
|
|
/// postgres://user[:password]@host[:port][/database][?param1=val1[[¶m2=val2]...]]
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// The password may be omitted if not required. The default Postgres port
|
|
|
|
/// (5432) is used if none is specified. The database name defaults to the
|
|
|
|
/// username if not specified.
|
2014-03-09 22:22:20 +00:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
2014-03-10 17:06:40 +00:00
|
|
|
/// ```rust,no_run
|
2014-03-09 22:22:20 +00:00
|
|
|
/// # use postgres::{PostgresConnection, NoSsl};
|
|
|
|
/// let url = "postgres://postgres:hunter2@localhost:2994/foodb";
|
2014-03-28 04:08:22 +00:00
|
|
|
/// let maybe_conn = PostgresConnection::connect(url, &NoSsl);
|
2014-03-09 22:22:20 +00:00
|
|
|
/// let conn = match maybe_conn {
|
|
|
|
/// Ok(conn) => conn,
|
|
|
|
/// Err(err) => fail!("Error connecting: {}", err)
|
|
|
|
/// };
|
|
|
|
/// ```
|
2014-04-03 05:56:16 +00:00
|
|
|
pub fn connect(url: &str, ssl: &SslMode) -> Result<PostgresConnection,
|
|
|
|
PostgresConnectError> {
|
2014-03-28 04:08:22 +00:00
|
|
|
InnerPostgresConnection::connect(url, ssl).map(|conn| {
|
2013-10-01 07:40:46 +00:00
|
|
|
PostgresConnection {
|
2013-11-24 21:12:46 +00:00
|
|
|
conn: RefCell::new(conn)
|
2013-10-01 07:40:46 +00:00
|
|
|
}
|
2013-11-27 02:27:34 +00:00
|
|
|
})
|
2013-09-20 04:06:45 +00:00
|
|
|
}
|
|
|
|
|
2014-04-18 21:29:51 +00:00
|
|
|
/// Creates a new connection to a Postgres database.
|
|
|
|
///
|
|
|
|
/// The path should be the directory containing the socket.
|
|
|
|
///
|
|
|
|
/// The password in the UserInfo may be omitted if not required.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
|
|
|
/// # use postgres::PostgresConnection;
|
|
|
|
/// let path = Path::new("/tmp");
|
|
|
|
/// let port = 5432;
|
|
|
|
/// let user = UserInfo::new(~"username", None);
|
|
|
|
/// let database = ~"postgres";
|
|
|
|
/// let maybe_conn = PostgresConnection::connect_unix(&path, 5432, user, database);
|
|
|
|
/// let conn = match maybe_conn {
|
|
|
|
/// Ok(conn) => conn,
|
|
|
|
/// Err(err) => fail!("Error connecting: {}", err)
|
|
|
|
/// };
|
|
|
|
/// ```
|
|
|
|
pub fn connect_unix(path: &Path, port: Port, user: UserInfo, database: ~str)
|
|
|
|
-> Result<PostgresConnection, PostgresConnectError> {
|
|
|
|
InnerPostgresConnection::connect_unix(path, port, user, database).map(|conn| {
|
|
|
|
PostgresConnection {
|
|
|
|
conn: RefCell::new(conn)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2013-09-30 02:12:20 +00:00
|
|
|
/// Sets the notice handler for the connection, returning the old handler.
|
2014-03-29 21:33:11 +00:00
|
|
|
pub fn set_notice_handler(&self, handler: ~PostgresNoticeHandler:Send)
|
|
|
|
-> ~PostgresNoticeHandler:Send {
|
2014-03-19 04:00:06 +00:00
|
|
|
self.conn.borrow_mut().set_notice_handler(handler)
|
2013-09-20 04:06:45 +00:00
|
|
|
}
|
|
|
|
|
2013-10-15 05:41:03 +00:00
|
|
|
/// Returns an iterator over asynchronous notification messages.
|
|
|
|
///
|
|
|
|
/// Use the `LISTEN` command to register this connection for notifications.
|
2014-02-03 05:09:48 +00:00
|
|
|
pub fn notifications<'a>(&'a self) -> PostgresNotifications<'a> {
|
|
|
|
PostgresNotifications {
|
2013-10-15 05:41:03 +00:00
|
|
|
conn: self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-28 04:08:22 +00:00
|
|
|
/// Creates a new prepared statement.
|
2013-09-30 02:12:20 +00:00
|
|
|
///
|
|
|
|
/// A statement may contain parameters, specified by `$n` where `n` is the
|
|
|
|
/// index of the parameter in the list provided at execution time,
|
|
|
|
/// 1-indexed.
|
|
|
|
///
|
|
|
|
/// The statement is associated with the connection that created it and may
|
|
|
|
/// not outlive that connection.
|
2014-03-09 22:22:20 +00:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
2014-03-10 17:06:40 +00:00
|
|
|
/// ```rust,no_run
|
2014-03-09 22:22:20 +00:00
|
|
|
/// # use postgres::{PostgresConnection, NoSsl};
|
2014-03-28 04:08:22 +00:00
|
|
|
/// # let conn = PostgresConnection::connect("", &NoSsl).unwrap();
|
|
|
|
/// let maybe_stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1");
|
2014-03-09 22:22:20 +00:00
|
|
|
/// let stmt = match maybe_stmt {
|
|
|
|
/// Ok(stmt) => stmt,
|
|
|
|
/// Err(err) => fail!("Error preparing statement: {}", err)
|
|
|
|
/// };
|
2014-03-28 04:08:22 +00:00
|
|
|
pub fn prepare<'a>(&'a self, query: &str)
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<PostgresStatement<'a>> {
|
2014-03-28 04:08:22 +00:00
|
|
|
self.conn.borrow_mut().prepare(query, self)
|
2013-09-20 04:06:45 +00:00
|
|
|
}
|
|
|
|
|
2014-03-28 04:08:22 +00:00
|
|
|
/// Begins a new transaction.
|
2013-09-30 02:12:20 +00:00
|
|
|
///
|
2013-10-14 01:58:31 +00:00
|
|
|
/// Returns a `PostgresTransaction` object which should be used instead of
|
|
|
|
/// the connection for the duration of the transaction. The transaction
|
2013-10-15 02:36:01 +00:00
|
|
|
/// is active until the `PostgresTransaction` object falls out of scope.
|
2013-10-14 01:58:31 +00:00
|
|
|
/// A transaction will commit by default unless the task fails or the
|
|
|
|
/// transaction is set to roll back.
|
2014-03-09 22:22:20 +00:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
2014-03-10 17:06:40 +00:00
|
|
|
/// ```rust,no_run
|
2014-03-09 22:22:20 +00:00
|
|
|
/// # use postgres::{PostgresConnection, NoSsl};
|
|
|
|
/// # fn foo() -> Result<(), postgres::error::PostgresError> {
|
2014-03-28 04:08:22 +00:00
|
|
|
/// # let conn = PostgresConnection::connect("", &NoSsl).unwrap();
|
|
|
|
/// let trans = try!(conn.transaction());
|
|
|
|
/// try!(trans.execute("UPDATE foo SET bar = 10", []));
|
2014-03-09 22:22:20 +00:00
|
|
|
///
|
|
|
|
/// # let something_bad_happened = true;
|
|
|
|
/// if something_bad_happened {
|
|
|
|
/// trans.set_rollback();
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// drop(trans);
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2014-03-28 04:08:22 +00:00
|
|
|
pub fn transaction<'a>(&'a self)
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<PostgresTransaction<'a>> {
|
2014-02-10 06:45:26 +00:00
|
|
|
check_desync!(self);
|
2014-02-22 07:18:39 +00:00
|
|
|
try!(self.quick_query("BEGIN"));
|
2014-02-10 06:45:26 +00:00
|
|
|
Ok(PostgresTransaction {
|
2013-08-27 05:40:23 +00:00
|
|
|
conn: self,
|
2014-02-10 06:45:26 +00:00
|
|
|
commit: Cell::new(true),
|
2014-02-10 07:55:16 +00:00
|
|
|
nested: false,
|
|
|
|
finished: false,
|
2014-02-10 06:45:26 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2013-12-29 03:24:52 +00:00
|
|
|
/// A convenience function for queries that are only run once.
|
2013-09-30 02:12:20 +00:00
|
|
|
///
|
|
|
|
/// If an error is returned, it could have come from either the preparation
|
|
|
|
/// or execution of the statement.
|
|
|
|
///
|
|
|
|
/// On success, returns the number of rows modified or 0 if not applicable.
|
2014-03-28 04:08:22 +00:00
|
|
|
pub fn execute(&self, query: &str, params: &[&ToSql])
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<uint> {
|
2014-03-28 04:08:22 +00:00
|
|
|
self.prepare(query).and_then(|stmt| stmt.execute(params))
|
2013-09-01 18:06:33 +00:00
|
|
|
}
|
|
|
|
|
2013-10-21 00:32:14 +00:00
|
|
|
/// Returns information used to cancel pending queries.
|
|
|
|
///
|
|
|
|
/// Used with the `cancel_query` function. The object returned can be used
|
|
|
|
/// to cancel any query executed by the connection it was created from.
|
|
|
|
pub fn cancel_data(&self) -> PostgresCancelData {
|
2014-03-19 04:00:06 +00:00
|
|
|
self.conn.borrow().cancel_data
|
2013-10-21 00:32:14 +00:00
|
|
|
}
|
|
|
|
|
2014-02-10 06:45:26 +00:00
|
|
|
/// Returns whether or not the stream has been desynchronized due to an
|
|
|
|
/// error in the communication channel with the server.
|
|
|
|
///
|
|
|
|
/// If this has occurred, all further queries will immediately return an
|
|
|
|
/// error.
|
|
|
|
pub fn is_desynchronized(&self) -> bool {
|
2014-03-19 04:00:06 +00:00
|
|
|
self.conn.borrow().is_desynchronized()
|
2014-02-10 06:45:26 +00:00
|
|
|
}
|
|
|
|
|
2014-02-10 07:55:16 +00:00
|
|
|
/// Consumes the connection, closing it.
|
|
|
|
///
|
|
|
|
/// Functionally equivalent to the `Drop` implementation for
|
|
|
|
/// `PostgresConnection` except that it returns any error encountered to
|
|
|
|
/// the caller.
|
2014-04-03 04:26:41 +00:00
|
|
|
pub fn finish(self) -> PostgresResult<()> {
|
2014-03-19 04:00:06 +00:00
|
|
|
let mut conn = self.conn.borrow_mut();
|
|
|
|
conn.finished = true;
|
|
|
|
conn.finish_inner()
|
2014-02-10 07:55:16 +00:00
|
|
|
}
|
|
|
|
|
2014-04-08 03:52:41 +00:00
|
|
|
fn canary(&self) -> u32 {
|
2014-04-10 04:28:20 +00:00
|
|
|
self.conn.borrow().canary()
|
2014-04-08 03:52:41 +00:00
|
|
|
}
|
|
|
|
|
2014-02-10 07:55:16 +00:00
|
|
|
fn quick_query(&self, query: &str)
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<Vec<Vec<Option<~str>>>> {
|
2014-03-19 04:00:06 +00:00
|
|
|
self.conn.borrow_mut().quick_query(query)
|
2013-08-23 07:13:42 +00:00
|
|
|
}
|
|
|
|
|
2014-04-03 04:26:41 +00:00
|
|
|
fn wait_for_ready(&self) -> PostgresResult<()> {
|
2014-03-19 04:00:06 +00:00
|
|
|
self.conn.borrow_mut().wait_for_ready()
|
2013-09-20 04:06:45 +00:00
|
|
|
}
|
|
|
|
|
2014-02-07 04:58:47 +00:00
|
|
|
fn read_message(&self) -> IoResult<BackendMessage> {
|
2014-03-19 04:00:06 +00:00
|
|
|
self.conn.borrow_mut().read_message()
|
2013-09-20 04:06:45 +00:00
|
|
|
}
|
|
|
|
|
2014-02-07 04:58:47 +00:00
|
|
|
fn write_messages(&self, messages: &[FrontendMessage]) -> IoResult<()> {
|
2014-03-19 04:00:06 +00:00
|
|
|
self.conn.borrow_mut().write_messages(messages)
|
2013-08-17 22:09:26 +00:00
|
|
|
}
|
|
|
|
}
|
2013-08-18 02:09:56 +00:00
|
|
|
|
2013-11-10 04:58:38 +00:00
|
|
|
/// Specifies the SSL support requested for a new connection
|
2013-11-11 02:13:32 +00:00
|
|
|
pub enum SslMode {
|
2013-11-10 04:58:38 +00:00
|
|
|
/// The connection will not use SSL
|
|
|
|
NoSsl,
|
|
|
|
/// The connection will use SSL if the backend supports it
|
2013-11-11 02:13:32 +00:00
|
|
|
PreferSsl(SslContext),
|
2013-11-10 04:58:38 +00:00
|
|
|
/// The connection must use SSL
|
2013-11-11 02:13:32 +00:00
|
|
|
RequireSsl(SslContext)
|
2013-11-10 04:58:38 +00:00
|
|
|
}
|
|
|
|
|
2013-09-30 02:12:20 +00:00
|
|
|
/// Represents a transaction on a database connection
|
2013-11-24 22:42:44 +00:00
|
|
|
pub struct PostgresTransaction<'conn> {
|
2014-04-01 06:51:42 +00:00
|
|
|
conn: &'conn PostgresConnection,
|
|
|
|
commit: Cell<bool>,
|
|
|
|
nested: bool,
|
|
|
|
finished: bool,
|
2013-09-16 05:35:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[unsafe_destructor]
|
2013-11-24 22:42:44 +00:00
|
|
|
impl<'conn> Drop for PostgresTransaction<'conn> {
|
2013-09-18 05:06:47 +00:00
|
|
|
fn drop(&mut self) {
|
2014-02-10 07:55:16 +00:00
|
|
|
if !self.finished {
|
2014-04-07 06:37:17 +00:00
|
|
|
let _ = self.finish_inner();
|
2014-02-10 07:55:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'conn> PostgresTransaction<'conn> {
|
2014-04-03 04:26:41 +00:00
|
|
|
fn finish_inner(&mut self) -> PostgresResult<()> {
|
2014-02-10 06:45:26 +00:00
|
|
|
if task::failing() || !self.commit.get() {
|
2014-02-07 04:58:47 +00:00
|
|
|
if self.nested {
|
2014-02-22 07:18:39 +00:00
|
|
|
try!(self.conn.quick_query("ROLLBACK TO sp"));
|
2013-09-16 05:35:30 +00:00
|
|
|
} else {
|
2014-02-22 07:18:39 +00:00
|
|
|
try!(self.conn.quick_query("ROLLBACK"));
|
2013-09-16 05:35:30 +00:00
|
|
|
}
|
2014-02-07 04:58:47 +00:00
|
|
|
} else {
|
|
|
|
if self.nested {
|
2014-02-22 07:18:39 +00:00
|
|
|
try!(self.conn.quick_query("RELEASE sp"));
|
2014-02-07 04:58:47 +00:00
|
|
|
} else {
|
2014-02-22 07:18:39 +00:00
|
|
|
try!(self.conn.quick_query("COMMIT"));
|
2014-02-07 04:58:47 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-10 07:55:16 +00:00
|
|
|
Ok(())
|
2013-09-16 05:35:30 +00:00
|
|
|
}
|
2013-08-27 05:40:23 +00:00
|
|
|
}
|
|
|
|
|
2013-11-24 22:42:44 +00:00
|
|
|
impl<'conn> PostgresTransaction<'conn> {
|
2013-09-30 02:12:20 +00:00
|
|
|
/// Like `PostgresConnection::prepare`.
|
2014-03-28 04:08:22 +00:00
|
|
|
pub fn prepare<'a>(&'a self, query: &str)
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<PostgresStatement<'a>> {
|
2014-03-26 04:01:38 +00:00
|
|
|
self.conn.prepare(query)
|
2013-09-01 18:06:33 +00:00
|
|
|
}
|
|
|
|
|
2013-12-29 03:24:52 +00:00
|
|
|
/// Like `PostgresConnection::execute`.
|
2014-03-28 04:08:22 +00:00
|
|
|
pub fn execute(&self, query: &str, params: &[&ToSql])
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<uint> {
|
2013-12-29 03:24:52 +00:00
|
|
|
self.conn.execute(query, params)
|
2013-09-30 02:12:20 +00:00
|
|
|
}
|
|
|
|
|
2014-03-28 04:08:22 +00:00
|
|
|
/// Like `PostgresConnection::transaction`.
|
|
|
|
pub fn transaction<'a>(&'a self)
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<PostgresTransaction<'a>> {
|
2014-02-10 06:45:26 +00:00
|
|
|
check_desync!(self.conn);
|
2014-02-22 07:18:39 +00:00
|
|
|
try!(self.conn.quick_query("SAVEPOINT sp"));
|
2014-02-10 06:45:26 +00:00
|
|
|
Ok(PostgresTransaction {
|
2013-09-05 06:28:44 +00:00
|
|
|
conn: self.conn,
|
2014-02-10 06:45:26 +00:00
|
|
|
commit: Cell::new(true),
|
2014-02-10 07:55:16 +00:00
|
|
|
nested: true,
|
|
|
|
finished: false,
|
2014-02-10 06:45:26 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2013-10-15 06:34:18 +00:00
|
|
|
/// Like `PostgresConnection::notifications`.
|
2014-02-03 05:09:48 +00:00
|
|
|
pub fn notifications<'a>(&'a self) -> PostgresNotifications<'a> {
|
2013-10-15 06:34:18 +00:00
|
|
|
self.conn.notifications()
|
|
|
|
}
|
|
|
|
|
2014-02-10 06:45:26 +00:00
|
|
|
/// Like `PostgresConnection::is_desynchronized`.
|
|
|
|
pub fn is_desynchronized(&self) -> bool {
|
|
|
|
self.conn.is_desynchronized()
|
|
|
|
}
|
|
|
|
|
2013-09-30 02:12:20 +00:00
|
|
|
/// Determines if the transaction is currently set to commit or roll back.
|
2013-08-27 05:40:23 +00:00
|
|
|
pub fn will_commit(&self) -> bool {
|
2014-02-10 06:45:26 +00:00
|
|
|
self.commit.get()
|
2013-08-27 05:40:23 +00:00
|
|
|
}
|
|
|
|
|
2013-09-30 02:12:20 +00:00
|
|
|
/// Sets the transaction to commit at its completion.
|
2013-08-27 05:40:23 +00:00
|
|
|
pub fn set_commit(&self) {
|
2014-02-10 06:45:26 +00:00
|
|
|
self.commit.set(true);
|
2013-08-27 05:40:23 +00:00
|
|
|
}
|
|
|
|
|
2013-09-30 02:12:20 +00:00
|
|
|
/// Sets the transaction to roll back at its completion.
|
2013-08-27 05:40:23 +00:00
|
|
|
pub fn set_rollback(&self) {
|
2014-02-10 06:45:26 +00:00
|
|
|
self.commit.set(false);
|
2013-08-27 05:40:23 +00:00
|
|
|
}
|
2014-02-10 07:55:16 +00:00
|
|
|
|
|
|
|
/// Consumes the transaction, commiting or rolling it back as appropriate.
|
|
|
|
///
|
|
|
|
/// Functionally equivalent to the `Drop` implementation of
|
|
|
|
/// `PostgresTransaction` except that it returns any error to the caller.
|
2014-04-03 04:26:41 +00:00
|
|
|
pub fn finish(mut self) -> PostgresResult<()> {
|
2014-02-10 07:55:16 +00:00
|
|
|
self.finished = true;
|
|
|
|
self.finish_inner()
|
|
|
|
}
|
2014-03-26 04:01:38 +00:00
|
|
|
|
2014-03-28 04:08:22 +00:00
|
|
|
/// Executes a prepared statement, returning a lazily loaded iterator over
|
|
|
|
/// the resulting rows.
|
2014-03-26 04:01:38 +00:00
|
|
|
///
|
|
|
|
/// No more than `row_limit` rows will be stored in memory at a time. Rows
|
|
|
|
/// will be pulled from the database in batches of `row_limit` as needed.
|
2014-03-28 04:08:22 +00:00
|
|
|
/// If `row_limit` is 0, `lazy_query` is equivalent to `query`.
|
|
|
|
pub fn lazy_query<'trans, 'stmt>(&'trans self,
|
|
|
|
stmt: &'stmt PostgresStatement,
|
|
|
|
params: &[&ToSql],
|
|
|
|
row_limit: uint)
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<PostgresLazyRows
|
|
|
|
<'trans, 'stmt>> {
|
2014-03-26 04:01:38 +00:00
|
|
|
if self.conn as *PostgresConnection != stmt.conn as *PostgresConnection {
|
|
|
|
return Err(PgWrongConnection);
|
|
|
|
}
|
|
|
|
check_desync!(self.conn);
|
2014-03-28 04:08:22 +00:00
|
|
|
stmt.lazy_query(row_limit, params).map(|result| {
|
2014-04-02 07:16:21 +00:00
|
|
|
PostgresLazyRows {
|
2014-03-26 04:01:38 +00:00
|
|
|
trans: self,
|
|
|
|
result: result
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2013-08-27 05:40:23 +00:00
|
|
|
}
|
|
|
|
|
2014-03-26 04:01:38 +00:00
|
|
|
/// A prepared statement
|
|
|
|
pub struct PostgresStatement<'conn> {
|
2014-04-01 06:51:42 +00:00
|
|
|
conn: &'conn PostgresConnection,
|
|
|
|
name: ~str,
|
|
|
|
param_types: Vec<PostgresType>,
|
|
|
|
result_desc: Vec<ResultDescription>,
|
|
|
|
next_portal_id: Cell<uint>,
|
|
|
|
finished: Cell<bool>,
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[unsafe_destructor]
|
|
|
|
impl<'conn> Drop for PostgresStatement<'conn> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
if !self.finished.get() {
|
2014-04-07 06:37:17 +00:00
|
|
|
let _ = self.finish_inner();
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'conn> PostgresStatement<'conn> {
|
2014-04-03 04:26:41 +00:00
|
|
|
fn finish_inner(&mut self) -> PostgresResult<()> {
|
2014-03-26 04:01:38 +00:00
|
|
|
check_desync!(self.conn);
|
|
|
|
try_pg!(self.conn.write_messages([
|
|
|
|
Close {
|
|
|
|
variant: 'S' as u8,
|
|
|
|
name: self.name.as_slice()
|
|
|
|
},
|
|
|
|
Sync]));
|
|
|
|
loop {
|
|
|
|
match try_pg!(self.conn.read_message()) {
|
|
|
|
ReadyForQuery { .. } => break,
|
|
|
|
ErrorResponse { fields } => {
|
|
|
|
try!(self.conn.wait_for_ready());
|
|
|
|
return Err(PgDbError(PostgresDbError::new(fields)));
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn inner_execute(&self, portal_name: &str, row_limit: uint, params: &[&ToSql])
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<()> {
|
2014-03-28 04:39:03 +00:00
|
|
|
if self.param_types.len() != params.len() {
|
|
|
|
return Err(PgWrongParamCount {
|
|
|
|
expected: self.param_types.len(),
|
|
|
|
actual: params.len(),
|
|
|
|
});
|
|
|
|
}
|
2014-03-30 23:19:04 +00:00
|
|
|
let mut formats = Vec::new();
|
|
|
|
let mut values = Vec::new();
|
2014-03-26 04:01:38 +00:00
|
|
|
for (¶m, ty) in params.iter().zip(self.param_types.iter()) {
|
2014-03-28 05:43:10 +00:00
|
|
|
let (format, value) = try!(param.to_sql(ty));
|
2014-03-26 04:01:38 +00:00
|
|
|
formats.push(format as i16);
|
|
|
|
values.push(value);
|
|
|
|
};
|
|
|
|
|
|
|
|
let result_formats: Vec<i16> = self.result_desc.iter().map(|desc| {
|
|
|
|
desc.ty.result_format() as i16
|
|
|
|
}).collect();
|
|
|
|
|
|
|
|
try_pg!(self.conn.write_messages([
|
|
|
|
Bind {
|
|
|
|
portal: portal_name,
|
|
|
|
statement: self.name.as_slice(),
|
|
|
|
formats: formats.as_slice(),
|
|
|
|
values: values.as_slice(),
|
|
|
|
result_formats: result_formats.as_slice()
|
|
|
|
},
|
|
|
|
Execute {
|
|
|
|
portal: portal_name,
|
|
|
|
max_rows: row_limit as i32
|
|
|
|
},
|
|
|
|
Sync]));
|
|
|
|
|
|
|
|
match try_pg!(self.conn.read_message()) {
|
|
|
|
BindComplete => Ok(()),
|
|
|
|
ErrorResponse { fields } => {
|
|
|
|
try!(self.conn.wait_for_ready());
|
|
|
|
Err(PgDbError(PostgresDbError::new(fields)))
|
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-28 04:08:22 +00:00
|
|
|
fn lazy_query<'a>(&'a self, row_limit: uint, params: &[&ToSql])
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<PostgresRows<'a>> {
|
2014-03-26 04:01:38 +00:00
|
|
|
let id = self.next_portal_id.get();
|
|
|
|
self.next_portal_id.set(id + 1);
|
2014-04-10 04:28:20 +00:00
|
|
|
let portal_name = format!("{}p{}", self.name, id);
|
2014-03-26 04:01:38 +00:00
|
|
|
|
|
|
|
try!(self.inner_execute(portal_name, row_limit, params));
|
|
|
|
|
2014-04-02 07:16:21 +00:00
|
|
|
let mut result = PostgresRows {
|
2014-03-26 04:01:38 +00:00
|
|
|
stmt: self,
|
|
|
|
name: portal_name,
|
|
|
|
data: RingBuf::new(),
|
|
|
|
row_limit: row_limit,
|
|
|
|
more_rows: true,
|
|
|
|
finished: false,
|
|
|
|
};
|
|
|
|
try!(result.read_rows())
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a slice containing the expected parameter types.
|
|
|
|
pub fn param_types<'a>(&'a self) -> &'a [PostgresType] {
|
|
|
|
self.param_types.as_slice()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a slice describing the columns of the result of the query.
|
|
|
|
pub fn result_descriptions<'a>(&'a self) -> &'a [ResultDescription] {
|
|
|
|
self.result_desc.as_slice()
|
|
|
|
}
|
|
|
|
|
2014-03-28 04:08:22 +00:00
|
|
|
/// Executes the prepared statement, returning the number of rows modified.
|
2014-03-26 04:01:38 +00:00
|
|
|
///
|
|
|
|
/// If the statement does not modify any rows (e.g. SELECT), 0 is returned.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
|
|
|
/// # use postgres::{PostgresConnection, NoSsl};
|
|
|
|
/// # use postgres::types::ToSql;
|
2014-03-28 04:08:22 +00:00
|
|
|
/// # let conn = PostgresConnection::connect("", &NoSsl).unwrap();
|
2014-03-26 04:01:38 +00:00
|
|
|
/// # let bar = 1i32;
|
|
|
|
/// # let baz = true;
|
2014-03-28 04:08:22 +00:00
|
|
|
/// let stmt = conn.prepare("UPDATE foo SET bar = $1 WHERE baz = $2").unwrap();
|
|
|
|
/// match stmt.execute([&bar as &ToSql, &baz as &ToSql]) {
|
2014-03-26 04:01:38 +00:00
|
|
|
/// Ok(count) => println!("{} row(s) updated", count),
|
|
|
|
/// Err(err) => println!("Error executing query: {}", err)
|
|
|
|
/// }
|
2014-04-03 04:26:41 +00:00
|
|
|
pub fn execute(&self, params: &[&ToSql]) -> PostgresResult<uint> {
|
2014-03-26 04:01:38 +00:00
|
|
|
check_desync!(self.conn);
|
|
|
|
try!(self.inner_execute("", 0, params));
|
|
|
|
|
|
|
|
let num;
|
|
|
|
loop {
|
|
|
|
match try_pg!(self.conn.read_message()) {
|
|
|
|
DataRow { .. } => {}
|
|
|
|
ErrorResponse { fields } => {
|
|
|
|
try!(self.conn.wait_for_ready());
|
|
|
|
return Err(PgDbError(PostgresDbError::new(fields)));
|
|
|
|
}
|
|
|
|
CommandComplete { tag } => {
|
|
|
|
let s = tag.split(' ').last().unwrap();
|
|
|
|
num = match FromStr::from_str(s) {
|
|
|
|
None => 0,
|
|
|
|
Some(n) => n
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
EmptyQueryResponse => {
|
|
|
|
num = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
try!(self.conn.wait_for_ready());
|
|
|
|
|
|
|
|
Ok(num)
|
|
|
|
}
|
|
|
|
|
2014-03-28 04:08:22 +00:00
|
|
|
/// Executes the prepared statement, returning an iterator over the
|
|
|
|
/// resulting rows.
|
2014-03-26 04:01:38 +00:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
|
|
|
/// # use postgres::{PostgresConnection, NoSsl};
|
|
|
|
/// # use postgres::types::ToSql;
|
2014-03-28 04:08:22 +00:00
|
|
|
/// # let conn = PostgresConnection::connect("", &NoSsl).unwrap();
|
|
|
|
/// let stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1").unwrap();
|
2014-03-26 04:01:38 +00:00
|
|
|
/// # let baz = true;
|
2014-03-28 04:08:22 +00:00
|
|
|
/// let mut rows = match stmt.query([&baz as &ToSql]) {
|
2014-03-26 04:01:38 +00:00
|
|
|
/// Ok(rows) => rows,
|
|
|
|
/// Err(err) => fail!("Error running query: {}", err)
|
|
|
|
/// };
|
|
|
|
/// for row in rows {
|
|
|
|
/// let foo: i32 = row["foo"];
|
|
|
|
/// println!("foo: {}", foo);
|
|
|
|
/// }
|
|
|
|
/// ```
|
2014-03-28 04:08:22 +00:00
|
|
|
pub fn query<'a>(&'a self, params: &[&ToSql])
|
2014-04-03 04:26:41 +00:00
|
|
|
-> PostgresResult<PostgresRows<'a>> {
|
2014-03-26 04:01:38 +00:00
|
|
|
check_desync!(self.conn);
|
2014-03-28 04:08:22 +00:00
|
|
|
self.lazy_query(0, params)
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Consumes the statement, clearing it from the Postgres session.
|
|
|
|
///
|
|
|
|
/// Functionally identical to the `Drop` implementation of the
|
|
|
|
/// `PostgresStatement` except that it returns any error to the caller.
|
2014-04-03 04:26:41 +00:00
|
|
|
pub fn finish(mut self) -> PostgresResult<()> {
|
2014-03-26 04:01:38 +00:00
|
|
|
self.finished.set(true);
|
|
|
|
self.finish_inner()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Information about a column of the result of a query.
|
|
|
|
#[deriving(Eq)]
|
|
|
|
pub struct ResultDescription {
|
|
|
|
/// The name of the column
|
|
|
|
name: ~str,
|
|
|
|
/// The type of the data in the column
|
|
|
|
ty: PostgresType
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An iterator over the resulting rows of a query.
|
2014-04-02 07:16:21 +00:00
|
|
|
pub struct PostgresRows<'stmt> {
|
2014-04-01 06:51:42 +00:00
|
|
|
stmt: &'stmt PostgresStatement<'stmt>,
|
|
|
|
name: ~str,
|
2014-04-08 03:02:05 +00:00
|
|
|
data: RingBuf<Vec<Option<Vec<u8>>>>,
|
2014-04-01 06:51:42 +00:00
|
|
|
row_limit: uint,
|
|
|
|
more_rows: bool,
|
|
|
|
finished: bool,
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[unsafe_destructor]
|
2014-04-02 07:16:21 +00:00
|
|
|
impl<'stmt> Drop for PostgresRows<'stmt> {
|
2014-03-26 04:01:38 +00:00
|
|
|
fn drop(&mut self) {
|
|
|
|
if !self.finished {
|
2014-04-07 06:37:17 +00:00
|
|
|
let _ = self.finish_inner();
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-02 07:16:21 +00:00
|
|
|
impl<'stmt> PostgresRows<'stmt> {
|
2014-04-03 04:26:41 +00:00
|
|
|
fn finish_inner(&mut self) -> PostgresResult<()> {
|
2014-03-26 04:01:38 +00:00
|
|
|
check_desync!(self.stmt.conn);
|
|
|
|
try_pg!(self.stmt.conn.write_messages([
|
|
|
|
Close {
|
|
|
|
variant: 'P' as u8,
|
|
|
|
name: self.name.as_slice()
|
|
|
|
},
|
|
|
|
Sync]));
|
|
|
|
loop {
|
|
|
|
match try_pg!(self.stmt.conn.read_message()) {
|
|
|
|
ReadyForQuery { .. } => break,
|
|
|
|
ErrorResponse { fields } => {
|
|
|
|
try!(self.stmt.conn.wait_for_ready());
|
|
|
|
return Err(PgDbError(PostgresDbError::new(fields)));
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2014-04-03 04:26:41 +00:00
|
|
|
fn read_rows(&mut self) -> PostgresResult<()> {
|
2014-03-26 04:01:38 +00:00
|
|
|
loop {
|
|
|
|
match try_pg!(self.stmt.conn.read_message()) {
|
|
|
|
EmptyQueryResponse |
|
|
|
|
CommandComplete { .. } => {
|
|
|
|
self.more_rows = false;
|
|
|
|
break;
|
|
|
|
},
|
|
|
|
PortalSuspended => {
|
|
|
|
self.more_rows = true;
|
|
|
|
break;
|
|
|
|
},
|
|
|
|
DataRow { row } => self.data.push_back(row),
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.stmt.conn.wait_for_ready()
|
|
|
|
}
|
|
|
|
|
2014-04-03 04:26:41 +00:00
|
|
|
fn execute(&mut self) -> PostgresResult<()> {
|
2014-03-26 04:01:38 +00:00
|
|
|
try_pg!(self.stmt.conn.write_messages([
|
|
|
|
Execute {
|
|
|
|
portal: self.name,
|
|
|
|
max_rows: self.row_limit as i32
|
|
|
|
},
|
|
|
|
Sync]));
|
|
|
|
self.read_rows()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-02 07:16:21 +00:00
|
|
|
impl<'stmt> PostgresRows<'stmt> {
|
|
|
|
/// Consumes the `PostgresRows`, cleaning up associated state.
|
2014-03-26 04:01:38 +00:00
|
|
|
///
|
2014-04-02 07:16:21 +00:00
|
|
|
/// Functionally identical to the `Drop` implementation on `PostgresRows`
|
|
|
|
/// except that it returns any error to the caller.
|
2014-04-03 05:56:16 +00:00
|
|
|
#[inline]
|
2014-04-03 04:26:41 +00:00
|
|
|
pub fn finish(mut self) -> PostgresResult<()> {
|
2014-03-26 04:01:38 +00:00
|
|
|
self.finished = true;
|
|
|
|
self.finish_inner()
|
|
|
|
}
|
|
|
|
|
2014-04-03 04:26:41 +00:00
|
|
|
fn try_next(&mut self) -> Option<PostgresResult<PostgresRow<'stmt>>> {
|
2014-03-26 04:01:38 +00:00
|
|
|
if self.data.is_empty() && self.more_rows {
|
2014-03-28 04:20:04 +00:00
|
|
|
match self.execute() {
|
|
|
|
Ok(()) => {}
|
|
|
|
Err(err) => return Some(Err(err))
|
|
|
|
}
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
|
2014-03-28 04:20:04 +00:00
|
|
|
self.data.pop_front().map(|row| {
|
|
|
|
Ok(PostgresRow {
|
2014-03-26 04:01:38 +00:00
|
|
|
stmt: self.stmt,
|
|
|
|
data: row
|
2014-03-28 04:20:04 +00:00
|
|
|
})
|
|
|
|
})
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-02 07:16:21 +00:00
|
|
|
impl<'stmt> Iterator<PostgresRow<'stmt>> for PostgresRows<'stmt> {
|
2014-04-03 05:56:16 +00:00
|
|
|
#[inline]
|
2014-03-26 04:01:38 +00:00
|
|
|
fn next(&mut self) -> Option<PostgresRow<'stmt>> {
|
2014-03-28 04:20:04 +00:00
|
|
|
// we'll never hit the network on a non-lazy result
|
|
|
|
self.try_next().map(|r| r.unwrap())
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
|
2014-04-03 05:56:16 +00:00
|
|
|
#[inline]
|
2014-03-26 04:01:38 +00:00
|
|
|
fn size_hint(&self) -> (uint, Option<uint>) {
|
|
|
|
let lower = self.data.len();
|
|
|
|
let upper = if self.more_rows {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(lower)
|
|
|
|
};
|
|
|
|
(lower, upper)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A single result row of a query.
|
|
|
|
pub struct PostgresRow<'stmt> {
|
2014-04-01 06:51:42 +00:00
|
|
|
stmt: &'stmt PostgresStatement<'stmt>,
|
2014-04-08 03:02:05 +00:00
|
|
|
data: Vec<Option<Vec<u8>>>
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
|
2014-03-30 00:01:41 +00:00
|
|
|
impl<'stmt> PostgresRow<'stmt> {
|
|
|
|
/// Retrieves the contents of a field of the row.
|
|
|
|
///
|
|
|
|
/// A field can be accessed by the name or index of its column, though
|
|
|
|
/// access by index is more efficient. Rows are 1-indexed.
|
|
|
|
///
|
|
|
|
/// Returns an `Error` value if the index does not reference a column or
|
|
|
|
/// the return type is not compatible with the Postgres type.
|
2014-04-03 04:26:41 +00:00
|
|
|
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> PostgresResult<T> {
|
2014-03-30 00:01:41 +00:00
|
|
|
let idx = match idx.idx(self.stmt) {
|
|
|
|
Some(idx) => idx,
|
|
|
|
None => return Err(PgInvalidColumn)
|
|
|
|
};
|
|
|
|
FromSql::from_sql(&self.stmt.result_desc.get(idx).ty,
|
|
|
|
self.data.get(idx))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-26 04:01:38 +00:00
|
|
|
impl<'stmt> Container for PostgresRow<'stmt> {
|
|
|
|
#[inline]
|
|
|
|
fn len(&self) -> uint {
|
|
|
|
self.data.len()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-02 07:16:21 +00:00
|
|
|
impl<'stmt, I: RowIndex+Clone+fmt::Show, T: FromSql> Index<I, T>
|
|
|
|
for PostgresRow<'stmt> {
|
2014-03-30 00:01:41 +00:00
|
|
|
/// Retreives the contents of a field of the row.
|
|
|
|
///
|
|
|
|
/// A field can be accessed by the name or index of its column, though
|
|
|
|
/// access by index is more efficient. Rows are 1-indexed.
|
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
|
|
|
/// Fails if the index does not reference a column or the return type is
|
|
|
|
/// not compatible with the Postgres type.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
|
|
|
/// # use postgres::{PostgresConnection, NoSsl};
|
|
|
|
/// # let conn = PostgresConnection::connect("", &NoSsl).unwrap();
|
|
|
|
/// # let stmt = conn.prepare("").unwrap();
|
|
|
|
/// # let mut result = stmt.query([]).unwrap();
|
|
|
|
/// # let row = result.next().unwrap();
|
|
|
|
/// let foo: i32 = row[1];
|
|
|
|
/// let bar: ~str = row["bar"];
|
|
|
|
/// ```
|
2014-03-26 04:01:38 +00:00
|
|
|
fn index(&self, idx: &I) -> T {
|
2014-03-30 00:01:41 +00:00
|
|
|
match self.get(idx.clone()) {
|
|
|
|
Ok(ok) => ok,
|
2014-04-02 16:19:38 +00:00
|
|
|
Err(err) => fail!("error retrieving column {}: {}", idx, err)
|
2014-03-30 00:01:41 +00:00
|
|
|
}
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A trait implemented by types that can index into columns of a row.
|
2014-03-31 02:21:51 +00:00
|
|
|
pub trait RowIndex {
|
2014-03-30 00:01:41 +00:00
|
|
|
/// Returns the index of the appropriate column, or `None` if no such
|
|
|
|
/// column exists.
|
|
|
|
fn idx(&self, stmt: &PostgresStatement) -> Option<uint>;
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl RowIndex for uint {
|
|
|
|
#[inline]
|
2014-03-30 00:01:41 +00:00
|
|
|
fn idx(&self, stmt: &PostgresStatement) -> Option<uint> {
|
2014-03-30 23:19:04 +00:00
|
|
|
if *self == 0 || *self > stmt.result_desc.len() {
|
2014-03-30 00:01:41 +00:00
|
|
|
None
|
|
|
|
} else {
|
2014-03-30 23:19:04 +00:00
|
|
|
Some(*self - 1)
|
2014-03-30 00:01:41 +00:00
|
|
|
}
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a convenience as the 1 in get[1] resolves to int :(
|
|
|
|
impl RowIndex for int {
|
|
|
|
#[inline]
|
2014-03-30 00:01:41 +00:00
|
|
|
fn idx(&self, stmt: &PostgresStatement) -> Option<uint> {
|
|
|
|
if *self < 0 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*self as uint).idx(stmt)
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> RowIndex for &'a str {
|
2014-03-30 00:01:41 +00:00
|
|
|
fn idx(&self, stmt: &PostgresStatement) -> Option<uint> {
|
2014-03-26 04:01:38 +00:00
|
|
|
for (i, desc) in stmt.result_descriptions().iter().enumerate() {
|
|
|
|
if desc.name.as_slice() == *self {
|
2014-03-30 00:01:41 +00:00
|
|
|
return Some(i);
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-30 00:01:41 +00:00
|
|
|
None
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A lazily-loaded iterator over the resulting rows of a query
|
2014-04-02 07:16:21 +00:00
|
|
|
pub struct PostgresLazyRows<'trans, 'stmt> {
|
|
|
|
result: PostgresRows<'stmt>,
|
2014-04-01 06:51:42 +00:00
|
|
|
trans: &'trans PostgresTransaction<'trans>,
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
|
2014-04-02 07:16:21 +00:00
|
|
|
impl<'trans, 'stmt> PostgresLazyRows<'trans, 'stmt> {
|
|
|
|
/// Like `PostgresRows::finish`.
|
2014-04-03 05:56:16 +00:00
|
|
|
#[inline]
|
2014-04-03 04:26:41 +00:00
|
|
|
pub fn finish(self) -> PostgresResult<()> {
|
2014-03-26 04:01:38 +00:00
|
|
|
self.result.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-03 04:26:41 +00:00
|
|
|
impl<'trans, 'stmt> Iterator<PostgresResult<PostgresRow<'stmt>>>
|
2014-04-02 07:16:21 +00:00
|
|
|
for PostgresLazyRows<'trans, 'stmt> {
|
2014-04-03 05:56:16 +00:00
|
|
|
#[inline]
|
2014-04-03 04:26:41 +00:00
|
|
|
fn next(&mut self) -> Option<PostgresResult<PostgresRow<'stmt>>> {
|
2014-03-28 04:20:04 +00:00
|
|
|
self.result.try_next()
|
2014-03-26 04:01:38 +00:00
|
|
|
}
|
|
|
|
|
2014-04-03 05:56:16 +00:00
|
|
|
#[inline]
|
2014-03-26 04:01:38 +00:00
|
|
|
fn size_hint(&self) -> (uint, Option<uint>) {
|
|
|
|
self.result.size_hint()
|
|
|
|
}
|
|
|
|
}
|