From 14571ab0292f5fa11302b39fded69d98ee99b660 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 8 Dec 2018 16:11:03 -0800 Subject: [PATCH] Remove synchronous crate It will be coming back! It's just going to involve a full rewrite and removing it for now makes some of that restructuring easier. --- .circleci/config.yml | 1 - Cargo.toml | 3 - postgres-native-tls/Cargo.toml | 9 - postgres-native-tls/src/lib.rs | 76 -- postgres-native-tls/src/test.rs | 38 - postgres-openssl/Cargo.toml | 9 - postgres-openssl/src/lib.rs | 100 -- postgres-openssl/src/test.rs | 40 - postgres/Cargo.toml | 66 -- postgres/src/lib.rs | 1560 ---------------------------- postgres/src/macros.rs | 69 -- postgres/src/notification.rs | 203 ---- postgres/src/params.rs | 3 - postgres/src/priv_io.rs | 259 ----- postgres/src/rows.rs | 342 ------ postgres/src/stmt.rs | 605 ----------- postgres/src/text_rows.rs | 191 ---- postgres/src/tls.rs | 50 - postgres/src/transaction.rs | 327 ------ postgres/tests/test.rs | 1481 -------------------------- postgres/tests/types/bit_vec.rs | 30 - postgres/tests/types/chrono.rs | 150 --- postgres/tests/types/eui48.rs | 17 - postgres/tests/types/geo.rs | 58 -- postgres/tests/types/mod.rs | 530 ---------- postgres/tests/types/serde_json.rs | 40 - postgres/tests/types/uuid.rs | 17 - 27 files changed, 6274 deletions(-) delete mode 100644 postgres-native-tls/Cargo.toml delete mode 100644 postgres-native-tls/src/lib.rs delete mode 100644 postgres-native-tls/src/test.rs delete mode 100644 postgres-openssl/Cargo.toml delete mode 100644 postgres-openssl/src/lib.rs delete mode 100644 postgres-openssl/src/test.rs delete mode 100644 postgres/Cargo.toml delete mode 100644 postgres/src/lib.rs delete mode 100644 postgres/src/macros.rs delete mode 100644 postgres/src/notification.rs delete mode 100644 postgres/src/params.rs delete mode 100644 postgres/src/priv_io.rs delete mode 100644 postgres/src/rows.rs delete mode 100644 postgres/src/stmt.rs delete mode 100644 postgres/src/text_rows.rs delete mode 100644 postgres/src/tls.rs delete mode 100644 postgres/src/transaction.rs delete mode 100644 postgres/tests/test.rs delete mode 100644 postgres/tests/types/bit_vec.rs delete mode 100644 postgres/tests/types/chrono.rs delete mode 100644 postgres/tests/types/eui48.rs delete mode 100644 postgres/tests/types/geo.rs delete mode 100644 postgres/tests/types/mod.rs delete mode 100644 postgres/tests/types/serde_json.rs delete mode 100644 postgres/tests/types/uuid.rs diff --git a/.circleci/config.yml b/.circleci/config.yml index 96023d11..47efb05d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,6 +34,5 @@ jobs: - run: rustc --version > ~/rust-version - *RESTORE_DEPS - run: cargo test --all - - run: cargo test -p postgres --all-features - run: cargo test -p tokio-postgres --all-features - *SAVE_DEPS diff --git a/Cargo.toml b/Cargo.toml index 54c0fbbe..2424197c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,8 @@ [workspace] members = [ "codegen", - "postgres", "postgres-protocol", "postgres-shared", - "postgres-openssl", - "postgres-native-tls", "tokio-postgres", "tokio-postgres-native-tls", "tokio-postgres-openssl", diff --git a/postgres-native-tls/Cargo.toml b/postgres-native-tls/Cargo.toml deleted file mode 100644 index ada50ccf..00000000 --- a/postgres-native-tls/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "postgres-native-tls" -version = "0.1.0" -authors = ["Steven Fackler "] - -[dependencies] -native-tls = "0.2" - -postgres = { version = "0.15", path = "../postgres" } diff --git a/postgres-native-tls/src/lib.rs b/postgres-native-tls/src/lib.rs deleted file mode 100644 index ea887ed7..00000000 --- a/postgres-native-tls/src/lib.rs +++ /dev/null @@ -1,76 +0,0 @@ -pub extern crate native_tls; -extern crate postgres; - -use native_tls::TlsConnector; -use postgres::tls::{Stream, TlsHandshake, TlsStream}; -use std::error::Error; -use std::fmt::{self, Debug}; -use std::io::{self, Read, Write}; - -#[cfg(test)] -mod test; - -pub struct NativeTls { - connector: TlsConnector, -} - -impl Debug for NativeTls { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("NativeTls").finish() - } -} - -impl NativeTls { - pub fn new() -> Result { - let connector = TlsConnector::builder().build()?; - Ok(NativeTls::with_connector(connector)) - } - - pub fn with_connector(connector: TlsConnector) -> NativeTls { - NativeTls { connector } - } -} - -impl TlsHandshake for NativeTls { - fn tls_handshake( - &self, - domain: &str, - stream: Stream, - ) -> Result, Box> { - let stream = self.connector.connect(domain, stream)?; - Ok(Box::new(NativeTlsStream(stream))) - } -} - -#[derive(Debug)] -struct NativeTlsStream(native_tls::TlsStream); - -impl Read for NativeTlsStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } -} - -impl Write for NativeTlsStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } -} - -impl TlsStream for NativeTlsStream { - fn get_ref(&self) -> &Stream { - self.0.get_ref() - } - - fn get_mut(&mut self) -> &mut Stream { - self.0.get_mut() - } - - fn tls_server_end_point(&self) -> Option> { - self.0.tls_server_end_point().ok().and_then(|o| o) - } -} diff --git a/postgres-native-tls/src/test.rs b/postgres-native-tls/src/test.rs deleted file mode 100644 index a84798d4..00000000 --- a/postgres-native-tls/src/test.rs +++ /dev/null @@ -1,38 +0,0 @@ -use native_tls::{Certificate, TlsConnector}; -use postgres::{Connection, TlsMode}; - -use NativeTls; - -#[test] -fn connect() { - let cert = include_bytes!("../../test/server.crt"); - let cert = Certificate::from_pem(cert).unwrap(); - - let mut builder = TlsConnector::builder(); - builder.add_root_certificate(cert); - let connector = builder.build().unwrap(); - - let handshake = NativeTls::with_connector(connector); - let conn = Connection::connect( - "postgres://ssl_user@localhost:5433/postgres", - TlsMode::Require(&handshake), - ).unwrap(); - conn.execute("SELECT 1::VARCHAR", &[]).unwrap(); -} - -#[test] -fn scram_user() { - let cert = include_bytes!("../../test/server.crt"); - let cert = Certificate::from_pem(cert).unwrap(); - - let mut builder = TlsConnector::builder(); - builder.add_root_certificate(cert); - let connector = builder.build().unwrap(); - - let handshake = NativeTls::with_connector(connector); - let conn = Connection::connect( - "postgres://scram_user:password@localhost:5433/postgres", - TlsMode::Require(&handshake), - ).unwrap(); - conn.execute("SELECT 1::VARCHAR", &[]).unwrap(); -} diff --git a/postgres-openssl/Cargo.toml b/postgres-openssl/Cargo.toml deleted file mode 100644 index 53321fd5..00000000 --- a/postgres-openssl/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "postgres-openssl" -version = "0.1.0" -authors = ["Steven Fackler "] - -[dependencies] -openssl = "0.10.9" - -postgres = { version = "0.15", path = "../postgres" } diff --git a/postgres-openssl/src/lib.rs b/postgres-openssl/src/lib.rs deleted file mode 100644 index 8b29d35a..00000000 --- a/postgres-openssl/src/lib.rs +++ /dev/null @@ -1,100 +0,0 @@ -pub extern crate openssl; -extern crate postgres; - -use openssl::error::ErrorStack; -use openssl::hash::MessageDigest; -use openssl::nid::Nid; -use openssl::ssl::{ConnectConfiguration, SslConnector, SslMethod, SslStream}; -use postgres::tls::{Stream, TlsHandshake, TlsStream}; -use std::error::Error; -use std::fmt; -use std::io::{self, Read, Write}; - -#[cfg(test)] -mod test; - -pub struct OpenSsl { - connector: SslConnector, - config: Box Result<(), ErrorStack> + Sync + Send>, -} - -impl fmt::Debug for OpenSsl { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("OpenSsl").finish() - } -} - -impl OpenSsl { - pub fn new() -> Result { - let connector = SslConnector::builder(SslMethod::tls())?.build(); - Ok(OpenSsl::with_connector(connector)) - } - - pub fn with_connector(connector: SslConnector) -> OpenSsl { - OpenSsl { - connector, - config: Box::new(|_| Ok(())), - } - } - - pub fn callback(&mut self, f: F) - where - F: Fn(&mut ConnectConfiguration) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.config = Box::new(f); - } -} - -impl TlsHandshake for OpenSsl { - fn tls_handshake( - &self, - domain: &str, - stream: Stream, - ) -> Result, Box> { - let mut ssl = self.connector.configure()?; - (self.config)(&mut ssl)?; - let stream = ssl.connect(domain, stream)?; - - Ok(Box::new(OpenSslStream(stream))) - } -} - -#[derive(Debug)] -struct OpenSslStream(SslStream); - -impl Read for OpenSslStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } -} - -impl Write for OpenSslStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } -} - -impl TlsStream for OpenSslStream { - fn get_ref(&self) -> &Stream { - self.0.get_ref() - } - - fn get_mut(&mut self) -> &mut Stream { - self.0.get_mut() - } - - fn tls_server_end_point(&self) -> Option> { - let cert = self.0.ssl().peer_certificate()?; - let algo_nid = cert.signature_algorithm().object().nid(); - let signature_algorithms = algo_nid.signature_algorithms()?; - let md = match signature_algorithms.digest { - Nid::MD5 | Nid::SHA1 => MessageDigest::sha256(), - nid => MessageDigest::from_nid(nid)?, - }; - cert.digest(md).ok().map(|b| b.to_vec()) - } -} diff --git a/postgres-openssl/src/test.rs b/postgres-openssl/src/test.rs deleted file mode 100644 index 8314e179..00000000 --- a/postgres-openssl/src/test.rs +++ /dev/null @@ -1,40 +0,0 @@ -use openssl::ssl::{SslConnector, SslMethod}; -use postgres::{Connection, TlsMode}; - -use OpenSsl; - -#[test] -fn require() { - let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_ca_file("../test/server.crt").unwrap(); - let negotiator = OpenSsl::with_connector(builder.build()); - let conn = Connection::connect( - "postgres://ssl_user@localhost:5433/postgres", - TlsMode::Require(&negotiator), - ).unwrap(); - conn.execute("SELECT 1::VARCHAR", &[]).unwrap(); -} - -#[test] -fn prefer() { - let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_ca_file("../test/server.crt").unwrap(); - let negotiator = OpenSsl::with_connector(builder.build()); - let conn = Connection::connect( - "postgres://ssl_user@localhost:5433/postgres", - TlsMode::Require(&negotiator), - ).unwrap(); - conn.execute("SELECT 1::VARCHAR", &[]).unwrap(); -} - -#[test] -fn scram_user() { - let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_ca_file("../test/server.crt").unwrap(); - let negotiator = OpenSsl::with_connector(builder.build()); - let conn = Connection::connect( - "postgres://scram_user:password@localhost:5433/postgres", - TlsMode::Require(&negotiator), - ).unwrap(); - conn.execute("SELECT 1::VARCHAR", &[]).unwrap(); -} diff --git a/postgres/Cargo.toml b/postgres/Cargo.toml deleted file mode 100644 index 2a21d33a..00000000 --- a/postgres/Cargo.toml +++ /dev/null @@ -1,66 +0,0 @@ -[package] -name = "postgres" -version = "0.15.2" -authors = ["Steven Fackler "] -license = "MIT" -description = "A native PostgreSQL driver" -repository = "https://github.com/sfackler/rust-postgres" -readme = "../README.md" -keywords = ["database", "postgres", "postgresql", "sql"] -include = ["src/*", "Cargo.toml", "LICENSE", "README.md", "THIRD_PARTY"] -categories = ["database"] - -[package.metadata.docs.rs] -features = [ - "with-bit-vec-0.5", - "with-chrono-0.4", - "with-eui48-0.3", - "with-geo-0.10", - "with-serde_json-1", - "with-uuid-0.6", - "with-openssl", - "with-native-tls", -] - -[badges] -circle-ci = { repository = "sfackler/rust-postgres" } - -[lib] -name = "postgres" -path = "src/lib.rs" -test = false -bench = false - -[[test]] -name = "test" -path = "tests/test.rs" - -[features] -"with-bit-vec-0.5" = ["postgres-shared/with-bit-vec-0.5"] -"with-chrono-0.4" = ["postgres-shared/with-chrono-0.4"] -"with-eui48-0.3" = ["postgres-shared/with-eui48-0.3"] -"with-geo-0.10" = ["postgres-shared/with-geo-0.10"] -"with-serde_json-1" = ["postgres-shared/with-serde_json-1"] -"with-uuid-0.6" = ["postgres-shared/with-uuid-0.6"] - -no-logging = [] - -[dependencies] -bytes = "0.4" -fallible-iterator = "0.1.3" -log = "0.4" -socket2 = { version = "0.3.5", features = ["unix"] } - -postgres-protocol = { version = "0.3.0", path = "../postgres-protocol" } -postgres-shared = { version = "0.4.1", path = "../postgres-shared" } - -[dev-dependencies] -hex = "0.3" -url = "1.0" - -bit-vec = "0.5" -chrono = "0.4" -eui48 = "0.3" -geo = "0.10" -serde_json = "1.0" -uuid = "0.6" diff --git a/postgres/src/lib.rs b/postgres/src/lib.rs deleted file mode 100644 index dce02455..00000000 --- a/postgres/src/lib.rs +++ /dev/null @@ -1,1560 +0,0 @@ -//! A pure-Rust frontend for the popular PostgreSQL database. -//! -//! ```rust,no_run -//! extern crate postgres; -//! -//! use postgres::{Connection, TlsMode}; -//! -//! struct Person { -//! id: i32, -//! name: String, -//! data: Option> -//! } -//! -//! fn main() { -//! let conn = Connection::connect("postgresql://postgres@localhost:5433", TlsMode::None) -//! .unwrap(); -//! -//! conn.execute("CREATE TABLE person ( -//! id SERIAL PRIMARY KEY, -//! name VARCHAR NOT NULL, -//! data BYTEA -//! )", &[]).unwrap(); -//! let me = Person { -//! id: 0, -//! name: "Steven".to_owned(), -//! data: None -//! }; -//! conn.execute("INSERT INTO person (name, data) VALUES ($1, $2)", -//! &[&me.name, &me.data]).unwrap(); -//! -//! for row in &conn.query("SELECT id, name, data FROM person", &[]).unwrap() { -//! let person = Person { -//! id: row.get(0), -//! name: row.get(1), -//! data: row.get(2) -//! }; -//! println!("Found person {}", person.name); -//! } -//! } -//! ``` -//! -//! # SSL/TLS -//! -//! This crate supports TLS secured connections. The `TlsMode` enum is passed to connection methods -//! and indicates if the connection will not, may, or must be secured by TLS. The TLS implementation -//! is pluggable through the `TlsHandshake` trait. Implementations for OpenSSL, Secure Transport, -//! SChannel, and the `native-tls` crate are provided behind the `with-openssl`, -//! `with-security-framework`, `with-schannel`, and `with-native-tls` feature flags respectively. -//! -//! ## Examples -//! -//! Connecting using `native-tls`: -//! -//! ```no_run -//! extern crate postgres; -//! -//! use postgres::{Connection, TlsMode}; -//! # #[cfg(feature = "with-native-tls")] -//! use postgres::tls::native_tls::NativeTls; -//! -//! # #[cfg(not(feature = "with-native-tls"))] fn main() {} -//! # #[cfg(feature = "with-native-tls")] -//! fn main() { -//! let negotiator = NativeTls::new().unwrap(); -//! let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::Require(&negotiator)) -//! .unwrap(); -//! } -//! ``` -#![doc(html_root_url = "https://docs.rs/postgres/0.15.1")] -#![warn(missing_docs)] -#![allow(unknown_lints, needless_lifetimes, doc_markdown)] // for clippy - -extern crate bytes; -extern crate fallible_iterator; -#[cfg(not(feature = "no-logging"))] -#[macro_use] -extern crate log; -extern crate postgres_protocol; -extern crate postgres_shared; -extern crate socket2; - -use fallible_iterator::FallibleIterator; -use postgres_protocol::authentication; -use postgres_protocol::authentication::sasl::{self, ChannelBinding, ScramSha256}; -use postgres_protocol::message::backend::{self, ErrorFields}; -use postgres_protocol::message::frontend; -use postgres_shared::rows::RowData; -use std::cell::{Cell, RefCell}; -use std::collections::{HashMap, VecDeque}; -use std::fmt; -use std::io; -use std::mem; -use std::result; -use std::sync::Arc; -use std::time::Duration; - -use error::{DbError, SqlState}; -use notification::{Notification, Notifications}; -use params::{IntoConnectParams, User}; -use priv_io::MessageStream; -use rows::Rows; -use stmt::{Column, Statement}; -use text_rows::TextRows; -use tls::TlsHandshake; -use transaction::{IsolationLevel, Transaction}; -use types::{Field, FromSql, IsNull, Kind, Oid, ToSql, Type}; - -#[doc(inline)] -pub use error::Error; -#[doc(inline)] -pub use postgres_shared::CancelData; -#[doc(inline)] -pub use postgres_shared::{error, types}; - -#[macro_use] -mod macros; - -pub mod notification; -pub mod params; -mod priv_io; -pub mod rows; -pub mod stmt; -pub mod text_rows; -pub mod tls; -pub mod transaction; - -const TYPEINFO_QUERY: &'static str = "__typeinfo"; -const TYPEINFO_ENUM_QUERY: &'static str = "__typeinfo_enum"; -const TYPEINFO_COMPOSITE_QUERY: &'static str = "__typeinfo_composite"; - -/// A type alias of the result returned by many methods. -pub type Result = result::Result; - -/// A trait implemented by types that can handle Postgres notice messages. -/// -/// It is implemented for all `Send + FnMut(DbError)` closures. -pub trait HandleNotice: Send { - /// Handle a Postgres notice message - fn handle_notice(&mut self, notice: DbError); -} - -impl HandleNotice for F { - fn handle_notice(&mut self, notice: DbError) { - self(notice) - } -} - -/// A notice handler which logs at the `info` level. -/// -/// This is the default handler used by a `Connection`. -#[derive(Copy, Clone, Debug)] -pub struct LoggingNoticeHandler; - -impl HandleNotice for LoggingNoticeHandler { - fn handle_notice(&mut self, _notice: DbError) { - info!("{}: {}", _notice.severity, _notice.message); - } -} - -/// 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 `CancelData` object can be created via `Connection::cancel_data`. The -/// object can cancel any query made on that connection. -/// -/// Only the host and port of the connection info are used. See -/// `Connection::connect` for details of the `params` argument. -/// -/// # Example -/// -/// ```rust,no_run -/// # use postgres::{Connection, TlsMode}; -/// # use std::thread; -/// # let url = ""; -/// let conn = Connection::connect(url, TlsMode::None).unwrap(); -/// let cancel_data = conn.cancel_data(); -/// thread::spawn(move || { -/// conn.execute("SOME EXPENSIVE QUERY", &[]).unwrap(); -/// }); -/// postgres::cancel_query(url, TlsMode::None, &cancel_data).unwrap(); -/// ``` -pub fn cancel_query(params: T, tls: TlsMode, data: &CancelData) -> Result<()> -where - T: IntoConnectParams, -{ - let params = params.into_connect_params().map_err(error::connect)?; - let mut socket = priv_io::initialize_stream(¶ms, tls)?; - - let mut buf = vec![]; - frontend::cancel_request(data.process_id, data.secret_key, &mut buf); - socket.write_all(&buf)?; - socket.flush()?; - - Ok(()) -} - -fn bad_response() -> io::Error { - io::Error::new( - io::ErrorKind::InvalidInput, - "the server returned an unexpected response", - ) -} - -fn desynchronized() -> io::Error { - io::Error::new( - io::ErrorKind::Other, - "communication with the server has desynchronized due to an earlier IO error", - ) -} - -/// Specifies the TLS support requested for a new connection. -#[derive(Debug)] -pub enum TlsMode<'a> { - /// The connection will not use TLS. - None, - /// The connection will use TLS if the backend supports it. - Prefer(&'a TlsHandshake), - /// The connection must use TLS. - Require(&'a TlsHandshake), -} - -#[derive(Debug)] -struct StatementInfo { - name: String, - param_types: Vec, - columns: Vec, -} - -struct InnerConnection { - stream: MessageStream, - notice_handler: Box, - notifications: VecDeque, - cancel_data: CancelData, - unknown_types: HashMap, - cached_statements: HashMap>, - parameters: HashMap, - next_stmt_id: u32, - trans_depth: u32, - desynchronized: bool, - finished: bool, - has_typeinfo_query: bool, - has_typeinfo_enum_query: bool, - has_typeinfo_composite_query: bool, -} - -impl Drop for InnerConnection { - fn drop(&mut self) { - if !self.finished { - let _ = self.finish_inner(); - } - } -} - -impl InnerConnection { - fn connect(params: T, tls: TlsMode) -> Result - where - T: IntoConnectParams, - { - let params = params.into_connect_params().map_err(error::connect)?; - let stream = priv_io::initialize_stream(¶ms, tls)?; - - let user = match params.user() { - Some(user) => user, - None => { - return Err(error::connect( - "user missing from connection parameters".into(), - )); - } - }; - - let mut conn = InnerConnection { - stream: MessageStream::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, - has_typeinfo_query: false, - has_typeinfo_enum_query: false, - has_typeinfo_composite_query: false, - }; - - let mut options = params.options().to_owned(); - options.push(("client_encoding".to_owned(), "UTF8".to_owned())); - // Postgres uses the value of TimeZone as the time zone for TIMESTAMP - // WITH TIME ZONE values. Timespec converts to GMT internally. - options.push(("timezone".to_owned(), "GMT".to_owned())); - // We have to clone here since we need the user again for auth - options.push(("user".to_owned(), user.name().to_owned())); - if let Some(database) = params.database() { - options.push(("database".to_owned(), database.to_owned())); - } - - let options = options.iter().map(|&(ref a, ref b)| (&**a, &**b)); - conn.stream - .write_message(|buf| frontend::startup_message(options, buf))?; - conn.stream.flush()?; - - conn.handle_auth(user)?; - - loop { - match conn.read_message()? { - backend::Message::BackendKeyData(body) => { - conn.cancel_data.process_id = body.process_id(); - conn.cancel_data.secret_key = body.secret_key(); - } - backend::Message::ReadyForQuery(_) => break, - backend::Message::ErrorResponse(body) => { - return Err(err(&mut body.fields())); - } - _ => return Err(bad_response().into()), - } - } - - Ok(conn) - } - - fn read_message_with_notification(&mut self) -> io::Result { - debug_assert!(!self.desynchronized); - loop { - match try_desync!(self, self.stream.read_message()) { - backend::Message::NoticeResponse(body) => { - if let Ok(err) = DbError::new(&mut body.fields()) { - self.notice_handler.handle_notice(err); - } - } - backend::Message::ParameterStatus(body) => { - self.parameters - .insert(body.name()?.to_owned(), body.value()?.to_owned()); - } - val => return Ok(val), - } - } - } - - fn read_message_with_notification_timeout( - &mut self, - timeout: Duration, - ) -> io::Result> { - debug_assert!(!self.desynchronized); - loop { - match try_desync!(self, self.stream.read_message_timeout(timeout)) { - Some(backend::Message::NoticeResponse(body)) => { - if let Ok(err) = Err(err(&mut body.fields())) { - self.notice_handler.handle_notice(err); - } - } - Some(backend::Message::ParameterStatus(body)) => { - self.parameters - .insert(body.name()?.to_owned(), body.value()?.to_owned()); - } - val => return Ok(val), - } - } - } - - fn read_message_with_notification_nonblocking( - &mut self, - ) -> io::Result> { - debug_assert!(!self.desynchronized); - loop { - match try_desync!(self, self.stream.read_message_nonblocking()) { - Some(backend::Message::NoticeResponse(body)) => { - if let Ok(err) = Err(err(&mut body.fields())) { - self.notice_handler.handle_notice(err); - } - } - Some(backend::Message::ParameterStatus(body)) => { - self.parameters - .insert(body.name()?.to_owned(), body.value()?.to_owned()); - } - val => return Ok(val), - } - } - } - - fn read_message(&mut self) -> io::Result { - loop { - match self.read_message_with_notification()? { - backend::Message::NotificationResponse(body) => { - self.notifications.push_back(Notification { - process_id: body.process_id(), - channel: body.channel()?.to_owned(), - payload: body.message()?.to_owned(), - }) - } - val => return Ok(val), - } - } - } - - fn handle_auth(&mut self, user: &User) -> Result<()> { - match self.read_message()? { - backend::Message::AuthenticationOk => return Ok(()), - backend::Message::AuthenticationCleartextPassword => { - let pass = user.password().ok_or_else(|| { - error::connect("a password was requested but not provided".into()) - })?; - self.stream - .write_message(|buf| frontend::password_message(pass, buf))?; - self.stream.flush()?; - } - backend::Message::AuthenticationMd5Password(body) => { - let pass = user.password().ok_or_else(|| { - error::connect("a password was requested but not provided".into()) - })?; - let output = - authentication::md5_hash(user.name().as_bytes(), pass.as_bytes(), body.salt()); - self.stream - .write_message(|buf| frontend::password_message(&output, buf))?; - self.stream.flush()?; - } - backend::Message::AuthenticationSasl(body) => { - let mut has_scram = false; - let mut has_scram_plus = false; - let mut mechanisms = body.mechanisms(); - while let Some(mechanism) = mechanisms.next()? { - match mechanism { - sasl::SCRAM_SHA_256 => has_scram = true, - sasl::SCRAM_SHA_256_PLUS => has_scram_plus = true, - _ => {} - } - } - let channel_binding = self - .stream - .get_ref() - .tls_server_end_point() - .map(ChannelBinding::tls_server_end_point); - - let (channel_binding, mechanism) = if has_scram_plus { - match channel_binding { - Some(channel_binding) => (channel_binding, sasl::SCRAM_SHA_256_PLUS), - None => (ChannelBinding::unsupported(), sasl::SCRAM_SHA_256), - } - } else if has_scram { - match channel_binding { - Some(_) => (ChannelBinding::unrequested(), sasl::SCRAM_SHA_256), - None => (ChannelBinding::unsupported(), sasl::SCRAM_SHA_256), - } - } else { - return Err( - io::Error::new(io::ErrorKind::Other, "unsupported authentication").into(), - ); - }; - - let pass = user.password().ok_or_else(|| { - error::connect("a password was requested but not provided".into()) - })?; - - let mut scram = ScramSha256::new(pass.as_bytes(), channel_binding); - - self.stream.write_message(|buf| { - frontend::sasl_initial_response(mechanism, scram.message(), buf) - })?; - self.stream.flush()?; - - let body = match self.read_message()? { - backend::Message::AuthenticationSaslContinue(body) => body, - backend::Message::ErrorResponse(body) => return Err(err(&mut body.fields())), - _ => return Err(bad_response().into()), - }; - - scram.update(body.data())?; - - self.stream - .write_message(|buf| frontend::sasl_response(scram.message(), buf))?; - self.stream.flush()?; - - let body = match self.read_message()? { - backend::Message::AuthenticationSaslFinal(body) => body, - backend::Message::ErrorResponse(body) => return Err(err(&mut body.fields())), - _ => return Err(bad_response().into()), - }; - - scram.finish(body.data())?; - } - backend::Message::AuthenticationKerberosV5 - | backend::Message::AuthenticationScmCredential - | backend::Message::AuthenticationGss - | backend::Message::AuthenticationSspi => { - return Err( - io::Error::new(io::ErrorKind::Other, "unsupported authentication").into(), - ) - } - backend::Message::ErrorResponse(body) => return Err(err(&mut body.fields())), - _ => return Err(bad_response().into()), - } - - match self.read_message()? { - backend::Message::AuthenticationOk => Ok(()), - backend::Message::ErrorResponse(body) => Err(err(&mut body.fields())), - _ => Err(bad_response().into()), - } - } - - fn set_notice_handler(&mut self, handler: Box) -> Box { - mem::replace(&mut self.notice_handler, handler) - } - - fn raw_prepare( - &mut self, - stmt_name: &str, - query: &str, - types: &[Option], - ) -> Result<(Vec, Vec)> { - debug!("preparing query with name `{}`: {}", stmt_name, query); - - self.stream.write_message(|buf| { - frontend::parse( - stmt_name, - query, - types.iter().map(|t| t.as_ref().map_or(0, |t| t.oid())), - buf, - ) - })?; - self.stream - .write_message(|buf| frontend::describe(b'S', stmt_name, buf))?; - self.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?; - self.stream.flush()?; - - match self.read_message()? { - backend::Message::ParseComplete => {} - backend::Message::ErrorResponse(body) => { - self.wait_for_ready()?; - return Err(err(&mut body.fields())); - } - _ => bad_response!(self), - } - - let raw_param_types = match self.read_message()? { - backend::Message::ParameterDescription(body) => body, - _ => bad_response!(self), - }; - - let raw_columns = match self.read_message()? { - backend::Message::RowDescription(body) => Some(body), - backend::Message::NoData => None, - _ => bad_response!(self), - }; - - self.wait_for_ready()?; - - let param_types = raw_param_types - .parameters() - .map_err(Into::into) - .and_then(|oid| self.get_type(oid)) - .collect()?; - - let columns = self.parse_cols(raw_columns)?; - Ok((param_types, columns)) - } - - fn read_rows(&mut self, mut consumer: F) -> Result - where - F: FnMut(RowData), - { - let more_rows; - loop { - match self.read_message()? { - backend::Message::EmptyQueryResponse | backend::Message::CommandComplete(_) => { - more_rows = false; - break; - } - backend::Message::PortalSuspended => { - more_rows = true; - break; - } - backend::Message::DataRow(body) => consumer(RowData::new(body)?), - backend::Message::ErrorResponse(body) => { - self.wait_for_ready()?; - return Err(err(&mut body.fields())); - } - backend::Message::CopyInResponse(_) => { - self.stream.write_message(|buf| { - frontend::copy_fail("COPY queries cannot be directly executed", buf) - })?; - self.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?; - self.stream.flush()?; - } - backend::Message::CopyOutResponse(_) => { - loop { - if let backend::Message::ReadyForQuery(_) = self.read_message()? { - break; - } - } - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "COPY queries cannot be directly \ - executed", - ).into()); - } - _ => { - self.desynchronized = true; - return Err(bad_response().into()); - } - } - } - self.wait_for_ready()?; - Ok(more_rows) - } - - fn raw_execute( - &mut self, - stmt_name: &str, - portal_name: &str, - row_limit: i32, - param_types: &[Type], - params: &[&ToSql], - ) -> Result<()> { - assert!( - param_types.len() == params.len(), - "expected {} parameters but got {}", - param_types.len(), - params.len() - ); - debug!( - "executing statement {} with parameters: {:?}", - stmt_name, params - ); - - { - let r = self.stream.write_message(|buf| { - frontend::bind( - portal_name, - stmt_name, - Some(1), - params.iter().zip(param_types), - |(param, ty), buf| match param.to_sql_checked(ty, buf) { - Ok(IsNull::Yes) => Ok(postgres_protocol::IsNull::Yes), - Ok(IsNull::No) => Ok(postgres_protocol::IsNull::No), - Err(e) => Err(e), - }, - Some(1), - buf, - ) - }); - match r { - Ok(()) => {} - Err(frontend::BindError::Conversion(e)) => { - return Err(error::conversion(e)); - } - Err(frontend::BindError::Serialization(e)) => return Err(e.into()), - } - } - - self.stream - .write_message(|buf| frontend::execute(portal_name, row_limit, buf))?; - self.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?; - self.stream.flush()?; - - match self.read_message()? { - backend::Message::BindComplete => Ok(()), - backend::Message::ErrorResponse(body) => { - self.wait_for_ready()?; - Err(err(&mut body.fields())) - } - _ => { - self.desynchronized = true; - Err(bad_response().into()) - } - } - } - - 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_typed<'a>( - &mut self, - query: &str, - types: &[Option], - conn: &'a Connection, - ) -> Result> { - let stmt_name = self.make_stmt_name(); - let (param_types, columns) = self.raw_prepare(&stmt_name, query, types)?; - let info = Arc::new(StatementInfo { - name: stmt_name, - param_types: param_types, - columns: columns, - }); - Ok(Statement::new(conn, info, Cell::new(0), false)) - } - - fn prepare_cached<'a>(&mut self, query: &str, conn: &'a Connection) -> Result> { - let info = self.cached_statements.get(query).cloned(); - - let info = match info { - Some(info) => info, - None => { - let stmt_name = self.make_stmt_name(); - let (param_types, columns) = self.raw_prepare(&stmt_name, query, &[])?; - let info = Arc::new(StatementInfo { - name: stmt_name, - param_types: param_types, - columns: columns, - }); - self.cached_statements - .insert(query.to_owned(), info.clone()); - info - } - }; - - Ok(Statement::new(conn, info, Cell::new(0), true)) - } - - fn close_statement(&mut self, name: &str, type_: u8) -> Result<()> { - self.stream - .write_message(|buf| frontend::close(type_, name, buf))?; - self.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?; - self.stream.flush()?; - let resp = match self.read_message()? { - backend::Message::CloseComplete => Ok(()), - backend::Message::ErrorResponse(body) => Err(err(&mut body.fields())), - _ => bad_response!(self), - }; - self.wait_for_ready()?; - resp - } - - fn get_type(&mut self, oid: Oid) -> Result { - if let Some(ty) = Type::from_oid(oid) { - return Ok(ty); - } - - if let Some(ty) = self.unknown_types.get(&oid) { - return Ok(ty.clone()); - } - - let ty = self.read_type(oid)?; - self.unknown_types.insert(oid, ty.clone()); - Ok(ty) - } - - fn parse_cols(&mut self, raw: Option) -> Result> { - match raw { - Some(body) => body - .fields() - .and_then(|field| { - Ok(Column::new( - field.name().to_owned(), - self.get_type(field.type_oid())?, - )) - }).collect() - .map_err(From::from), - None => Ok(vec![]), - } - } - - fn setup_typeinfo_query(&mut self) -> Result<()> { - if self.has_typeinfo_query { - return Ok(()); - } - - match self.raw_prepare( - TYPEINFO_QUERY, - "SELECT t.typname, t.typtype, t.typelem, r.rngsubtype, \ - t.typbasetype, n.nspname, t.typrelid \ - FROM pg_catalog.pg_type t \ - LEFT OUTER JOIN pg_catalog.pg_range r ON \ - r.rngtypid = t.oid \ - INNER JOIN pg_catalog.pg_namespace n ON \ - t.typnamespace = n.oid \ - WHERE t.oid = $1", - &[], - ) { - Ok(..) => {} - // Range types weren't added until Postgres 9.2, so pg_range may not exist - Err(ref e) if e.code() == Some(&SqlState::UNDEFINED_TABLE) => { - self.raw_prepare( - TYPEINFO_QUERY, - "SELECT t.typname, t.typtype, t.typelem, NULL::OID, \ - t.typbasetype, n.nspname, t.typrelid \ - FROM pg_catalog.pg_type t \ - INNER JOIN pg_catalog.pg_namespace n \ - ON t.typnamespace = n.oid \ - WHERE t.oid = $1", - &[], - )?; - } - Err(e) => return Err(e), - } - - self.has_typeinfo_query = true; - Ok(()) - } - - #[allow(if_not_else)] - fn read_type(&mut self, oid: Oid) -> Result { - self.setup_typeinfo_query()?; - self.raw_execute(TYPEINFO_QUERY, "", 0, &[Type::OID], &[&oid])?; - let mut row = None; - self.read_rows(|r| row = Some(r))?; - - let get_raw = |i: usize| row.as_ref().and_then(|r| r.get(i)); - - let (name, type_, elem_oid, rngsubtype, basetype, schema, relid) = { - let name = - String::from_sql_nullable(&Type::NAME, get_raw(0)).map_err(error::conversion)?; - let type_ = - i8::from_sql_nullable(&Type::CHAR, get_raw(1)).map_err(error::conversion)?; - let elem_oid = - Oid::from_sql_nullable(&Type::OID, get_raw(2)).map_err(error::conversion)?; - let rngsubtype = Option::::from_sql_nullable(&Type::OID, get_raw(3)) - .map_err(error::conversion)?; - let basetype = - Oid::from_sql_nullable(&Type::OID, get_raw(4)).map_err(error::conversion)?; - let schema = - String::from_sql_nullable(&Type::NAME, get_raw(5)).map_err(error::conversion)?; - let relid = - Oid::from_sql_nullable(&Type::OID, get_raw(6)).map_err(error::conversion)?; - (name, type_, elem_oid, rngsubtype, basetype, schema, relid) - }; - - let kind = if type_ == b'e' as i8 { - Kind::Enum(self.read_enum_variants(oid)?) - } else if type_ == b'p' as i8 { - Kind::Pseudo - } else if basetype != 0 { - Kind::Domain(self.get_type(basetype)?) - } else if elem_oid != 0 { - Kind::Array(self.get_type(elem_oid)?) - } else if relid != 0 { - Kind::Composite(self.read_composite_fields(relid)?) - } else { - match rngsubtype { - Some(oid) => Kind::Range(self.get_type(oid)?), - None => Kind::Simple, - } - }; - - Ok(Type::_new(name, oid, kind, schema)) - } - - fn setup_typeinfo_enum_query(&mut self) -> Result<()> { - if self.has_typeinfo_enum_query { - return Ok(()); - } - - match self.raw_prepare( - TYPEINFO_ENUM_QUERY, - "SELECT enumlabel \ - FROM pg_catalog.pg_enum \ - WHERE enumtypid = $1 \ - ORDER BY enumsortorder", - &[], - ) { - Ok(..) => {} - // Postgres 9.0 doesn't have enumsortorder - Err(ref e) if e.code() == Some(&SqlState::UNDEFINED_COLUMN) => { - self.raw_prepare( - TYPEINFO_ENUM_QUERY, - "SELECT enumlabel \ - FROM pg_catalog.pg_enum \ - WHERE enumtypid = $1 \ - ORDER BY oid", - &[], - )?; - } - Err(e) => return Err(e), - } - - self.has_typeinfo_enum_query = true; - Ok(()) - } - - fn read_enum_variants(&mut self, oid: Oid) -> Result> { - self.setup_typeinfo_enum_query()?; - self.raw_execute(TYPEINFO_ENUM_QUERY, "", 0, &[Type::OID], &[&oid])?; - let mut rows = vec![]; - self.read_rows(|row| rows.push(row))?; - - let mut variants = vec![]; - for row in rows { - variants.push( - String::from_sql_nullable(&Type::NAME, row.get(0)).map_err(error::conversion)?, - ); - } - - Ok(variants) - } - - fn setup_typeinfo_composite_query(&mut self) -> Result<()> { - if self.has_typeinfo_composite_query { - return Ok(()); - } - - self.raw_prepare( - TYPEINFO_COMPOSITE_QUERY, - "SELECT attname, atttypid \ - FROM pg_catalog.pg_attribute \ - WHERE attrelid = $1 \ - AND NOT attisdropped \ - AND attnum > 0 \ - ORDER BY attnum", - &[], - )?; - - self.has_typeinfo_composite_query = true; - Ok(()) - } - - fn read_composite_fields(&mut self, relid: Oid) -> Result> { - self.setup_typeinfo_composite_query()?; - self.raw_execute(TYPEINFO_COMPOSITE_QUERY, "", 0, &[Type::OID], &[&relid])?; - let mut rows = vec![]; - self.read_rows(|row| rows.push(row))?; - - let mut fields = vec![]; - for row in rows { - let (name, type_) = { - let name = String::from_sql_nullable(&Type::NAME, row.get(0)) - .map_err(error::conversion)?; - let type_ = - Oid::from_sql_nullable(&Type::OID, row.get(1)).map_err(error::conversion)?; - (name, type_) - }; - let type_ = self.get_type(type_)?; - fields.push(Field::new(name, type_)); - } - - Ok(fields) - } - - fn is_desynchronized(&self) -> bool { - self.desynchronized - } - - #[allow(needless_return)] - fn wait_for_ready(&mut self) -> Result<()> { - match self.read_message()? { - backend::Message::ReadyForQuery(_) => Ok(()), - _ => bad_response!(self), - } - } - - fn simple_query_(&mut self, query: &str) -> Result> { - check_desync!(self); - debug!("executing query: {}", query); - self.stream - .write_message(|buf| frontend::query(query, buf))?; - self.stream.flush()?; - - let mut result = vec![]; - let mut rows = vec![]; - let mut columns = None; - - loop { - match self.read_message()? { - backend::Message::ReadyForQuery(_) => break, - backend::Message::DataRow(body) => { - rows.push(RowData::new(body)?); - } - backend::Message::CopyInResponse(_) => { - self.stream.write_message(|buf| { - frontend::copy_fail("COPY queries cannot be directly executed", buf) - })?; - self.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?; - self.stream.flush()?; - } - backend::Message::ErrorResponse(body) => { - self.wait_for_ready()?; - return Err(err(&mut body.fields())); - } - backend::Message::RowDescription(body) => { - columns = Some(self.parse_cols(Some(body))?); - } - backend::Message::CommandComplete(_) => { - if let Some(cols) = columns.take() { - result.push(TextRows::new(cols, mem::replace(&mut rows, Vec::new()))); - } - } - _ => bad_response!(self), - } - } - Ok(result) - } - - fn quick_query(&mut self, query: &str) -> Result>>> { - check_desync!(self); - debug!("executing query: {}", query); - self.stream - .write_message(|buf| frontend::query(query, buf))?; - self.stream.flush()?; - - let mut result = vec![]; - loop { - match self.read_message()? { - backend::Message::ReadyForQuery(_) => break, - backend::Message::DataRow(body) => { - let row = body - .ranges() - .map(|r| r.map(|r| String::from_utf8_lossy(&body.buffer()[r]).into_owned())) - .collect()?; - result.push(row); - } - backend::Message::CopyInResponse(_) => { - self.stream.write_message(|buf| { - frontend::copy_fail("COPY queries cannot be directly executed", buf) - })?; - self.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?; - self.stream.flush()?; - } - backend::Message::ErrorResponse(body) => { - self.wait_for_ready()?; - return Err(err(&mut body.fields())); - } - _ => {} - } - } - Ok(result) - } - - fn finish_inner(&mut self) -> Result<()> { - check_desync!(self); - self.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::terminate(buf)))?; - self.stream.flush()?; - Ok(()) - } -} - -fn _ensure_send() { - fn _is_send() {} - _is_send::(); -} - -/// A connection to a Postgres database. -pub struct Connection(RefCell); - -impl fmt::Debug for Connection { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let conn = self.0.borrow(); - fmt.debug_struct("Connection") - .field("stream", &conn.stream.get_ref()) - .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 { - /// Creates a new connection to a Postgres database. - /// - /// Most applications can use a URL string in the normal format: - /// - /// ```notrust - /// postgresql://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. - /// - /// To connect to the server via Unix sockets, `host` should be set to the - /// absolute path of the directory containing the socket file. Since `/` is - /// a reserved character in URLs, the path should be URL encoded. If the - /// path contains non-UTF 8 characters, a `ConnectParams` struct should be - /// created manually and passed in. Note that Postgres does not support TLS - /// over Unix sockets. - /// - /// # Examples - /// - /// To connect over TCP: - /// - /// ```rust,no_run - /// use postgres::{Connection, TlsMode}; - /// - /// let url = "postgresql://postgres:hunter2@localhost:5433:2994/foodb"; - /// let conn = Connection::connect(url, TlsMode::None).unwrap(); - /// ``` - /// - /// To connect over a Unix socket located in `/run/postgres`: - /// - /// ```rust,no_run - /// use postgres::{Connection, TlsMode}; - /// - /// let url = "postgresql://postgres@%2Frun%2Fpostgres"; - /// let conn = Connection::connect(url, TlsMode::None).unwrap(); - /// ``` - /// - /// To connect with a manually constructed `ConnectParams`: - /// - /// ```rust,no_run - /// use postgres::{Connection, TlsMode}; - /// use postgres::params::{ConnectParams, Host}; - /// # use std::path::PathBuf; - /// - /// # #[cfg(unix)] - /// # fn f() { - /// # let some_crazy_path = PathBuf::new(); - /// let params = ConnectParams::builder() - /// .user("postgres", None) - /// .build(Host::Unix(some_crazy_path)); - /// let conn = Connection::connect(params, TlsMode::None).unwrap(); - /// # } - /// ``` - pub fn connect(params: T, tls: TlsMode) -> Result - where - T: IntoConnectParams, - { - InnerConnection::connect(params, tls).map(|conn| Connection(RefCell::new(conn))) - } - - /// Executes a statement, returning the number of rows modified. - /// - /// A statement may contain parameters, specified by `$n` where `n` is the - /// index of the parameter in the list provided, 1-indexed. - /// - /// If the statement does not modify any rows (e.g. SELECT), 0 is returned. - /// - /// If the same statement will be repeatedly executed (perhaps with - /// different query parameters), consider using the `prepare` and - /// `prepare_cached` methods. - /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number - /// expected. - /// - /// # Example - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode}; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// # let bar = 1i32; - /// # let baz = true; - /// let rows_updated = conn.execute("UPDATE foo SET bar = $1 WHERE baz = $2", &[&bar, &baz]) - /// .unwrap(); - /// println!("{} rows updated", rows_updated); - /// ``` - pub fn execute(&self, query: &str, params: &[&ToSql]) -> Result { - let (param_types, columns) = self.0.borrow_mut().raw_prepare("", query, &[])?; - let info = Arc::new(StatementInfo { - name: String::new(), - param_types: param_types, - columns: columns, - }); - let stmt = Statement::new(self, info, Cell::new(0), true); - stmt.execute(params) - } - - /// Executes a statement, returning the resulting rows. - /// - /// A statement may contain parameters, specified by `$n` where `n` is the - /// index of the parameter in the list provided, 1-indexed. - /// - /// If the same statement will be repeatedly executed (perhaps with - /// different query parameters), consider using the `prepare` and - /// `prepare_cached` methods. - /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number - /// expected. - /// - /// # Example - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode}; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// # let baz = true; - /// for row in &conn.query("SELECT foo FROM bar WHERE baz = $1", &[&baz]).unwrap() { - /// let foo: i32 = row.get("foo"); - /// println!("foo: {}", foo); - /// } - /// ``` - pub fn query(&self, query: &str, params: &[&ToSql]) -> Result { - let (param_types, columns) = self.0.borrow_mut().raw_prepare("", query, &[])?; - let info = Arc::new(StatementInfo { - name: String::new(), - param_types: param_types, - columns: columns, - }); - let stmt = Statement::new(self, info, Cell::new(0), true); - stmt.into_query(params) - } - - /// Begins a new transaction. - /// - /// Returns a `Transaction` object which should be used instead of - /// the connection for the duration of the transaction. The transaction - /// is active until the `Transaction` object falls out of scope. - /// - /// # Note - /// A transaction will roll back by default. The `set_commit`, - /// `set_rollback`, and `commit` methods alter this behavior. - /// - /// # Panics - /// - /// Panics if a transaction is already active. - /// - /// # Example - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode}; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// let trans = conn.transaction().unwrap(); - /// trans.execute("UPDATE foo SET bar = 10", &[]).unwrap(); - /// // ... - /// - /// trans.commit().unwrap(); - /// ``` - pub fn transaction<'a>(&'a self) -> Result> { - self.transaction_with(&transaction::Config::new()) - } - - /// Begins a new transaction with the specified configuration. - pub fn transaction_with<'a>(&'a self, config: &transaction::Config) -> Result> { - let mut conn = self.0.borrow_mut(); - check_desync!(conn); - assert!( - conn.trans_depth == 0, - "`transaction` must be called on the active transaction" - ); - let mut query = "BEGIN".to_owned(); - config.build_command(&mut query); - conn.quick_query(&query)?; - conn.trans_depth += 1; - Ok(Transaction::new(self, 1)) - } - - /// Creates a new prepared statement. - /// - /// If the same statement will be executed repeatedly, explicitly preparing - /// it can improve performance. - /// - /// The statement is associated with the connection that created it and may - /// not outlive that connection. - /// - /// # Example - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode}; - /// # let x = 10i32; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// # let (a, b) = (0i32, 1i32); - /// # let updates = vec![(&a, &b)]; - /// let stmt = conn.prepare("UPDATE foo SET bar = $1 WHERE baz = $2").unwrap(); - /// for (bar, baz) in updates { - /// stmt.execute(&[bar, baz]).unwrap(); - /// } - /// ``` - pub fn prepare<'a>(&'a self, query: &str) -> Result> { - self.prepare_typed(query, &[]) - } - - /// Like `prepare`, but allows for the types of query parameters to be explicitly specified. - /// - /// Postgres will normally infer the types of parameters, but this function offers more control - /// of that behavior. `None` will cause Postgres to infer the type. The list of types can be - /// shorter than the number of parameters in the query; it will act as if padded out with `None` - /// values. - /// - /// # Example - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode}; - /// # use postgres::types::Type; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// // $1 would normally be assigned the type INT4, but we can override that to INT8 - /// let stmt = conn.prepare_typed("SELECT $1::INT4", &[Some(Type::INT8)]).unwrap(); - /// assert_eq!(stmt.param_types()[0], Type::INT8); - /// ``` - pub fn prepare_typed<'a>( - &'a self, - query: &str, - types: &[Option], - ) -> Result> { - self.0.borrow_mut().prepare_typed(query, types, self) - } - - /// Creates a cached prepared statement. - /// - /// Like `prepare`, except that the statement is only prepared once over - /// the lifetime of the connection and then cached. If the same statement - /// is going to be prepared frequently, caching it can improve performance - /// by reducing the number of round trips to the Postgres backend. - /// - /// # Example - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode}; - /// # let x = 10i32; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// # let (a, b) = (0i32, 1i32); - /// # let updates = vec![(&a, &b)]; - /// let stmt = conn.prepare_cached("UPDATE foo SET bar = $1 WHERE baz = $2").unwrap(); - /// for (bar, baz) in updates { - /// stmt.execute(&[bar, baz]).unwrap(); - /// } - /// ``` - pub fn prepare_cached<'a>(&'a self, query: &str) -> Result> { - self.0.borrow_mut().prepare_cached(query, self) - } - - /// Returns the isolation level which will be used for future transactions. - /// - /// This is a simple wrapper around `SHOW TRANSACTION ISOLATION LEVEL`. - pub fn transaction_isolation(&self) -> Result { - let mut conn = self.0.borrow_mut(); - check_desync!(conn); - let result = conn.quick_query("SHOW TRANSACTION ISOLATION LEVEL")?; - IsolationLevel::new(result[0][0].as_ref().unwrap()) - } - - /// Sets the configuration that will be used for future transactions. - pub fn set_transaction_config(&self, config: &transaction::Config) -> Result<()> { - let mut command = "SET SESSION CHARACTERISTICS AS TRANSACTION".to_owned(); - config.build_command(&mut command); - self.simple_query(&command).map(|_| ()) - } - - /// Execute a sequence of SQL statements. - /// - /// Statements should be separated by `;` characters. If an error occurs, - /// execution of the sequence will stop at that point. This is intended for - /// execution of batches of non-dynamic statements - for example, creation - /// of a schema for a fresh database. - /// - /// # Warning - /// - /// Prepared statements should be used for any SQL statement which contains - /// user-specified data, as it provides functionality to safely embed that - /// data in the statement. Do not form statements via string concatenation - /// and feed them into this method. - /// - /// # Example - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode, Result}; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// conn.batch_execute(" - /// CREATE TABLE person ( - /// id SERIAL PRIMARY KEY, - /// name NOT NULL - /// ); - /// - /// CREATE TABLE purchase ( - /// id SERIAL PRIMARY KEY, - /// person INT NOT NULL REFERENCES person (id), - /// time TIMESTAMPTZ NOT NULL, - /// ); - /// - /// CREATE INDEX ON purchase (time); - /// ").unwrap(); - /// ``` - #[deprecated(since = "0.15.3", note = "please use `simple_query` instead")] - pub fn batch_execute(&self, query: &str) -> Result<()> { - self.0.borrow_mut().quick_query(query).map(|_| ()) - } - - /// Send a simple, non-prepared query - /// - /// Executes a query without making a prepared statement. All result columns - /// are returned in a UTF-8 text format rather than compact binary - /// representations. This can be useful when communicating with services - /// like _pgbouncer_ which speak "basic" postgres but don't support prepared - /// statements. - /// - /// Because rust-postgres' query parameter substitution relies on prepared - /// statements, it's not possible to pass a separate parameters list with - /// this API. - /// - /// In general, the `query` API should be preferred whenever possible. - /// - /// # Example - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode}; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// for response in &conn.simple_query("SELECT foo FROM bar WHERE baz = 'quux'").unwrap() { - /// for row in response { - /// let foo: &str = row.get("foo"); - /// println!("foo: {}", foo); - /// } - /// } - /// ``` - pub fn simple_query(&self, query: &str) -> Result> { - self.0.borrow_mut().simple_query_(query) - } - - /// Returns a structure providing access to asynchronous notifications. - /// - /// Use the `LISTEN` command to register this connection for notifications. - pub fn notifications<'a>(&'a self) -> Notifications<'a> { - Notifications::new(self) - } - - /// 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) -> CancelData { - self.0.borrow().cancel_data - } - - /// Returns the value of the specified Postgres backend parameter, such as - /// `timezone` or `server_version`. - pub fn parameter(&self, param: &str) -> Option { - self.0.borrow().parameters.get(param).cloned() - } - - /// Sets the notice handler for the connection, returning the old handler. - pub fn set_notice_handler(&self, handler: Box) -> Box { - self.0.borrow_mut().set_notice_handler(handler) - } - - /// 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 { - self.0.borrow().is_desynchronized() - } - - /// Determines if the `Connection` is currently "active", that is, if there - /// are no active transactions. - /// - /// The `transaction` method can only be called on the active `Connection` - /// or `Transaction`. - pub fn is_active(&self) -> bool { - self.0.borrow().trans_depth == 0 - } - - /// Consumes the connection, closing it. - /// - /// Functionally equivalent to the `Drop` implementation for `Connection` - /// except that it returns any error encountered to the caller. - pub fn finish(self) -> Result<()> { - let mut conn = self.0.borrow_mut(); - conn.finished = true; - conn.finish_inner() - } -} - -/// A trait allowing abstraction over connections and transactions -pub trait GenericConnection { - /// Like `Connection::execute`. - fn execute(&self, query: &str, params: &[&ToSql]) -> Result; - - /// Like `Connection::query`. - fn query<'a>(&'a self, query: &str, params: &[&ToSql]) -> Result; - - /// Like `Connection::prepare`. - fn prepare<'a>(&'a self, query: &str) -> Result>; - - /// Like `Connection::prepare_cached`. - fn prepare_cached<'a>(&'a self, query: &str) -> Result>; - - /// Like `Connection::transaction`. - fn transaction<'a>(&'a self) -> Result>; - - /// Like `Connection::batch_execute`. - #[deprecated(since = "0.15.3", note = "please use `simple_query` instead")] - fn batch_execute(&self, query: &str) -> Result<()>; - - /// Like `Connection::is_active`. - fn is_active(&self) -> bool; - - /// Like `Connection::simple_query`. - fn simple_query(&self, query: &str) -> Result>; -} - -impl GenericConnection for Connection { - fn execute(&self, query: &str, params: &[&ToSql]) -> Result { - self.execute(query, params) - } - - fn query<'a>(&'a self, query: &str, params: &[&ToSql]) -> Result { - self.query(query, params) - } - - fn prepare<'a>(&'a self, query: &str) -> Result> { - self.prepare(query) - } - - fn prepare_cached<'a>(&'a self, query: &str) -> Result> { - self.prepare_cached(query) - } - - fn transaction<'a>(&'a self) -> Result> { - self.transaction() - } - - fn batch_execute(&self, query: &str) -> Result<()> { - self.simple_query(query).map(|_| ()) - } - - fn is_active(&self) -> bool { - self.is_active() - } - - fn simple_query(&self, query: &str) -> Result> { - self.simple_query(query) - } -} - -impl<'a> GenericConnection for Transaction<'a> { - fn execute(&self, query: &str, params: &[&ToSql]) -> Result { - self.execute(query, params) - } - - fn query<'b>(&'b self, query: &str, params: &[&ToSql]) -> Result { - self.query(query, params) - } - - fn prepare<'b>(&'b self, query: &str) -> Result> { - self.prepare(query) - } - - fn prepare_cached<'b>(&'b self, query: &str) -> Result> { - self.prepare_cached(query) - } - - fn transaction<'b>(&'b self) -> Result> { - self.transaction() - } - - fn batch_execute(&self, query: &str) -> Result<()> { - self.simple_query(query).map(|_| ()) - } - - fn simple_query(&self, query: &str) -> Result> { - self.simple_query(query) - } - - fn is_active(&self) -> bool { - self.is_active() - } -} - -fn err(fields: &mut ErrorFields) -> Error { - match DbError::new(fields) { - Ok(err) => error::db(err), - Err(err) => err.into(), - } -} diff --git a/postgres/src/macros.rs b/postgres/src/macros.rs deleted file mode 100644 index 939a0344..00000000 --- a/postgres/src/macros.rs +++ /dev/null @@ -1,69 +0,0 @@ -macro_rules! try_desync { - ($s:expr, $e:expr) => ( - match $e { - Ok(ok) => ok, - Err(err) => { - $s.desynchronized = true; - return Err(::std::convert::From::from(err)); - } - } - ) -} - -macro_rules! check_desync { - ($e:expr) => ({ - if $e.is_desynchronized() { - return Err(::desynchronized().into()); - } - }) -} - -macro_rules! bad_response { - ($s:expr) => {{ - debug!("Bad response at {}:{}", file!(), line!()); - $s.desynchronized = true; - return Err(::bad_response().into()); - }}; -} - -#[cfg(feature = "no-logging")] -macro_rules! debug { - ($($t:tt)*) => {}; -} - -#[cfg(feature = "no-logging")] -macro_rules! info { - ($($t:tt)*) => {}; -} - -/// Generates a simple implementation of `ToSql::accepts` which accepts the -/// types passed to it. -#[macro_export] -macro_rules! accepts { - ($($expected:pat),+) => ( - fn accepts(ty: &$crate::types::Type) -> bool { - match *ty { - $($expected)|+ => true, - _ => false - } - } - ) -} - -/// Generates an implementation of `ToSql::to_sql_checked`. -/// -/// All `ToSql` implementations should use this macro. -#[macro_export] -macro_rules! to_sql_checked { - () => { - fn to_sql_checked(&self, - ty: &$crate::types::Type, - out: &mut ::std::vec::Vec) - -> ::std::result::Result<$crate::types::IsNull, - Box<::std::error::Error + - ::std::marker::Sync + - ::std::marker::Send>> { - $crate::types::__to_sql_checked(self, ty, out) - } - } -} diff --git a/postgres/src/notification.rs b/postgres/src/notification.rs deleted file mode 100644 index 73eb9862..00000000 --- a/postgres/src/notification.rs +++ /dev/null @@ -1,203 +0,0 @@ -//! Asynchronous notifications. - -use error::DbError; -use fallible_iterator::{FallibleIterator, IntoFallibleIterator}; -use postgres_protocol::message::backend::{self, ErrorFields}; -use std::fmt; -use std::time::Duration; - -#[doc(inline)] -use postgres_shared; -pub use postgres_shared::Notification; - -use error::Error; -use {desynchronized, Connection, Result}; - -/// Notifications from the Postgres backend. -pub struct Notifications<'conn> { - conn: &'conn Connection, -} - -impl<'a> fmt::Debug for Notifications<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("Notifications") - .field("pending", &self.len()) - .finish() - } -} - -impl<'conn> Notifications<'conn> { - pub(crate) fn new(conn: &'conn Connection) -> Notifications<'conn> { - Notifications { conn: conn } - } - - /// Returns the number of pending notifications. - pub fn len(&self) -> usize { - self.conn.0.borrow().notifications.len() - } - - /// Determines if there are any pending notifications. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns a fallible iterator over pending notifications. - /// - /// # Note - /// - /// This iterator may start returning `Some` after previously returning - /// `None` if more notifications are received. - pub fn iter<'a>(&'a self) -> Iter<'a> { - Iter { conn: self.conn } - } - - /// Returns a fallible iterator over notifications that blocks until one is - /// received if none are pending. - /// - /// The iterator will never return `None`. - pub fn blocking_iter<'a>(&'a self) -> BlockingIter<'a> { - BlockingIter { conn: self.conn } - } - - /// Returns a fallible iterator over notifications that blocks for a limited - /// time waiting to receive one if none are pending. - /// - /// # Note - /// - /// This iterator may start returning `Some` after previously returning - /// `None` if more notifications are received. - pub fn timeout_iter<'a>(&'a self, timeout: Duration) -> TimeoutIter<'a> { - TimeoutIter { - conn: self.conn, - timeout: timeout, - } - } -} - -impl<'a, 'conn> IntoFallibleIterator for &'a Notifications<'conn> { - type Item = Notification; - type Error = Error; - type IntoIter = Iter<'a>; - - fn into_fallible_iterator(self) -> Iter<'a> { - self.iter() - } -} - -/// A fallible iterator over pending notifications. -pub struct Iter<'a> { - conn: &'a Connection, -} - -impl<'a> FallibleIterator for Iter<'a> { - type Item = Notification; - type Error = Error; - - fn next(&mut self) -> Result> { - let mut conn = self.conn.0.borrow_mut(); - - if let Some(notification) = conn.notifications.pop_front() { - return Ok(Some(notification)); - } - - if conn.is_desynchronized() { - return Err(desynchronized().into()); - } - - match conn.read_message_with_notification_nonblocking() { - Ok(Some(backend::Message::NotificationResponse(body))) => Ok(Some(Notification { - process_id: body.process_id(), - channel: body.channel()?.to_owned(), - payload: body.message()?.to_owned(), - })), - Ok(Some(backend::Message::ErrorResponse(body))) => Err(err(&mut body.fields())), - Ok(None) => Ok(None), - Err(err) => Err(err.into()), - _ => unreachable!(), - } - } - - fn size_hint(&self) -> (usize, Option) { - (self.conn.0.borrow().notifications.len(), None) - } -} - -/// An iterator over notifications which will block if none are pending. -pub struct BlockingIter<'a> { - conn: &'a Connection, -} - -impl<'a> FallibleIterator for BlockingIter<'a> { - type Item = Notification; - type Error = Error; - - fn next(&mut self) -> Result> { - let mut conn = self.conn.0.borrow_mut(); - - if let Some(notification) = conn.notifications.pop_front() { - return Ok(Some(notification)); - } - - if conn.is_desynchronized() { - return Err(desynchronized().into()); - } - - match conn.read_message_with_notification() { - Ok(backend::Message::NotificationResponse(body)) => Ok(Some(Notification { - process_id: body.process_id(), - channel: body.channel()?.to_owned(), - payload: body.message()?.to_owned(), - })), - Ok(backend::Message::ErrorResponse(body)) => Err(err(&mut body.fields())), - Err(err) => Err(err.into()), - _ => unreachable!(), - } - } -} - -/// An iterator over notifications which will block for a period of time if -/// none are pending. -pub struct TimeoutIter<'a> { - conn: &'a Connection, - timeout: Duration, -} - -impl<'a> FallibleIterator for TimeoutIter<'a> { - type Item = Notification; - type Error = Error; - - fn next(&mut self) -> Result> { - let mut conn = self.conn.0.borrow_mut(); - - if let Some(notification) = conn.notifications.pop_front() { - return Ok(Some(notification)); - } - - if conn.is_desynchronized() { - return Err(desynchronized().into()); - } - - match conn.read_message_with_notification_timeout(self.timeout) { - Ok(Some(backend::Message::NotificationResponse(body))) => Ok(Some(Notification { - process_id: body.process_id(), - channel: body.channel()?.to_owned(), - payload: body.message()?.to_owned(), - })), - Ok(Some(backend::Message::ErrorResponse(body))) => Err(err(&mut body.fields())), - Ok(None) => Ok(None), - Err(err) => Err(err.into()), - _ => unreachable!(), - } - } - - fn size_hint(&self) -> (usize, Option) { - (self.conn.0.borrow().notifications.len(), None) - } -} - -fn err(fields: &mut ErrorFields) -> Error { - match DbError::new(fields) { - Ok(err) => postgres_shared::error::db(err), - Err(err) => err.into(), - } -} diff --git a/postgres/src/params.rs b/postgres/src/params.rs deleted file mode 100644 index 29e51228..00000000 --- a/postgres/src/params.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Connection parameters - -pub use postgres_shared::params::{Builder, ConnectParams, Host, IntoConnectParams, User}; diff --git a/postgres/src/priv_io.rs b/postgres/src/priv_io.rs deleted file mode 100644 index c0e3a131..00000000 --- a/postgres/src/priv_io.rs +++ /dev/null @@ -1,259 +0,0 @@ -use bytes::{BufMut, BytesMut}; -use postgres_protocol::message::backend; -use postgres_protocol::message::frontend; -use socket2::{Domain, SockAddr, Socket, Type}; -use std::io::{self, BufWriter, Read, Write}; -use std::net::{SocketAddr, ToSocketAddrs}; -#[cfg(unix)] -use std::os::unix::io::{AsRawFd, RawFd}; -#[cfg(windows)] -use std::os::windows::io::{AsRawSocket, RawSocket}; -use std::result; -use std::time::Duration; - -use error; -use params::{ConnectParams, Host}; -use tls::TlsStream; -use {Result, TlsMode}; - -const INITIAL_CAPACITY: usize = 8 * 1024; - -pub struct MessageStream { - stream: BufWriter>, - in_buf: BytesMut, - out_buf: Vec, -} - -impl MessageStream { - pub fn new(stream: Box) -> MessageStream { - MessageStream { - stream: BufWriter::new(stream), - in_buf: BytesMut::with_capacity(INITIAL_CAPACITY), - out_buf: vec![], - } - } - - pub fn get_ref(&self) -> &TlsStream { - &**self.stream.get_ref() - } - - pub fn write_message(&mut self, f: F) -> result::Result<(), E> - where - F: FnOnce(&mut Vec) -> result::Result<(), E>, - E: From, - { - self.out_buf.clear(); - f(&mut self.out_buf)?; - self.stream.write_all(&self.out_buf).map_err(From::from) - } - - pub fn read_message(&mut self) -> io::Result { - loop { - match backend::Message::parse(&mut self.in_buf) { - Ok(Some(message)) => return Ok(message), - Ok(None) => self.read_in()?, - Err(e) => return Err(e), - } - } - } - - fn read_in(&mut self) -> io::Result<()> { - self.in_buf.reserve(1); - match self - .stream - .get_mut() - .read(unsafe { self.in_buf.bytes_mut() }) - { - Ok(0) => Err(io::Error::new( - io::ErrorKind::UnexpectedEof, - "unexpected EOF", - )), - Ok(n) => { - unsafe { self.in_buf.advance_mut(n) }; - Ok(()) - } - Err(e) => Err(e), - } - } - - pub fn read_message_timeout( - &mut self, - timeout: Duration, - ) -> io::Result> { - if self.in_buf.is_empty() { - self.set_read_timeout(Some(timeout))?; - let r = self.read_in(); - self.set_read_timeout(None)?; - - match r { - Ok(()) => {} - Err(ref e) - if e.kind() == io::ErrorKind::WouldBlock - || e.kind() == io::ErrorKind::TimedOut => - { - return Ok(None) - } - Err(e) => return Err(e), - } - } - - self.read_message().map(Some) - } - - pub fn read_message_nonblocking(&mut self) -> io::Result> { - if self.in_buf.is_empty() { - self.set_nonblocking(true)?; - let r = self.read_in(); - self.set_nonblocking(false)?; - - match r { - Ok(()) => {} - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(None), - Err(e) => return Err(e), - } - } - - self.read_message().map(Some) - } - - pub fn flush(&mut self) -> io::Result<()> { - self.stream.flush() - } - - fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { - self.stream.get_ref().get_ref().0.set_read_timeout(timeout) - } - - fn set_nonblocking(&self, nonblock: bool) -> io::Result<()> { - self.stream.get_ref().get_ref().0.set_nonblocking(nonblock) - } -} - -/// A connection to the Postgres server. -/// -/// It implements `Read`, `Write` and `TlsStream`, as well as `AsRawFd` on -/// Unix platforms and `AsRawSocket` on Windows platforms. -#[derive(Debug)] -pub struct Stream(Socket); - -impl Read for Stream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } -} - -impl Write for Stream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } -} - -impl TlsStream for Stream { - fn get_ref(&self) -> &Stream { - self - } - - fn get_mut(&mut self) -> &mut Stream { - self - } -} - -#[cfg(unix)] -impl AsRawFd for Stream { - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -#[cfg(windows)] -impl AsRawSocket for Stream { - fn as_raw_socket(&self) -> RawSocket { - self.0.as_raw_socket() - } -} - -fn open_socket(params: &ConnectParams) -> Result { - let port = params.port(); - match *params.host() { - Host::Tcp(ref host) => { - let mut error = None; - for addr in (&**host, port).to_socket_addrs()? { - let domain = match addr { - SocketAddr::V4(_) => Domain::ipv4(), - SocketAddr::V6(_) => Domain::ipv6(), - }; - let socket = Socket::new(domain, Type::stream(), None)?; - if let Some(keepalive) = params.keepalive() { - socket.set_keepalive(Some(keepalive))?; - } - let addr = SockAddr::from(addr); - let r = match params.connect_timeout() { - Some(timeout) => socket.connect_timeout(&addr, timeout), - None => socket.connect(&addr), - }; - match r { - Ok(()) => return Ok(socket), - Err(e) => error = Some(e), - } - } - - Err(error - .unwrap_or_else(|| { - io::Error::new( - io::ErrorKind::InvalidInput, - "could not resolve any addresses", - ) - }).into()) - } - #[cfg(unix)] - Host::Unix(ref path) => { - let path = path.join(&format!(".s.PGSQL.{}", port)); - let socket = Socket::new(Domain::unix(), Type::stream(), None)?; - let addr = SockAddr::unix(path)?; - socket.connect(&addr)?; - Ok(socket) - } - #[cfg(not(unix))] - Host::Unix(..) => Err(io::Error::new( - io::ErrorKind::InvalidInput, - "unix sockets are not supported on this system", - ).into()), - } -} - -pub fn initialize_stream(params: &ConnectParams, tls: TlsMode) -> Result> { - let mut socket = Stream(open_socket(params)?); - - let (tls_required, handshaker) = match tls { - TlsMode::None => return Ok(Box::new(socket)), - TlsMode::Prefer(handshaker) => (false, handshaker), - TlsMode::Require(handshaker) => (true, handshaker), - }; - - let mut buf = vec![]; - frontend::ssl_request(&mut buf); - socket.write_all(&buf)?; - socket.flush()?; - - let mut b = [0; 1]; - socket.read_exact(&mut b)?; - if b[0] == b'N' { - if tls_required { - return Err(error::tls("the server does not support TLS".into())); - } else { - return Ok(Box::new(socket)); - } - } - - let host = match *params.host() { - Host::Tcp(ref host) => host, - // Postgres doesn't support TLS over unix sockets - Host::Unix(_) => return Err(::bad_response().into()), - }; - - handshaker.tls_handshake(host, socket).map_err(error::tls) -} diff --git a/postgres/src/rows.rs b/postgres/src/rows.rs deleted file mode 100644 index 25b006fd..00000000 --- a/postgres/src/rows.rs +++ /dev/null @@ -1,342 +0,0 @@ -//! Query result rows. - -use fallible_iterator::FallibleIterator; -use postgres_protocol::message::frontend; -use postgres_shared::rows::RowData; -use std::collections::VecDeque; -use std::fmt; -use std::io; -use std::ops::Deref; -use std::slice; -use std::sync::Arc; - -#[doc(inline)] -pub use postgres_shared::rows::RowIndex; - -use error; -use stmt::{Column, Statement}; -use transaction::Transaction; -use types::{FromSql, WrongType}; -use {Error, Result, StatementInfo}; - -enum MaybeOwned<'a, T: 'a> { - Borrowed(&'a T), - Owned(T), -} - -impl<'a, T> Deref for MaybeOwned<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - match *self { - MaybeOwned::Borrowed(s) => s, - MaybeOwned::Owned(ref s) => s, - } - } -} - -/// The resulting rows of a query. -pub struct Rows { - stmt_info: Arc, - data: Vec, -} - -impl fmt::Debug for Rows { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("Rows") - .field("columns", &self.columns()) - .field("rows", &self.data.len()) - .finish() - } -} - -impl Rows { - pub(crate) fn new(stmt: &Statement, data: Vec) -> Rows { - Rows { - stmt_info: stmt.info().clone(), - data: data, - } - } - - /// Returns a slice describing the columns of the `Rows`. - pub fn columns(&self) -> &[Column] { - &self.stmt_info.columns[..] - } - - /// Returns the number of rows present. - pub fn len(&self) -> usize { - self.data.len() - } - - /// Determines if there are any rows present. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns a specific `Row`. - /// - /// # Panics - /// - /// Panics if `idx` is out of bounds. - pub fn get<'a>(&'a self, idx: usize) -> Row<'a> { - Row { - stmt_info: &self.stmt_info, - data: MaybeOwned::Borrowed(&self.data[idx]), - } - } - - /// Returns an iterator over the `Row`s. - pub fn iter<'a>(&'a self) -> Iter<'a> { - Iter { - stmt_info: &self.stmt_info, - iter: self.data.iter(), - } - } -} - -impl<'a> IntoIterator for &'a Rows { - type Item = Row<'a>; - type IntoIter = Iter<'a>; - - fn into_iter(self) -> Iter<'a> { - self.iter() - } -} - -/// An iterator over `Row`s. -pub struct Iter<'a> { - stmt_info: &'a StatementInfo, - iter: slice::Iter<'a, RowData>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = Row<'a>; - - fn next(&mut self) -> Option> { - self.iter.next().map(|row| Row { - stmt_info: self.stmt_info, - data: MaybeOwned::Borrowed(row), - }) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl<'a> DoubleEndedIterator for Iter<'a> { - fn next_back(&mut self) -> Option> { - self.iter.next_back().map(|row| Row { - stmt_info: self.stmt_info, - data: MaybeOwned::Borrowed(row), - }) - } -} - -impl<'a> ExactSizeIterator for Iter<'a> {} - -/// A single result row of a query. -pub struct Row<'a> { - stmt_info: &'a StatementInfo, - data: MaybeOwned<'a, RowData>, -} - -impl<'a> fmt::Debug for Row<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("Row") - .field("statement", self.stmt_info) - .finish() - } -} - -impl<'a> Row<'a> { - /// Returns the number of values in the row. - pub fn len(&self) -> usize { - self.data.len() - } - - /// Determines if there are any values in the row. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns a slice describing the columns of the `Row`. - pub fn columns(&self) -> &[Column] { - &self.stmt_info.columns[..] - } - - /// 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 0-indexed. - /// - /// # Panics - /// - /// Panics 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::{Connection, TlsMode}; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// let stmt = conn.prepare("SELECT foo, bar from BAZ").unwrap(); - /// for row in &stmt.query(&[]).unwrap() { - /// let foo: i32 = row.get(0); - /// let bar: String = row.get("bar"); - /// println!("{}: {}", foo, bar); - /// } - /// ``` - pub fn get<'b, I, T>(&'b self, idx: I) -> T - where - I: RowIndex + fmt::Debug, - T: FromSql<'b>, - { - match self.get_inner(&idx) { - Some(Ok(ok)) => ok, - Some(Err(err)) => panic!("error retrieving column {:?}: {:?}", idx, err), - None => panic!("no such column {:?}", idx), - } - } - - /// 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 0-indexed. - /// - /// Returns `None` if the index does not reference a column, `Some(Err(..))` - /// if there was an error converting the result value, and `Some(Ok(..))` - /// on success. - pub fn get_opt<'b, I, T>(&'b self, idx: I) -> Option> - where - I: RowIndex, - T: FromSql<'b>, - { - self.get_inner(&idx) - } - - fn get_inner<'b, I, T>(&'b self, idx: &I) -> Option> - where - I: RowIndex, - T: FromSql<'b>, - { - let idx = match idx.__idx(&self.stmt_info.columns) { - Some(idx) => idx, - None => return None, - }; - - let ty = self.stmt_info.columns[idx].type_(); - if !::accepts(ty) { - return Some(Err(error::conversion(Box::new(WrongType::new(ty.clone()))))); - } - let value = FromSql::from_sql_nullable(ty, self.data.get(idx)); - Some(value.map_err(error::conversion)) - } -} - -/// A lazily-loaded iterator over the resulting rows of a query. -pub struct LazyRows<'trans, 'stmt> { - stmt: &'stmt Statement<'stmt>, - data: VecDeque, - name: String, - row_limit: i32, - more_rows: bool, - finished: bool, - _trans: &'trans Transaction<'trans>, -} - -impl<'a, 'b> Drop for LazyRows<'a, 'b> { - fn drop(&mut self) { - if !self.finished { - let _ = self.finish_inner(); - } - } -} - -impl<'a, 'b> fmt::Debug for LazyRows<'a, 'b> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("LazyRows") - .field("name", &self.name) - .field("row_limit", &self.row_limit) - .field("remaining_rows", &self.data.len()) - .field("more_rows", &self.more_rows) - .finish() - } -} - -impl<'trans, 'stmt> LazyRows<'trans, 'stmt> { - pub(crate) fn new( - stmt: &'stmt Statement<'stmt>, - data: VecDeque, - name: String, - row_limit: i32, - more_rows: bool, - finished: bool, - trans: &'trans Transaction<'trans>, - ) -> LazyRows<'trans, 'stmt> { - LazyRows { - stmt: stmt, - data: data, - name: name, - row_limit: row_limit, - more_rows: more_rows, - finished: finished, - _trans: trans, - } - } - - fn finish_inner(&mut self) -> Result<()> { - let mut conn = self.stmt.conn().0.borrow_mut(); - check_desync!(conn); - conn.close_statement(&self.name, b'P') - } - - fn execute(&mut self) -> Result<()> { - let mut conn = self.stmt.conn().0.borrow_mut(); - - conn.stream - .write_message(|buf| frontend::execute(&self.name, self.row_limit, buf))?; - conn.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?; - conn.stream.flush()?; - conn.read_rows(|row| self.data.push_back(row)) - .map(|more_rows| self.more_rows = more_rows) - } - - /// Returns a slice describing the columns of the `LazyRows`. - pub fn columns(&self) -> &[Column] { - self.stmt.columns() - } - - /// Consumes the `LazyRows`, cleaning up associated state. - /// - /// Functionally identical to the `Drop` implementation on `LazyRows` - /// except that it returns any error to the caller. - pub fn finish(mut self) -> Result<()> { - self.finish_inner() - } -} - -impl<'trans, 'stmt> FallibleIterator for LazyRows<'trans, 'stmt> { - type Item = Row<'stmt>; - type Error = Error; - - fn next(&mut self) -> Result>> { - if self.data.is_empty() && self.more_rows { - self.execute()?; - } - - let row = self.data.pop_front().map(|r| Row { - stmt_info: &**self.stmt.info(), - data: MaybeOwned::Owned(r), - }); - - Ok(row) - } - - fn size_hint(&self) -> (usize, Option) { - let lower = self.data.len(); - let upper = if self.more_rows { None } else { Some(lower) }; - (lower, upper) - } -} diff --git a/postgres/src/stmt.rs b/postgres/src/stmt.rs deleted file mode 100644 index 640cbcb2..00000000 --- a/postgres/src/stmt.rs +++ /dev/null @@ -1,605 +0,0 @@ -//! Prepared statements - -use fallible_iterator::FallibleIterator; -use postgres_protocol::message::{backend, frontend}; -use postgres_shared::rows::RowData; -use std::cell::Cell; -use std::collections::VecDeque; -use std::fmt; -use std::io::{self, Read, Write}; -use std::sync::Arc; - -#[doc(inline)] -pub use postgres_shared::stmt::Column; - -use rows::{LazyRows, Rows}; -use transaction::Transaction; -use types::{ToSql, Type}; -use {bad_response, err, Connection, Result, StatementInfo}; - -/// A prepared statement. -pub struct Statement<'conn> { - conn: &'conn Connection, - info: Arc, - next_portal_id: Cell, - finished: bool, -} - -impl<'a> fmt::Debug for Statement<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&*self.info, fmt) - } -} - -impl<'conn> Drop for Statement<'conn> { - fn drop(&mut self) { - let _ = self.finish_inner(); - } -} - -impl<'conn> Statement<'conn> { - pub(crate) fn new( - conn: &'conn Connection, - info: Arc, - next_portal_id: Cell, - finished: bool, - ) -> Statement<'conn> { - Statement { - conn: conn, - info: info, - next_portal_id: next_portal_id, - finished: finished, - } - } - - pub(crate) fn info(&self) -> &Arc { - &self.info - } - - pub(crate) fn conn(&self) -> &'conn Connection { - self.conn - } - - pub(crate) fn into_query(self, params: &[&ToSql]) -> Result { - check_desync!(self.conn); - let mut rows = vec![]; - self.inner_query("", 0, params, |row| rows.push(row))?; - Ok(Rows::new(&self, rows)) - } - - fn finish_inner(&mut self) -> Result<()> { - if self.finished { - Ok(()) - } else { - self.finished = true; - let mut conn = self.conn.0.borrow_mut(); - check_desync!(conn); - conn.close_statement(&self.info.name, b'S') - } - } - - #[allow(type_complexity)] - fn inner_query( - &self, - portal_name: &str, - row_limit: i32, - params: &[&ToSql], - acceptor: F, - ) -> Result - where - F: FnMut(RowData), - { - let mut conn = self.conn.0.borrow_mut(); - - conn.raw_execute( - &self.info.name, - portal_name, - row_limit, - self.param_types(), - params, - )?; - - conn.read_rows(acceptor) - } - - /// Returns a slice containing the expected parameter types. - pub fn param_types(&self) -> &[Type] { - &self.info.param_types - } - - /// Returns a slice describing the columns of the result of the query. - pub fn columns(&self) -> &[Column] { - &self.info.columns - } - - /// Executes the prepared statement, returning the number of rows modified. - /// - /// If the statement does not modify any rows (e.g. SELECT), 0 is returned. - /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number - /// expected. - /// - /// # Example - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode}; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// # let bar = 1i32; - /// # let baz = true; - /// let stmt = conn.prepare("UPDATE foo SET bar = $1 WHERE baz = $2").unwrap(); - /// let rows_updated = stmt.execute(&[&bar, &baz]).unwrap(); - /// println!("{} rows updated", rows_updated); - /// ``` - pub fn execute(&self, params: &[&ToSql]) -> Result { - let mut conn = self.conn.0.borrow_mut(); - check_desync!(conn); - conn.raw_execute(&self.info.name, "", 0, self.param_types(), params)?; - - let num; - loop { - match conn.read_message()? { - backend::Message::DataRow(_) => {} - backend::Message::ErrorResponse(body) => { - conn.wait_for_ready()?; - return Err(err(&mut body.fields())); - } - backend::Message::CommandComplete(body) => { - num = parse_update_count(body.tag()?); - break; - } - backend::Message::EmptyQueryResponse => { - num = 0; - break; - } - backend::Message::CopyInResponse(_) => { - conn.stream.write_message(|buf| { - frontend::copy_fail("COPY queries cannot be directly executed", buf) - })?; - conn.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?; - conn.stream.flush()?; - } - backend::Message::CopyOutResponse(_) => { - loop { - match conn.read_message()? { - backend::Message::CopyDone => break, - backend::Message::ErrorResponse(body) => { - conn.wait_for_ready()?; - return Err(err(&mut body.fields())); - } - _ => {} - } - } - num = 0; - break; - } - _ => { - conn.desynchronized = true; - return Err(bad_response().into()); - } - } - } - conn.wait_for_ready()?; - - Ok(num) - } - - /// Executes the prepared statement, returning the resulting rows. - /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number - /// expected. - /// - /// # Example - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode}; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// let stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1").unwrap(); - /// # let baz = true; - /// for row in &stmt.query(&[&baz]).unwrap() { - /// let foo: i32 = row.get("foo"); - /// println!("foo: {}", foo); - /// } - /// ``` - pub fn query(&self, params: &[&ToSql]) -> Result { - check_desync!(self.conn); - let mut rows = vec![]; - self.inner_query("", 0, params, |row| rows.push(row))?; - Ok(Rows::new(self, rows)) - } - - /// Executes the prepared statement, returning a lazily loaded iterator - /// over the resulting rows. - /// - /// 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. - /// If `row_limit` is less than or equal to 0, `lazy_query` is equivalent - /// to `query`. - /// - /// This can only be called inside of a transaction, and the `Transaction` - /// object representing the active transaction must be passed to - /// `lazy_query`. - /// - /// # Panics - /// - /// Panics if the provided `Transaction` is not associated with the same - /// `Connection` as this `Statement`, if the `Transaction` is not - /// active, or if the number of parameters provided does not match the - /// number of parameters expected. - /// - /// # Examples - /// - /// ```no_run - /// extern crate fallible_iterator; - /// extern crate postgres; - /// - /// use fallible_iterator::FallibleIterator; - /// # use postgres::{Connection, TlsMode}; - /// - /// # fn main() { - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// let stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1").unwrap(); - /// let trans = conn.transaction().unwrap(); - /// # let baz = true; - /// let mut rows = stmt.lazy_query(&trans, &[&baz], 100).unwrap(); - /// - /// while let Some(row) = rows.next().unwrap() { - /// let foo: i32 = row.get("foo"); - /// println!("foo: {}", foo); - /// } - /// # } - /// ``` - pub fn lazy_query<'trans, 'stmt>( - &'stmt self, - trans: &'trans Transaction, - params: &[&ToSql], - row_limit: i32, - ) -> Result> { - 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.0.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.info.name, id); - - let mut rows = VecDeque::new(); - let more_rows = - self.inner_query(&portal_name, row_limit, params, |row| rows.push_back(row))?; - Ok(LazyRows::new( - self, - rows, - portal_name, - row_limit, - more_rows, - false, - trans, - )) - } - - /// Executes a `COPY FROM STDIN` statement, returning the number of rows - /// added. - /// - /// The contents of the provided reader are passed to the Postgres server - /// verbatim; it is the caller's responsibility to ensure it uses the - /// proper format. See the - /// [Postgres documentation](http://www.postgresql.org/docs/9.4/static/sql-copy.html) - /// for details. - /// - /// If the statement is not a `COPY FROM STDIN` statement it will still be - /// executed and this method will return an error. - /// - /// # Examples - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode}; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// conn.batch_execute("CREATE TABLE people (id INT PRIMARY KEY, name VARCHAR)").unwrap(); - /// let stmt = conn.prepare("COPY people FROM STDIN").unwrap(); - /// stmt.copy_in(&[], &mut "1\tjohn\n2\tjane\n".as_bytes()).unwrap(); - /// ``` - pub fn copy_in(&self, params: &[&ToSql], r: &mut R) -> Result { - let mut conn = self.conn.0.borrow_mut(); - conn.raw_execute(&self.info.name, "", 0, self.param_types(), params)?; - - let (format, column_formats) = match conn.read_message()? { - backend::Message::CopyInResponse(body) => { - let format = body.format(); - let column_formats = body - .column_formats() - .map(|f| Format::from_u16(f)) - .collect()?; - (format, column_formats) - } - backend::Message::ErrorResponse(body) => { - conn.wait_for_ready()?; - return Err(err(&mut body.fields())); - } - _ => loop { - if let backend::Message::ReadyForQuery(_) = conn.read_message()? { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "called `copy_in` on a non-`COPY FROM STDIN` statement", - ).into()); - } - }, - }; - - let info = CopyInfo { - format: Format::from_u16(format as u16), - column_formats: column_formats, - }; - - let mut buf = [0; 16 * 1024]; - loop { - match fill_copy_buf(&mut buf, r, &info) { - Ok(0) => break, - Ok(len) => { - conn.stream - .write_message(|out| frontend::copy_data(&buf[..len], out))?; - } - Err(err) => { - conn.stream - .write_message(|buf| frontend::copy_fail("", buf))?; - conn.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::copy_done(buf)))?; - conn.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?; - conn.stream.flush()?; - match conn.read_message()? { - backend::Message::ErrorResponse(_) => { - // expected from the CopyFail - } - _ => { - conn.desynchronized = true; - return Err(bad_response().into()); - } - } - conn.wait_for_ready()?; - return Err(err.into()); - } - } - } - - conn.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::copy_done(buf)))?; - conn.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?; - conn.stream.flush()?; - - let num = match conn.read_message()? { - backend::Message::CommandComplete(body) => parse_update_count(body.tag()?), - backend::Message::ErrorResponse(body) => { - conn.wait_for_ready()?; - return Err(err(&mut body.fields())); - } - _ => { - conn.desynchronized = true; - return Err(bad_response().into()); - } - }; - - conn.wait_for_ready()?; - Ok(num) - } - - /// Executes a `COPY TO STDOUT` statement, passing the resulting data to - /// the provided writer and returning the number of rows received. - /// - /// See the [Postgres documentation](http://www.postgresql.org/docs/9.4/static/sql-copy.html) - /// for details on the data format. - /// - /// If the statement is not a `COPY TO STDOUT` statement it will still be - /// executed and this method will return an error. - /// - /// # Examples - /// - /// ```rust,no_run - /// # use postgres::{Connection, TlsMode}; - /// # let conn = Connection::connect("", TlsMode::None).unwrap(); - /// conn.batch_execute(" - /// CREATE TABLE people (id INT PRIMARY KEY, name VARCHAR); - /// INSERT INTO people (id, name) VALUES (1, 'john'), (2, 'jane');").unwrap(); - /// let stmt = conn.prepare("COPY people TO STDOUT").unwrap(); - /// let mut buf = vec![]; - /// stmt.copy_out(&[], &mut buf).unwrap(); - /// assert_eq!(buf, b"1\tjohn\n2\tjane\n"); - /// ``` - pub fn copy_out<'a, W: WriteWithInfo>(&'a self, params: &[&ToSql], w: &mut W) -> Result { - let mut conn = self.conn.0.borrow_mut(); - conn.raw_execute(&self.info.name, "", 0, self.param_types(), params)?; - - let (format, column_formats) = match conn.read_message()? { - backend::Message::CopyOutResponse(body) => { - let format = body.format(); - let column_formats = body - .column_formats() - .map(|f| Format::from_u16(f)) - .collect()?; - (format, column_formats) - } - backend::Message::CopyInResponse(_) => { - conn.stream - .write_message(|buf| frontend::copy_fail("", buf))?; - conn.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::copy_done(buf)))?; - conn.stream - .write_message(|buf| Ok::<(), io::Error>(frontend::sync(buf)))?; - conn.stream.flush()?; - match conn.read_message()? { - backend::Message::ErrorResponse(_) => { - // expected from the CopyFail - } - _ => { - conn.desynchronized = true; - return Err(bad_response().into()); - } - } - conn.wait_for_ready()?; - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "called `copy_out` on a non-`COPY TO STDOUT` statement", - ).into()); - } - backend::Message::ErrorResponse(body) => { - conn.wait_for_ready()?; - return Err(err(&mut body.fields())); - } - _ => loop { - if let backend::Message::ReadyForQuery(_) = conn.read_message()? { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "called `copy_out` on a non-`COPY TO STDOUT` statement", - ).into()); - } - }, - }; - - let info = CopyInfo { - format: Format::from_u16(format as u16), - column_formats: column_formats, - }; - - let count; - loop { - match conn.read_message()? { - backend::Message::CopyData(body) => { - let mut data = body.data(); - while !data.is_empty() { - match w.write_with_info(data, &info) { - Ok(n) => data = &data[n..], - Err(e) => loop { - if let backend::Message::ReadyForQuery(_) = conn.read_message()? { - return Err(e.into()); - } - }, - } - } - } - backend::Message::CopyDone => {} - backend::Message::CommandComplete(body) => { - count = parse_update_count(body.tag()?); - break; - } - backend::Message::ErrorResponse(body) => loop { - if let backend::Message::ReadyForQuery(_) = conn.read_message()? { - return Err(err(&mut body.fields())); - } - }, - _ => loop { - if let backend::Message::ReadyForQuery(_) = conn.read_message()? { - return Err(bad_response().into()); - } - }, - } - } - - conn.wait_for_ready()?; - Ok(count) - } - - /// Consumes the statement, clearing it from the Postgres session. - /// - /// If this statement was created via the `prepare_cached` method, `finish` - /// does nothing. - /// - /// Functionally identical to the `Drop` implementation of the - /// `Statement` except that it returns any error to the caller. - pub fn finish(mut self) -> Result<()> { - self.finish_inner() - } -} - -fn fill_copy_buf(buf: &mut [u8], r: &mut R, info: &CopyInfo) -> io::Result { - 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() == io::ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - Ok(nread) -} - -/// A struct containing information relevant for a `COPY` operation. -pub struct CopyInfo { - format: Format, - column_formats: Vec, -} - -impl CopyInfo { - /// Returns the format of the overall data. - pub fn format(&self) -> Format { - self.format - } - - /// Returns the format of the individual columns. - pub fn column_formats(&self) -> &[Format] { - &self.column_formats - } -} - -/// Like `Read` except that a `CopyInfo` object is provided as well. -/// -/// All types that implement `Read` also implement this trait. -pub trait ReadWithInfo { - /// Like `Read::read`. - fn read_with_info(&mut self, buf: &mut [u8], info: &CopyInfo) -> io::Result; -} - -impl ReadWithInfo for R { - fn read_with_info(&mut self, buf: &mut [u8], _: &CopyInfo) -> io::Result { - self.read(buf) - } -} - -/// Like `Write` except that a `CopyInfo` object is provided as well. -/// -/// All types that implement `Write` also implement this trait. -pub trait WriteWithInfo { - /// Like `Write::write`. - fn write_with_info(&mut self, buf: &[u8], info: &CopyInfo) -> io::Result; -} - -impl WriteWithInfo for W { - fn write_with_info(&mut self, buf: &[u8], _: &CopyInfo) -> io::Result { - self.write(buf) - } -} - -/// The format of a portion of COPY query data. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Format { - /// A text based format. - Text, - /// A binary format. - Binary, -} - -impl Format { - fn from_u16(value: u16) -> Format { - match value { - 0 => Format::Text, - _ => Format::Binary, - } - } -} - -fn parse_update_count(tag: &str) -> u64 { - tag.split(' ').last().unwrap().parse().unwrap_or(0) -} diff --git a/postgres/src/text_rows.rs b/postgres/src/text_rows.rs deleted file mode 100644 index 678e98a5..00000000 --- a/postgres/src/text_rows.rs +++ /dev/null @@ -1,191 +0,0 @@ -//! Query result rows. - -use postgres_shared::rows::RowData; -use std::fmt; -use std::slice; -use std::str; - -#[doc(inline)] -pub use postgres_shared::rows::RowIndex; - -use stmt::Column; -use {error, Result}; - -/// The resulting rows of a query. -pub struct TextRows { - columns: Vec, - data: Vec, -} - -impl fmt::Debug for TextRows { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("TextRows") - .field("columns", &self.columns()) - .field("rows", &self.data.len()) - .finish() - } -} - -impl TextRows { - pub(crate) fn new(columns: Vec, data: Vec) -> TextRows { - TextRows { - columns: columns, - data: data, - } - } - - /// Returns a slice describing the columns of the `TextRows`. - pub fn columns(&self) -> &[Column] { - &self.columns[..] - } - - /// Returns the number of rows present. - pub fn len(&self) -> usize { - self.data.len() - } - - /// Determines if there are any rows present. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns a specific `TextRow`. - /// - /// # Panics - /// - /// Panics if `idx` is out of bounds. - pub fn get<'a>(&'a self, idx: usize) -> TextRow<'a> { - TextRow { - columns: &self.columns, - data: &self.data[idx], - } - } - - /// Returns an iterator over the `TextRow`s. - pub fn iter<'a>(&'a self) -> Iter<'a> { - Iter { - columns: self.columns(), - iter: self.data.iter(), - } - } -} - -impl<'a> IntoIterator for &'a TextRows { - type Item = TextRow<'a>; - type IntoIter = Iter<'a>; - - fn into_iter(self) -> Iter<'a> { - self.iter() - } -} - -/// An iterator over `TextRow`s. -pub struct Iter<'a> { - columns: &'a [Column], - iter: slice::Iter<'a, RowData>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = TextRow<'a>; - - fn next(&mut self) -> Option> { - self.iter.next().map(|row| TextRow { - columns: self.columns, - data: row, - }) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl<'a> DoubleEndedIterator for Iter<'a> { - fn next_back(&mut self) -> Option> { - self.iter.next_back().map(|row| TextRow { - columns: self.columns, - data: row, - }) - } -} - -impl<'a> ExactSizeIterator for Iter<'a> {} - -/// A single result row of a query. -pub struct TextRow<'a> { - columns: &'a [Column], - data: &'a RowData, -} - -impl<'a> fmt::Debug for TextRow<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("TextRow") - .field("columns", &self.columns) - .finish() - } -} - -impl<'a> TextRow<'a> { - /// Returns the number of values in the row. - pub fn len(&self) -> usize { - self.data.len() - } - - /// Determines if there are any values in the row. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns a slice describing the columns of the `TextRow`. - pub fn columns(&self) -> &[Column] { - self.columns - } - - /// Retrieve the contents of a field of a row - /// - /// A field can be accessed by the name or index of its column, though - /// access by index is more efficient. Rows are 0-indexed. - /// - /// # Panics - /// - /// Panics if the index does not reference a column - pub fn get(&self, idx: I) -> &str - where - I: RowIndex + fmt::Debug, - { - match self.get_inner(&idx) { - Some(Ok(value)) => value, - Some(Err(err)) => panic!("error retrieving column {:?}: {:?}", idx, err), - None => panic!("no such column {:?}", idx), - } - } - - /// 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 0-indexed. - /// - /// Returns None if the index does not reference a column, Some(Err(..)) if - /// there was an error parsing the result as UTF-8, and Some(Ok(..)) on - /// success. - pub fn get_opt(&self, idx: I) -> Option> - where - I: RowIndex, - { - self.get_inner(&idx) - } - - fn get_inner(&self, idx: &I) -> Option> - where - I: RowIndex, - { - let idx = match idx.__idx(self.columns) { - Some(idx) => idx, - None => return None, - }; - - self.data - .get(idx) - .map(|s| str::from_utf8(s).map_err(|e| error::conversion(Box::new(e)))) - } -} diff --git a/postgres/src/tls.rs b/postgres/src/tls.rs deleted file mode 100644 index 8d425285..00000000 --- a/postgres/src/tls.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! Types and traits for TLS support. -pub use priv_io::Stream; - -use std::error::Error; -use std::fmt; -use std::io::prelude::*; - -/// A trait implemented by TLS streams. -pub trait TlsStream: fmt::Debug + Read + Write + Send { - /// Returns a reference to the underlying `Stream`. - fn get_ref(&self) -> &Stream; - - /// Returns a mutable reference to the underlying `Stream`. - fn get_mut(&mut self) -> &mut Stream; - - /// Returns the data associated with the `tls-server-end-point` channel binding type as - /// described in [RFC 5929], if supported. - /// - /// An implementation only needs to support one of this or `tls_unique`. - /// - /// [RFC 5929]: https://tools.ietf.org/html/rfc5929 - fn tls_server_end_point(&self) -> Option> { - None - } -} - -/// A trait implemented by types that can initiate a TLS session over a Postgres -/// stream. -pub trait TlsHandshake: fmt::Debug { - /// Performs a client-side TLS handshake, returning a wrapper around the - /// provided stream. - /// - /// The host portion of the connection parameters is provided for hostname - /// verification. - fn tls_handshake( - &self, - host: &str, - stream: Stream, - ) -> Result, Box>; -} - -impl TlsHandshake for Box { - fn tls_handshake( - &self, - host: &str, - stream: Stream, - ) -> Result, Box> { - (**self).tls_handshake(host, stream) - } -} diff --git a/postgres/src/transaction.rs b/postgres/src/transaction.rs deleted file mode 100644 index 3769c1a8..00000000 --- a/postgres/src/transaction.rs +++ /dev/null @@ -1,327 +0,0 @@ -//! Transactions - -use std::cell::Cell; -use std::fmt; - -use rows::Rows; -use stmt::Statement; -use text_rows::TextRows; -use types::ToSql; -use {bad_response, Connection, Result}; - -/// An enumeration of transaction isolation levels. -/// -/// See the [Postgres documentation](http://www.postgresql.org/docs/9.4/static/transaction-iso.html) -/// for full details on the semantics of each level. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum IsolationLevel { - /// The "read uncommitted" level. - /// - /// In current versions of Postgres, this behaves identically to - /// `ReadCommitted`. - ReadUncommitted, - /// The "read committed" level. - /// - /// This is the default isolation level in Postgres. - ReadCommitted, - /// The "repeatable read" level. - RepeatableRead, - /// The "serializable" level. - Serializable, -} - -impl IsolationLevel { - pub(crate) fn new(raw: &str) -> Result { - 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(bad_response().into()) - } - } - - fn to_sql(&self) -> &'static str { - match *self { - IsolationLevel::ReadUncommitted => "READ UNCOMMITTED", - IsolationLevel::ReadCommitted => "READ COMMITTED", - IsolationLevel::RepeatableRead => "REPEATABLE READ", - IsolationLevel::Serializable => "SERIALIZABLE", - } - } -} - -/// Configuration of a transaction. -#[derive(Debug)] -pub struct Config { - isolation_level: Option, - read_only: Option, - deferrable: Option, -} - -impl Default for Config { - fn default() -> Config { - Config { - isolation_level: None, - read_only: None, - deferrable: None, - } - } -} - -impl Config { - pub(crate) fn build_command(&self, s: &mut String) { - let mut first = true; - - if let Some(isolation_level) = self.isolation_level { - s.push_str(" ISOLATION LEVEL "); - s.push_str(isolation_level.to_sql()); - first = false; - } - - if let Some(read_only) = self.read_only { - if !first { - s.push(','); - } - if read_only { - s.push_str(" READ ONLY"); - } else { - s.push_str(" READ WRITE"); - } - first = false; - } - - if let Some(deferrable) = self.deferrable { - if !first { - s.push(','); - } - if deferrable { - s.push_str(" DEFERRABLE"); - } else { - s.push_str(" NOT DEFERRABLE"); - } - } - } - - /// Creates a new `Config` with no configuration overrides. - pub fn new() -> Config { - Config::default() - } - - /// Sets the isolation level of the configuration. - pub fn isolation_level(&mut self, isolation_level: IsolationLevel) -> &mut Config { - self.isolation_level = Some(isolation_level); - self - } - - /// Sets the read-only property of a transaction. - /// - /// If enabled, a transaction will be unable to modify any persistent - /// database state. - pub fn read_only(&mut self, read_only: bool) -> &mut Config { - self.read_only = Some(read_only); - self - } - - /// Sets the deferrable property of a transaction. - /// - /// If enabled in a read only, serializable transaction, the transaction may - /// block when created, after which it will run without the normal overhead - /// of a serializable transaction and will not be forced to roll back due - /// to serialization failures. - pub fn deferrable(&mut self, deferrable: bool) -> &mut Config { - self.deferrable = Some(deferrable); - self - } -} - -/// A transaction on a database connection. -/// -/// The transaction will roll back by default. -pub struct Transaction<'conn> { - conn: &'conn Connection, - depth: u32, - savepoint_name: Option, - commit: Cell, - finished: bool, -} - -impl<'a> fmt::Debug for Transaction<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("Transaction") - .field("commit", &self.commit.get()) - .field("depth", &self.depth) - .finish() - } -} - -impl<'conn> Drop for Transaction<'conn> { - fn drop(&mut self) { - if !self.finished { - let _ = self.finish_inner(); - } - } -} - -impl<'conn> Transaction<'conn> { - pub(crate) fn new(conn: &'conn Connection, depth: u32) -> Transaction<'conn> { - Transaction { - conn: conn, - depth: depth, - savepoint_name: None, - commit: Cell::new(false), - finished: false, - } - } - - pub(crate) fn conn(&self) -> &'conn Connection { - self.conn - } - - pub(crate) fn depth(&self) -> u32 { - self.depth - } - - fn finish_inner(&mut self) -> Result<()> { - let mut conn = self.conn.0.borrow_mut(); - debug_assert!(self.depth == conn.trans_depth); - conn.trans_depth -= 1; - match (self.commit.get(), &self.savepoint_name) { - (false, &Some(ref sp)) => conn.quick_query(&format!("ROLLBACK TO {}", sp))?, - (false, &None) => conn.quick_query("ROLLBACK")?, - (true, &Some(ref sp)) => conn.quick_query(&format!("RELEASE {}", sp))?, - (true, &None) => conn.quick_query("COMMIT")?, - }; - - Ok(()) - } - - /// Like `Connection::prepare`. - pub fn prepare(&self, query: &str) -> Result> { - self.conn.prepare(query) - } - - /// Like `Connection::prepare_cached`. - /// - /// # Note - /// - /// The statement will be cached for the duration of the - /// connection, not just the duration of this transaction. - pub fn prepare_cached(&self, query: &str) -> Result> { - self.conn.prepare_cached(query) - } - - /// Like `Connection::execute`. - pub fn execute(&self, query: &str, params: &[&ToSql]) -> Result { - self.conn.execute(query, params) - } - - /// Like `Connection::query`. - pub fn query<'a>(&'a self, query: &str, params: &[&ToSql]) -> Result { - self.conn.query(query, params) - } - - /// Like `Connection::batch_execute`. - #[deprecated(since = "0.15.3", note = "please use `simple_query` instead")] - pub fn batch_execute(&self, query: &str) -> Result<()> { - self.simple_query(query).map(|_| ()) - } - - /// Like `Connection::simple_query`. - pub fn simple_query(&self, query: &str) -> Result> { - self.conn.simple_query(query) - } - - /// Like `Connection::transaction`, but creates a nested transaction via - /// a savepoint. - /// - /// # Panics - /// - /// Panics if there is an active nested transaction. - pub fn transaction<'a>(&'a self) -> Result> { - self.savepoint(format!("sp_{}", self.depth())) - } - - /// Like `Connection::transaction`, but creates a nested transaction via - /// a savepoint with the specified name. - /// - /// # Panics - /// - /// Panics if there is an active nested transaction. - #[inline] - pub fn savepoint<'a, I>(&'a self, name: I) -> Result> - where - I: Into, - { - self._savepoint(name.into()) - } - - fn _savepoint<'a>(&'a self, name: String) -> Result> { - let mut conn = self.conn.0.borrow_mut(); - check_desync!(conn); - assert!( - conn.trans_depth == self.depth, - "`savepoint` may only be called on the active transaction" - ); - conn.quick_query(&format!("SAVEPOINT {}", name))?; - conn.trans_depth += 1; - Ok(Transaction { - conn: self.conn, - depth: self.depth + 1, - savepoint_name: Some(name), - commit: Cell::new(false), - finished: false, - }) - } - - /// Returns a reference to the `Transaction`'s `Connection`. - pub fn connection(&self) -> &'conn Connection { - self.conn - } - - /// Like `Connection::is_active`. - pub fn is_active(&self) -> bool { - self.conn.0.borrow().trans_depth == self.depth - } - - /// Alters the configuration of the active transaction. - pub fn set_config(&self, config: &Config) -> Result<()> { - let mut command = "SET TRANSACTION".to_owned(); - config.build_command(&mut command); - self.simple_query(&command).map(|_| ()) - } - - /// Determines if the transaction is currently set to commit or roll back. - pub fn will_commit(&self) -> bool { - self.commit.get() - } - - /// Sets the transaction to commit at its completion. - pub fn set_commit(&self) { - self.commit.set(true); - } - - /// Sets the transaction to roll back at its completion. - pub fn set_rollback(&self) { - self.commit.set(false); - } - - /// A convenience method which consumes and commits a transaction. - pub fn commit(self) -> Result<()> { - self.set_commit(); - self.finish() - } - - /// Consumes the transaction, committing or rolling it back as appropriate. - /// - /// Functionally equivalent to the `Drop` implementation of `Transaction` - /// except that it returns any error to the caller. - pub fn finish(mut self) -> Result<()> { - self.finished = true; - self.finish_inner() - } -} diff --git a/postgres/tests/test.rs b/postgres/tests/test.rs deleted file mode 100644 index 3b8ee384..00000000 --- a/postgres/tests/test.rs +++ /dev/null @@ -1,1481 +0,0 @@ -extern crate fallible_iterator; -extern crate postgres; -extern crate url; - -#[macro_use] -extern crate postgres_shared; - -use fallible_iterator::FallibleIterator; -use postgres::error::ErrorPosition::Normal; -use postgres::error::{DbError, SqlState}; -use postgres::notification::Notification; -use postgres::params::IntoConnectParams; -use postgres::transaction::{self, IsolationLevel}; -use postgres::types::{Kind, Oid, Type, WrongType}; -use postgres::{Connection, GenericConnection, HandleNotice, TlsMode}; -use std::io; -use std::thread; -use std::time::Duration; - -macro_rules! or_panic { - ($e:expr) => { - match $e { - Ok(ok) => ok, - Err(err) => panic!("{:#?}", err), - } - }; -} - -mod types; - -#[test] -fn test_non_default_database() { - or_panic!(Connection::connect( - "postgres://postgres@localhost:5433/postgres", - TlsMode::None, - )); -} - -#[test] -fn test_url_terminating_slash() { - or_panic!(Connection::connect( - "postgres://postgres@localhost:5433/", - TlsMode::None, - )); -} - -#[test] -fn test_prepare_err() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let err = conn.prepare("invalid sql database").unwrap_err(); - match err.as_db() { - Some(e) if e.code == SqlState::SYNTAX_ERROR && e.position == Some(Normal(1)) => {} - _ => panic!("Unexpected result {:?}", err), - } -} - -#[test] -fn test_unknown_database() { - match Connection::connect("postgres://postgres@localhost:5433/asdf", TlsMode::None) { - Err(ref e) if e.code() == Some(&SqlState::INVALID_CATALOG_NAME) => {} - Err(resp) => panic!("Unexpected result {:?}", resp), - _ => panic!("Unexpected result"), - } -} - -#[test] -fn test_connection_finish() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - assert!(conn.finish().is_ok()); -} - -#[test] -#[ignore] // doesn't work on our CI setup -fn test_unix_connection() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = or_panic!(conn.prepare("SHOW unix_socket_directories")); - let result = or_panic!(stmt.query(&[])); - let unix_socket_directories: String = result.iter().map(|row| row.get(0)).next().unwrap(); - - if unix_socket_directories.is_empty() { - panic!("can't test connect_unix; unix_socket_directories is empty"); - } - - let unix_socket_directory = unix_socket_directories.split(',').next().unwrap(); - - let path = url::percent_encoding::utf8_percent_encode( - unix_socket_directory, - url::percent_encoding::USERINFO_ENCODE_SET, - ); - let url = format!("postgres://postgres@{}", path); - let conn = or_panic!(Connection::connect(&url[..], TlsMode::None)); - assert!(conn.finish().is_ok()); -} - -#[test] -fn test_transaction_commit() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY)", &[])); - - let trans = or_panic!(conn.transaction()); - or_panic!(trans.execute("INSERT INTO foo (id) VALUES ($1)", &[&1i32])); - trans.set_commit(); - drop(trans); - - let stmt = or_panic!(conn.prepare("SELECT * FROM foo")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![1i32], - result.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -fn test_transaction_commit_finish() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY)", &[])); - - let trans = or_panic!(conn.transaction()); - or_panic!(trans.execute("INSERT INTO foo (id) VALUES ($1)", &[&1i32])); - trans.set_commit(); - assert!(trans.finish().is_ok()); - - let stmt = or_panic!(conn.prepare("SELECT * FROM foo")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![1i32], - result.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -fn test_transaction_commit_method() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY)", &[])); - - let trans = or_panic!(conn.transaction()); - or_panic!(trans.execute("INSERT INTO foo (id) VALUES ($1)", &[&1i32])); - assert!(trans.commit().is_ok()); - - let stmt = or_panic!(conn.prepare("SELECT * FROM foo")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![1i32], - result.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -fn test_transaction_rollback() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY)", &[])); - - or_panic!(conn.execute("INSERT INTO foo (id) VALUES ($1)", &[&1i32])); - - let trans = or_panic!(conn.transaction()); - or_panic!(trans.execute("INSERT INTO foo (id) VALUES ($1)", &[&2i32])); - drop(trans); - - let stmt = or_panic!(conn.prepare("SELECT * FROM foo")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![1i32], - result.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -fn test_transaction_rollback_finish() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY)", &[])); - - or_panic!(conn.execute("INSERT INTO foo (id) VALUES ($1)", &[&1i32])); - - let trans = or_panic!(conn.transaction()); - or_panic!(trans.execute("INSERT INTO foo (id) VALUES ($1)", &[&2i32])); - assert!(trans.finish().is_ok()); - - let stmt = or_panic!(conn.prepare("SELECT * FROM foo")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![1i32], - result.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -fn test_nested_transactions() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY)", &[])); - - or_panic!(conn.execute("INSERT INTO foo (id) VALUES (1)", &[])); - - { - let trans1 = or_panic!(conn.transaction()); - or_panic!(trans1.execute("INSERT INTO foo (id) VALUES (2)", &[])); - - { - let trans2 = or_panic!(trans1.transaction()); - or_panic!(trans2.execute("INSERT INTO foo (id) VALUES (3)", &[])); - } - - { - let trans2 = or_panic!(trans1.transaction()); - or_panic!(trans2.execute("INSERT INTO foo (id) VALUES (4)", &[])); - - { - let trans3 = or_panic!(trans2.transaction()); - or_panic!(trans3.execute("INSERT INTO foo (id) VALUES (5)", &[])); - } - - { - let sp = or_panic!(trans2.savepoint("custom")); - or_panic!(sp.execute("INSERT INTO foo (id) VALUES (6)", &[])); - assert!(sp.commit().is_ok()); - } - - assert!(trans2.commit().is_ok()); - } - - let stmt = or_panic!(trans1.prepare("SELECT * FROM foo ORDER BY id")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![1i32, 2, 4, 6], - result.iter().map(|row| row.get(0)).collect::>() - ); - } - - let stmt = or_panic!(conn.prepare("SELECT * FROM foo ORDER BY id")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![1i32], - result.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -fn test_nested_transactions_finish() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY)", &[])); - - or_panic!(conn.execute("INSERT INTO foo (id) VALUES (1)", &[])); - - { - let trans1 = or_panic!(conn.transaction()); - or_panic!(trans1.execute("INSERT INTO foo (id) VALUES (2)", &[])); - - { - let trans2 = or_panic!(trans1.transaction()); - or_panic!(trans2.execute("INSERT INTO foo (id) VALUES (3)", &[])); - assert!(trans2.finish().is_ok()); - } - - { - let trans2 = or_panic!(trans1.transaction()); - or_panic!(trans2.execute("INSERT INTO foo (id) VALUES (4)", &[])); - - { - let trans3 = or_panic!(trans2.transaction()); - or_panic!(trans3.execute("INSERT INTO foo (id) VALUES (5)", &[])); - assert!(trans3.finish().is_ok()); - } - - { - let sp = or_panic!(trans2.savepoint("custom")); - or_panic!(sp.execute("INSERT INTO foo (id) VALUES (6)", &[])); - sp.set_commit(); - assert!(sp.finish().is_ok()); - } - - trans2.set_commit(); - assert!(trans2.finish().is_ok()); - } - - // in a block to unborrow trans1 for the finish call - { - let stmt = or_panic!(trans1.prepare("SELECT * FROM foo ORDER BY id")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![1i32, 2, 4, 6], - result.iter().map(|row| row.get(0)).collect::>() - ); - } - - assert!(trans1.finish().is_ok()); - } - - let stmt = or_panic!(conn.prepare("SELECT * FROM foo ORDER BY id")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![1i32], - result.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -fn test_nested_transactions_partial_rollback() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY)", &[])); - - or_panic!(conn.execute("INSERT INTO foo (id) VALUES ($1)", &[&1i32])); - - { - let trans = or_panic!(conn.transaction()); - or_panic!(trans.execute("INSERT INTO foo (id) VALUES ($1)", &[&2i32])); - { - let trans = or_panic!(trans.transaction()); - or_panic!(trans.execute("INSERT INTO foo (id) VALUES ($1)", &[&3i32])); - { - let trans = or_panic!(trans.transaction()); - or_panic!(trans.execute("INSERT INTO foo (id) VALUES ($1)", &[&4i32])); - drop(trans); - } - drop(trans); - } - or_panic!(trans.commit()); - } - - let stmt = or_panic!(conn.prepare("SELECT * FROM foo ORDER BY id")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![1i32, 2], - result.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -#[should_panic(expected = "active transaction")] -fn test_conn_trans_when_nested() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let _trans = or_panic!(conn.transaction()); - conn.transaction().unwrap(); -} - -#[test] -#[should_panic(expected = "active transaction")] -fn test_trans_with_nested_trans() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let trans = or_panic!(conn.transaction()); - let _trans2 = or_panic!(trans.transaction()); - trans.transaction().unwrap(); -} - -#[test] -#[should_panic(expected = "active transaction")] -fn test_trans_with_savepoints() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let trans = or_panic!(conn.transaction()); - let _sp = or_panic!(trans.savepoint("custom")); - trans.savepoint("custom2").unwrap(); -} - -#[test] -fn test_stmt_execute_after_transaction() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let trans = or_panic!(conn.transaction()); - let stmt = or_panic!(trans.prepare("SELECT 1")); - or_panic!(trans.finish()); - let result = or_panic!(stmt.query(&[])); - assert_eq!(1i32, result.iter().next().unwrap().get::<_, i32>(0)); -} - -#[test] -fn test_stmt_finish() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id BIGINT PRIMARY KEY)", &[])); - let stmt = or_panic!(conn.prepare("SELECT * FROM foo")); - assert!(stmt.finish().is_ok()); -} - -#[test] -#[allow(deprecated)] -fn test_batch_execute() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let query = "CREATE TEMPORARY TABLE foo (id BIGINT PRIMARY KEY); - INSERT INTO foo (id) VALUES (10);"; - or_panic!(conn.batch_execute(query)); - - let stmt = or_panic!(conn.prepare("SELECT * from foo ORDER BY id")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![10i64], - result.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -#[allow(deprecated)] -fn test_batch_execute_error() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let query = "CREATE TEMPORARY TABLE foo (id BIGINT PRIMARY KEY); - INSERT INTO foo (id) VALUES (10); - asdfa; - INSERT INTO foo (id) VALUES (11)"; - conn.batch_execute(query).err().unwrap(); - - let stmt = conn.prepare("SELECT * FROM foo ORDER BY id"); - match stmt { - Err(ref e) if e.code() == Some(&SqlState::UNDEFINED_TABLE) => {} - Err(e) => panic!("unexpected error {:?}", e), - _ => panic!("unexpected success"), - } -} - -#[test] -#[allow(deprecated)] -fn test_transaction_batch_execute() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let trans = or_panic!(conn.transaction()); - let query = "CREATE TEMPORARY TABLE foo (id BIGINT PRIMARY KEY); - INSERT INTO foo (id) VALUES (10);"; - or_panic!(trans.batch_execute(query)); - - let stmt = or_panic!(trans.prepare("SELECT * from foo ORDER BY id")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![10i64], - result.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -fn test_query() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id BIGINT PRIMARY KEY)", &[])); - or_panic!(conn.execute("INSERT INTO foo (id) VALUES ($1), ($2)", &[&1i64, &2i64])); - let stmt = or_panic!(conn.prepare("SELECT * from foo ORDER BY id")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![1i64, 2], - result.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -fn test_error_after_datarow() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = or_panic!(conn.prepare( - " -SELECT - (SELECT generate_series(1, ss.i)) -FROM (SELECT gs.i - FROM generate_series(1, 2) gs(i) - ORDER BY gs.i - LIMIT 2) ss", - )); - match stmt.query(&[]) { - Err(ref e) if e.code() == Some(&SqlState::CARDINALITY_VIOLATION) => {} - Err(err) => panic!("Unexpected error {:?}", err), - Ok(_) => panic!("Expected failure"), - }; -} - -#[test] -fn test_lazy_query() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - - let trans = or_panic!(conn.transaction()); - or_panic!(trans.execute("CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY)", &[])); - let stmt = or_panic!(trans.prepare("INSERT INTO foo (id) VALUES ($1)")); - let values = vec![0i32, 1, 2, 3, 4, 5]; - for value in &values { - or_panic!(stmt.execute(&[value])); - } - let stmt = or_panic!(trans.prepare("SELECT id FROM foo ORDER BY id")); - let result = or_panic!(stmt.lazy_query(&trans, &[], 2)); - assert_eq!( - values, - result.map(|row| row.get(0)).collect::>().unwrap() - ); -} - -#[test] -#[should_panic(expected = "same `Connection` as")] -fn test_lazy_query_wrong_conn() { - let conn1 = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let conn2 = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - - let trans = or_panic!(conn1.transaction()); - let stmt = or_panic!(conn2.prepare("SELECT 1::INT")); - stmt.lazy_query(&trans, &[], 1).unwrap(); -} - -#[test] -fn test_param_types() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = or_panic!(conn.prepare("SELECT $1::INT, $2::VARCHAR")); - assert_eq!(stmt.param_types(), &[Type::INT4, Type::VARCHAR][..]); -} - -#[test] -fn test_columns() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = or_panic!(conn.prepare("SELECT 1::INT as a, 'hi'::VARCHAR as b")); - let cols = stmt.columns(); - assert_eq!(2, cols.len()); - assert_eq!(cols[0].name(), "a"); - assert_eq!(cols[0].type_(), &Type::INT4); - assert_eq!(cols[1].name(), "b"); - assert_eq!(cols[1].type_(), &Type::VARCHAR); -} - -#[test] -fn test_execute_counts() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - assert_eq!( - 0, - or_panic!(conn.execute( - "CREATE TEMPORARY TABLE foo ( - id SERIAL PRIMARY KEY, - b INT - )", - &[], - )) - ); - assert_eq!( - 3, - or_panic!(conn.execute( - "INSERT INTO foo (b) VALUES ($1), ($2), ($2)", - &[&1i32, &2i32], - )) - ); - assert_eq!( - 2, - or_panic!(conn.execute("UPDATE foo SET b = 0 WHERE b = 2", &[])) - ); - assert_eq!(3, or_panic!(conn.execute("SELECT * FROM foo", &[]))); -} - -#[test] -fn test_wrong_param_type() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let err = conn.execute("SELECT $1::VARCHAR", &[&1i32]).unwrap_err(); - match err.as_conversion() { - Some(e) if e.is::() => {} - _ => panic!("unexpected result {:?}", err), - } -} - -#[test] -#[should_panic(expected = "expected 2 parameters but got 1")] -fn test_too_few_params() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let _ = conn.execute("SELECT $1::INT, $2::INT", &[&1i32]); -} - -#[test] -#[should_panic(expected = "expected 2 parameters but got 3")] -fn test_too_many_params() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let _ = conn.execute("SELECT $1::INT, $2::INT", &[&1i32, &2i32, &3i32]); -} - -#[test] -fn test_index_named() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = or_panic!(conn.prepare("SELECT 10::INT as val")); - let result = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![10i32], - result - .iter() - .map(|row| row.get("val")) - .collect::>() - ); -} - -#[test] -#[should_panic] -fn test_index_named_fail() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = or_panic!(conn.prepare("SELECT 10::INT as id")); - let result = or_panic!(stmt.query(&[])); - - let _: i32 = result.iter().next().unwrap().get("asdf"); -} - -#[test] -fn test_get_named_err() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = or_panic!(conn.prepare("SELECT 10::INT as id")); - let result = or_panic!(stmt.query(&[])); - - match result.iter().next().unwrap().get_opt::<_, i32>("asdf") { - None => {} - res => panic!("unexpected result {:?}", res), - }; -} - -#[test] -fn test_get_was_null() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = or_panic!(conn.prepare("SELECT NULL::INT as id")); - let result = or_panic!(stmt.query(&[])); - - match result.iter().next().unwrap().get_opt::<_, i32>(0) { - Some(Err(ref e)) if e.as_conversion().is_some() => {} - res => panic!("unexpected result {:?}", res), - }; -} - -#[test] -fn test_get_off_by_one() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = or_panic!(conn.prepare("SELECT 10::INT as id")); - let result = or_panic!(stmt.query(&[])); - - match result.iter().next().unwrap().get_opt::<_, i32>(1) { - None => {} - res => panic!("unexpected result {:?}", res), - }; -} - -#[test] -fn test_custom_notice_handler() { - static mut COUNT: usize = 0; - struct Handler; - - impl HandleNotice for Handler { - fn handle_notice(&mut self, notice: DbError) { - assert_eq!("note", notice.message); - unsafe { - COUNT += 1; - } - } - } - - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433?client_min_messages=NOTICE", - TlsMode::None, - )); - conn.set_notice_handler(Box::new(Handler)); - or_panic!(conn.execute( - "CREATE FUNCTION pg_temp.note() RETURNS INT AS $$ - BEGIN - RAISE NOTICE 'note'; - RETURN 1; - END; $$ LANGUAGE plpgsql", - &[], - )); - or_panic!(conn.execute("SELECT pg_temp.note()", &[])); - - assert_eq!(unsafe { COUNT }, 1); -} - -#[test] -fn test_notification_iterator_none() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - assert!(conn.notifications().iter().next().unwrap().is_none()); -} - -fn check_notification(expected: Notification, actual: Notification) { - assert_eq!(&expected.channel, &actual.channel); - assert_eq!(&expected.payload, &actual.payload); -} - -#[test] -fn test_notification_iterator_some() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let notifications = conn.notifications(); - let mut it = notifications.iter(); - or_panic!(conn.execute("LISTEN test_notification_iterator_one_channel", &[])); - or_panic!(conn.execute("LISTEN test_notification_iterator_one_channel2", &[])); - or_panic!(conn.execute( - "NOTIFY test_notification_iterator_one_channel, 'hello'", - &[], - )); - or_panic!(conn.execute( - "NOTIFY test_notification_iterator_one_channel2, 'world'", - &[], - )); - - check_notification( - Notification { - process_id: 0, - channel: "test_notification_iterator_one_channel".to_string(), - payload: "hello".to_string(), - }, - it.next().unwrap().unwrap(), - ); - check_notification( - Notification { - process_id: 0, - channel: "test_notification_iterator_one_channel2".to_string(), - payload: "world".to_string(), - }, - it.next().unwrap().unwrap(), - ); - assert!(it.next().unwrap().is_none()); - - or_panic!(conn.execute("NOTIFY test_notification_iterator_one_channel, '!'", &[])); - check_notification( - Notification { - process_id: 0, - channel: "test_notification_iterator_one_channel".to_string(), - payload: "!".to_string(), - }, - it.next().unwrap().unwrap(), - ); - assert!(it.next().unwrap().is_none()); -} - -#[test] -fn test_notifications_next_block() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("LISTEN test_notifications_next_block", &[])); - - let _t = thread::spawn(|| { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - thread::sleep(Duration::from_millis(500)); - or_panic!(conn.execute("NOTIFY test_notifications_next_block, 'foo'", &[])); - }); - - let notifications = conn.notifications(); - check_notification( - Notification { - process_id: 0, - channel: "test_notifications_next_block".to_string(), - payload: "foo".to_string(), - }, - notifications.blocking_iter().next().unwrap().unwrap(), - ); -} - -#[test] -fn test_notification_next_timeout() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("LISTEN test_notifications_next_timeout", &[])); - - let _t = thread::spawn(|| { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - thread::sleep(Duration::from_millis(500)); - or_panic!(conn.execute("NOTIFY test_notifications_next_timeout, 'foo'", &[])); - thread::sleep(Duration::from_millis(1500)); - or_panic!(conn.execute("NOTIFY test_notifications_next_timeout, 'foo'", &[])); - }); - - let notifications = conn.notifications(); - let mut it = notifications.timeout_iter(Duration::from_secs(1)); - check_notification( - Notification { - process_id: 0, - channel: "test_notifications_next_timeout".to_string(), - payload: "foo".to_string(), - }, - it.next().unwrap().unwrap(), - ); - - assert!(it.next().unwrap().is_none()); -} - -#[test] -fn test_notification_disconnect() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("LISTEN test_notifications_disconnect", &[])); - - let _t = thread::spawn(|| { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - thread::sleep(Duration::from_millis(500)); - or_panic!(conn.execute( - "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE query = 'LISTEN test_notifications_disconnect'", - &[], - )); - }); - - let notifications = conn.notifications(); - assert!(notifications.blocking_iter().next().is_err()); -} - -#[test] -// This test is pretty sad, but I don't think there's a better way :( -fn test_cancel_query() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let cancel_data = conn.cancel_data(); - - let t = thread::spawn(move || { - thread::sleep(Duration::from_millis(500)); - assert!( - postgres::cancel_query( - "postgres://postgres@localhost:5433", - TlsMode::None, - &cancel_data, - ).is_ok() - ); - }); - - match conn.execute("SELECT pg_sleep(10)", &[]) { - Err(ref e) if e.code() == Some(&SqlState::QUERY_CANCELED) => {} - Err(res) => panic!("Unexpected result {:?}", res), - _ => panic!("Unexpected result"), - } - - t.join().unwrap(); -} - -#[test] -fn test_plaintext_pass() { - or_panic!(Connection::connect( - "postgres://pass_user:password@localhost:5433/postgres", - TlsMode::None, - )); -} - -#[test] -fn test_plaintext_pass_no_pass() { - let ret = Connection::connect( - "postgres://pass_user@localhost:5433/postgres", - TlsMode::None, - ); - match ret { - Err(ref e) if e.as_connection().is_some() => (), - Err(err) => panic!("Unexpected error {:?}", err), - _ => panic!("Expected error"), - } -} - -#[test] -fn test_plaintext_pass_wrong_pass() { - let ret = Connection::connect( - "postgres://pass_user:asdf@localhost:5433/postgres", - TlsMode::None, - ); - match ret { - Err(ref e) if e.code() == Some(&SqlState::INVALID_PASSWORD) => {} - Err(err) => panic!("Unexpected error {:?}", err), - _ => panic!("Expected error"), - } -} - -#[test] -fn test_md5_pass() { - or_panic!(Connection::connect( - "postgres://md5_user:password@localhost:5433/postgres", - TlsMode::None, - )); -} - -#[test] -fn test_md5_pass_no_pass() { - let ret = Connection::connect("postgres://md5_user@localhost:5433/postgres", TlsMode::None); - match ret { - Err(ref e) if e.as_connection().is_some() => (), - Err(err) => panic!("Unexpected error {:?}", err), - _ => panic!("Expected error"), - } -} - -#[test] -fn test_md5_pass_wrong_pass() { - let ret = Connection::connect( - "postgres://md5_user:asdf@localhost:5433/postgres", - TlsMode::None, - ); - match ret { - Err(ref e) if e.code() == Some(&SqlState::INVALID_PASSWORD) => {} - Err(err) => panic!("Unexpected error {:?}", err), - _ => panic!("Expected error"), - } -} - -#[test] -fn test_scram_pass() { - or_panic!(Connection::connect( - "postgres://scram_user:password@localhost:5433/postgres", - TlsMode::None, - )); -} - -#[test] -fn test_scram_pass_no_pass() { - let ret = Connection::connect( - "postgres://scram_user@localhost:5433/postgres", - TlsMode::None, - ); - match ret { - Err(ref e) if e.as_connection().is_some() => (), - Err(err) => panic!("Unexpected error {:?}", err), - _ => panic!("Expected error"), - } -} - -#[test] -fn test_scram_pass_wrong_pass() { - let ret = Connection::connect( - "postgres://scram_user:asdf@localhost:5433/postgres", - TlsMode::None, - ); - match ret { - Err(ref e) if e.code() == Some(&SqlState::INVALID_PASSWORD) => {} - Err(err) => panic!("Unexpected error {:?}", err), - _ => panic!("Expected error"), - } -} - -#[test] -fn test_execute_copy_from_err() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[])); - let stmt = or_panic!(conn.prepare("COPY foo (id) FROM STDIN")); - - let err = stmt.execute(&[]).unwrap_err(); - match err.as_db() { - Some(err) if err.message.contains("COPY") => {} - _ => panic!("Unexpected error {:?}", err), - } - - let err = stmt.execute(&[]).unwrap_err(); - match err.as_db() { - Some(err) if err.message.contains("COPY") => {} - _ => panic!("Unexpected error {:?}", err), - } -} - -#[test] -#[allow(deprecated)] -fn test_batch_execute_copy_from_err() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[])); - let err = conn.batch_execute("COPY foo (id) FROM STDIN").unwrap_err(); - match err.as_db() { - Some(err) if err.message.contains("COPY") => {} - _ => panic!("Unexpected error {:?}", err), - } -} - -#[test] -fn test_copy_io_error() { - struct ErrorReader; - - impl io::Read for ErrorReader { - fn read(&mut self, _: &mut [u8]) -> io::Result { - Err(io::Error::new(io::ErrorKind::AddrNotAvailable, "boom")) - } - } - - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[])); - let stmt = or_panic!(conn.prepare("COPY foo (id) FROM STDIN")); - let err = stmt.copy_in(&[], &mut ErrorReader).unwrap_err(); - match err.as_io() { - Some(e) if e.kind() == io::ErrorKind::AddrNotAvailable => {} - _ => panic!("Unexpected error {:?}", err), - } - - or_panic!(conn.execute("SELECT 1", &[])); -} - -#[test] -fn test_copy() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[])); - let stmt = or_panic!(conn.prepare("COPY foo (id) FROM STDIN")); - let mut data = &b"1\n2\n3\n5\n8\n"[..]; - assert_eq!(5, or_panic!(stmt.copy_in(&[], &mut data))); - let stmt = or_panic!(conn.prepare("SELECT id FROM foo ORDER BY id")); - assert_eq!( - vec![1i32, 2, 3, 5, 8], - stmt.query(&[]) - .unwrap() - .iter() - .map(|r| r.get(0)) - .collect::>() - ); -} - -#[test] -fn test_query_copy_out_err() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.simple_query( - " - CREATE TEMPORARY TABLE foo (id INT); - INSERT INTO foo (id) VALUES (0), (1), (2), (3)", - )); - let stmt = or_panic!(conn.prepare("COPY foo (id) TO STDOUT")); - let err = stmt.query(&[]).unwrap_err(); - match err.as_io() { - Some(e) if e.to_string().contains("COPY") => {} - _ => panic!("unexpected error {:?}", err), - }; -} - -#[test] -fn test_copy_out() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.simple_query( - " - CREATE TEMPORARY TABLE foo (id INT); - INSERT INTO foo (id) VALUES (0), (1), (2), (3)", - )); - let stmt = or_panic!(conn.prepare("COPY (SELECT id FROM foo ORDER BY id) TO STDOUT")); - let mut buf = vec![]; - let count = or_panic!(stmt.copy_out(&[], &mut buf)); - assert_eq!(count, 4); - assert_eq!(buf, b"0\n1\n2\n3\n"); - or_panic!(conn.simple_query("SELECT 1")); -} - -#[test] -fn test_copy_out_error() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.simple_query( - " - CREATE TEMPORARY TABLE foo (id INT); - INSERT INTO foo (id) VALUES (0), (1), (2), (3)", - )); - let stmt = or_panic!(conn.prepare("COPY (SELECT id FROM foo ORDER BY id) TO STDOUT (OIDS)")); - let mut buf = vec![]; - let err = stmt.copy_out(&[], &mut buf).unwrap_err(); - match err.as_db() { - Some(_) => {} - _ => panic!("unexpected error {}", err), - } -} - -#[test] -// Just make sure the impls don't infinite loop -fn test_generic_connection() { - fn f(t: &T) - where - T: GenericConnection, - { - or_panic!(t.execute("SELECT 1", &[])); - } - - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - f(&conn); - let trans = or_panic!(conn.transaction()); - f(&trans); -} - -#[test] -fn test_custom_range_element_type() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute( - "CREATE TYPE pg_temp.floatrange AS RANGE ( - subtype = float8, - subtype_diff = float8mi - )", - &[], - )); - let stmt = or_panic!(conn.prepare("SELECT $1::floatrange")); - let ty = &stmt.param_types()[0]; - assert_eq!("floatrange", ty.name()); - assert_eq!(&Kind::Range(Type::FLOAT8), ty.kind()); -} - -#[test] -fn test_prepare_cached() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[])); - or_panic!(conn.execute("INSERT INTO foo (id) VALUES (1), (2)", &[])); - - let stmt = or_panic!(conn.prepare_cached("SELECT id FROM foo ORDER BY id")); - assert_eq!( - vec![1, 2], - or_panic!(stmt.query(&[])) - .iter() - .map(|r| r.get(0)) - .collect::>() - ); - or_panic!(stmt.finish()); - - let stmt = or_panic!(conn.prepare_cached("SELECT id FROM foo ORDER BY id")); - assert_eq!( - vec![1, 2], - or_panic!(stmt.query(&[])) - .iter() - .map(|r| r.get(0)) - .collect::>() - ); - or_panic!(stmt.finish()); - - let stmt = or_panic!(conn.prepare_cached("SELECT id FROM foo ORDER BY id DESC")); - assert_eq!( - vec![2, 1], - or_panic!(stmt.query(&[])) - .iter() - .map(|r| r.get(0)) - .collect::>() - ); - or_panic!(stmt.finish()); -} - -#[test] -fn test_is_active() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - assert!(conn.is_active()); - let trans = or_panic!(conn.transaction()); - assert!(!conn.is_active()); - assert!(trans.is_active()); - { - let trans2 = or_panic!(trans.transaction()); - assert!(!conn.is_active()); - assert!(!trans.is_active()); - assert!(trans2.is_active()); - or_panic!(trans2.finish()); - } - assert!(!conn.is_active()); - assert!(trans.is_active()); - or_panic!(trans.finish()); - assert!(conn.is_active()); -} - -#[test] -fn test_parameter() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - assert_eq!(Some("UTF8".to_string()), conn.parameter("client_encoding")); - assert_eq!(None, conn.parameter("asdf")); -} - -#[test] -fn url_unencoded_password() { - assert!( - "postgresql://username:password%1*@localhost:5433" - .into_connect_params() - .is_err() - ) -} - -#[test] -fn url_encoded_password() { - let params = "postgresql://username%7b%7c:password%7b%7c@localhost:5433" - .into_connect_params() - .unwrap(); - assert_eq!("username{|", params.user().unwrap().name()); - assert_eq!("password{|", params.user().unwrap().password().unwrap()); -} - -#[test] -fn test_transaction_isolation_level() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - assert_eq!( - IsolationLevel::ReadCommitted, - or_panic!(conn.transaction_isolation()) - ); - or_panic!(conn.set_transaction_config( - transaction::Config::new().isolation_level(IsolationLevel::ReadUncommitted), - )); - assert_eq!( - IsolationLevel::ReadUncommitted, - or_panic!(conn.transaction_isolation()) - ); - or_panic!(conn.set_transaction_config( - transaction::Config::new().isolation_level(IsolationLevel::RepeatableRead), - )); - assert_eq!( - IsolationLevel::RepeatableRead, - or_panic!(conn.transaction_isolation()) - ); - or_panic!(conn.set_transaction_config( - transaction::Config::new().isolation_level(IsolationLevel::Serializable), - )); - assert_eq!( - IsolationLevel::Serializable, - or_panic!(conn.transaction_isolation()) - ); - or_panic!(conn.set_transaction_config( - transaction::Config::new().isolation_level(IsolationLevel::ReadCommitted), - )); - assert_eq!( - IsolationLevel::ReadCommitted, - or_panic!(conn.transaction_isolation()) - ); -} - -#[test] -fn test_rows_index() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - conn.simple_query( - " - CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY); - INSERT INTO foo (id) VALUES (1), (2), (3); - ", - ).unwrap(); - let stmt = conn.prepare("SELECT id FROM foo ORDER BY id").unwrap(); - let rows = stmt.query(&[]).unwrap(); - assert_eq!(3, rows.len()); - assert_eq!(2i32, rows.get(1).get::<_, i32>(0)); -} - -#[test] -fn test_type_names() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - let stmt = conn - .prepare( - "SELECT t.oid, t.typname - FROM pg_catalog.pg_type t, pg_namespace n - WHERE n.oid = t.typnamespace - AND n.nspname = 'pg_catalog' - AND t.oid < 10000 - AND t.typtype != 'c'", - ).unwrap(); - for row in &stmt.query(&[]).unwrap() { - let id: Oid = row.get(0); - let name: String = row.get(1); - assert_eq!(Type::from_oid(id).unwrap().name(), name); - } -} - -#[test] -fn test_conn_query() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - conn.simple_query( - " - CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY); - INSERT INTO foo (id) VALUES (1), (2), (3); - ", - ).unwrap(); - let ids = conn - .query("SELECT id FROM foo ORDER BY id", &[]) - .unwrap() - .iter() - .map(|r| r.get(0)) - .collect::>(); - assert_eq!(ids, [1, 2, 3]); -} - -#[test] -fn transaction_config() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - let mut config = transaction::Config::new(); - config - .isolation_level(IsolationLevel::Serializable) - .read_only(true) - .deferrable(true); - conn.set_transaction_config(&config).unwrap(); -} - -#[test] -fn transaction_config_one_setting() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - conn.set_transaction_config(transaction::Config::new().read_only(true)) - .unwrap(); - conn.set_transaction_config(transaction::Config::new().deferrable(true)) - .unwrap(); -} - -#[test] -fn transaction_with() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - let mut config = transaction::Config::new(); - config - .isolation_level(IsolationLevel::Serializable) - .read_only(true) - .deferrable(true); - conn.transaction_with(&config).unwrap().finish().unwrap(); -} - -#[test] -fn transaction_set_config() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - let trans = conn.transaction().unwrap(); - let mut config = transaction::Config::new(); - config - .isolation_level(IsolationLevel::Serializable) - .read_only(true) - .deferrable(true); - trans.set_config(&config).unwrap(); - trans.finish().unwrap(); -} - -#[test] -fn keepalive() { - let params = "postgres://postgres@localhost:5433?keepalive=10" - .into_connect_params() - .unwrap(); - assert_eq!(params.keepalive(), Some(Duration::from_secs(10))); - - Connection::connect(params, TlsMode::None).unwrap(); -} - -#[test] -fn explicit_types() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - let stmt = conn - .prepare_typed("SELECT $1::INT4", &[Some(Type::INT8)]) - .unwrap(); - assert_eq!(stmt.param_types()[0], Type::INT8); -} - -#[test] -fn simple_query() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - conn.simple_query( - " - CREATE TEMPORARY TABLE foo (id INT PRIMARY KEY); - INSERT INTO foo (id) VALUES (1), (2), (3); - ", - ).unwrap(); - let queries = "SELECT id FROM foo WHERE id = 1 ORDER BY id; \ - SELECT id FROM foo WHERE id != 1 ORDER BY id"; - - let results = conn.simple_query(queries).unwrap(); - assert_eq!(results[0].get(0).get("id"), "1"); - assert_eq!(results[1].get(0).get("id"), "2"); - assert_eq!(results[1].get(1).get("id"), "3"); -} diff --git a/postgres/tests/types/bit_vec.rs b/postgres/tests/types/bit_vec.rs deleted file mode 100644 index 2e0ac53d..00000000 --- a/postgres/tests/types/bit_vec.rs +++ /dev/null @@ -1,30 +0,0 @@ -extern crate bit_vec; - -use self::bit_vec::BitVec; -use types::test_type; - -#[test] -fn test_bit_params() { - let mut bv = BitVec::from_bytes(&[0b0110_1001, 0b0000_0111]); - bv.pop(); - bv.pop(); - test_type( - "BIT(14)", - &[(Some(bv), "B'01101001000001'"), (None, "NULL")], - ) -} - -#[test] -fn test_varbit_params() { - let mut bv = BitVec::from_bytes(&[0b0110_1001, 0b0000_0111]); - bv.pop(); - bv.pop(); - test_type( - "VARBIT", - &[ - (Some(bv), "B'01101001000001'"), - (Some(BitVec::from_bytes(&[])), "B''"), - (None, "NULL"), - ], - ) -} diff --git a/postgres/tests/types/chrono.rs b/postgres/tests/types/chrono.rs deleted file mode 100644 index 3d5ef64d..00000000 --- a/postgres/tests/types/chrono.rs +++ /dev/null @@ -1,150 +0,0 @@ -extern crate chrono; - -use self::chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; -use types::test_type; - -use postgres::types::{Date, Timestamp}; - -#[test] -fn test_naive_date_time_params() { - fn make_check<'a>(time: &'a str) -> (Option, &'a str) { - ( - Some(NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()), - time, - ) - } - test_type( - "TIMESTAMP", - &[ - make_check("'1970-01-01 00:00:00.010000000'"), - make_check("'1965-09-25 11:19:33.100314000'"), - make_check("'2010-02-09 23:11:45.120200000'"), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_with_special_naive_date_time_params() { - fn make_check<'a>(time: &'a str) -> (Timestamp, &'a str) { - ( - Timestamp::Value( - NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap(), - ), - time, - ) - } - test_type( - "TIMESTAMP", - &[ - make_check("'1970-01-01 00:00:00.010000000'"), - make_check("'1965-09-25 11:19:33.100314000'"), - make_check("'2010-02-09 23:11:45.120200000'"), - (Timestamp::PosInfinity, "'infinity'"), - (Timestamp::NegInfinity, "'-infinity'"), - ], - ); -} - -#[test] -fn test_date_time_params() { - fn make_check<'a>(time: &'a str) -> (Option>, &'a str) { - ( - Some( - Utc.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'") - .unwrap(), - ), - time, - ) - } - test_type( - "TIMESTAMP WITH TIME ZONE", - &[ - make_check("'1970-01-01 00:00:00.010000000'"), - make_check("'1965-09-25 11:19:33.100314000'"), - make_check("'2010-02-09 23:11:45.120200000'"), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_with_special_date_time_params() { - fn make_check<'a>(time: &'a str) -> (Timestamp>, &'a str) { - ( - Timestamp::Value( - Utc.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'") - .unwrap(), - ), - time, - ) - } - test_type( - "TIMESTAMP WITH TIME ZONE", - &[ - make_check("'1970-01-01 00:00:00.010000000'"), - make_check("'1965-09-25 11:19:33.100314000'"), - make_check("'2010-02-09 23:11:45.120200000'"), - (Timestamp::PosInfinity, "'infinity'"), - (Timestamp::NegInfinity, "'-infinity'"), - ], - ); -} - -#[test] -fn test_date_params() { - fn make_check<'a>(time: &'a str) -> (Option, &'a str) { - ( - Some(NaiveDate::parse_from_str(time, "'%Y-%m-%d'").unwrap()), - time, - ) - } - test_type( - "DATE", - &[ - make_check("'1970-01-01'"), - make_check("'1965-09-25'"), - make_check("'2010-02-09'"), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_with_special_date_params() { - fn make_check<'a>(date: &'a str) -> (Date, &'a str) { - ( - Date::Value(NaiveDate::parse_from_str(date, "'%Y-%m-%d'").unwrap()), - date, - ) - } - test_type( - "DATE", - &[ - make_check("'1970-01-01'"), - make_check("'1965-09-25'"), - make_check("'2010-02-09'"), - (Date::PosInfinity, "'infinity'"), - (Date::NegInfinity, "'-infinity'"), - ], - ); -} - -#[test] -fn test_time_params() { - fn make_check<'a>(time: &'a str) -> (Option, &'a str) { - ( - Some(NaiveTime::parse_from_str(time, "'%H:%M:%S.%f'").unwrap()), - time, - ) - } - test_type( - "TIME", - &[ - make_check("'00:00:00.010000000'"), - make_check("'11:19:33.100314000'"), - make_check("'23:11:45.120200000'"), - (None, "NULL"), - ], - ); -} diff --git a/postgres/tests/types/eui48.rs b/postgres/tests/types/eui48.rs deleted file mode 100644 index dc77078e..00000000 --- a/postgres/tests/types/eui48.rs +++ /dev/null @@ -1,17 +0,0 @@ -extern crate eui48; - -use types::test_type; - -#[test] -fn test_eui48_params() { - test_type( - "MACADDR", - &[ - ( - Some(eui48::MacAddress::parse_str("12-34-56-AB-CD-EF").unwrap()), - "'12-34-56-ab-cd-ef'", - ), - (None, "NULL"), - ], - ) -} diff --git a/postgres/tests/types/geo.rs b/postgres/tests/types/geo.rs deleted file mode 100644 index 43a95deb..00000000 --- a/postgres/tests/types/geo.rs +++ /dev/null @@ -1,58 +0,0 @@ -extern crate geo; - -use self::geo::{Coordinate, LineString, Point, Rect}; -use types::test_type; - -#[test] -fn test_point_params() { - test_type( - "POINT", - &[ - (Some(Point::new(0.0, 0.0)), "POINT(0, 0)"), - (Some(Point::new(-3.14, 1.618)), "POINT(-3.14, 1.618)"), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_box_params() { - test_type( - "BOX", - &[ - ( - Some(Rect { - min: Coordinate { x: -3.14, y: 1.618 }, - max: Coordinate { - x: 160.0, - y: 69701.5615, - }, - }), - "BOX(POINT(160.0, 69701.5615), POINT(-3.14, 1.618))", - ), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_path_params() { - let points = vec![ - Coordinate { x: 0., y: 0. }, - Coordinate { x: -3.14, y: 1.618 }, - Coordinate { - x: 160.0, - y: 69701.5615, - }, - ]; - test_type( - "PATH", - &[ - ( - Some(LineString(points)), - "path '((0, 0), (-3.14, 1.618), (160.0, 69701.5615))'", - ), - (None, "NULL"), - ], - ); -} diff --git a/postgres/tests/types/mod.rs b/postgres/tests/types/mod.rs deleted file mode 100644 index 0a0be538..00000000 --- a/postgres/tests/types/mod.rs +++ /dev/null @@ -1,530 +0,0 @@ -use std::collections::HashMap; -use std::error; -use std::f32; -use std::f64; -use std::fmt; -use std::result; -use std::time::{Duration, UNIX_EPOCH}; - -use postgres::types::{FromSql, FromSqlOwned, IsNull, Kind, ToSql, Type, WrongType}; -use postgres::{Connection, TlsMode}; - -#[cfg(feature = "with-bit-vec-0.5")] -mod bit_vec; -#[cfg(feature = "with-chrono-0.4")] -mod chrono; -#[cfg(feature = "with-eui48-0.3")] -mod eui48; -#[cfg(feature = "with-geo-0.10")] -mod geo; -#[cfg(feature = "with-serde_json-1")] -mod serde_json; -#[cfg(feature = "with-uuid-0.6")] -mod uuid; - -fn test_type(sql_type: &str, checks: &[(T, S)]) -where - T: PartialEq + for<'a> FromSqlOwned + ToSql, - S: fmt::Display, -{ - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - for &(ref val, ref repr) in checks.iter() { - let stmt = or_panic!(conn.prepare(&*format!("SELECT {}::{}", *repr, sql_type))); - let rows = or_panic!(stmt.query(&[])); - let row = rows.iter().next().unwrap(); - let result = row.get(0); - assert_eq!(val, &result); - - let stmt = or_panic!(conn.prepare(&*format!("SELECT $1::{}", sql_type))); - let rows = or_panic!(stmt.query(&[val])); - let row = rows.iter().next().unwrap(); - let result = row.get(0); - assert_eq!(val, &result); - } -} - -#[test] -fn test_ref_tosql() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = conn.prepare("SELECT $1::Int").unwrap(); - let num: &ToSql = &&7; - stmt.query(&[num]).unwrap(); -} - -#[test] -fn test_bool_params() { - test_type( - "BOOL", - &[(Some(true), "'t'"), (Some(false), "'f'"), (None, "NULL")], - ); -} - -#[test] -fn test_i8_params() { - test_type("\"char\"", &[(Some('a' as i8), "'a'"), (None, "NULL")]); -} - -#[test] -fn test_name_params() { - test_type( - "NAME", - &[ - (Some("hello world".to_owned()), "'hello world'"), - ( - Some("イロハニホヘト チリヌルヲ".to_owned()), - "'イロハニホヘト チリヌルヲ'", - ), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_i16_params() { - test_type( - "SMALLINT", - &[ - (Some(15001i16), "15001"), - (Some(-15001i16), "-15001"), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_i32_params() { - test_type( - "INT", - &[ - (Some(2147483548i32), "2147483548"), - (Some(-2147483548i32), "-2147483548"), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_oid_params() { - test_type( - "OID", - &[ - (Some(2147483548u32), "2147483548"), - (Some(4000000000), "4000000000"), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_i64_params() { - test_type( - "BIGINT", - &[ - (Some(9223372036854775708i64), "9223372036854775708"), - (Some(-9223372036854775708i64), "-9223372036854775708"), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_f32_params() { - test_type( - "REAL", - &[ - (Some(f32::INFINITY), "'infinity'"), - (Some(f32::NEG_INFINITY), "'-infinity'"), - (Some(1000.55), "1000.55"), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_f64_params() { - test_type( - "DOUBLE PRECISION", - &[ - (Some(f64::INFINITY), "'infinity'"), - (Some(f64::NEG_INFINITY), "'-infinity'"), - (Some(10000.55), "10000.55"), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_varchar_params() { - test_type( - "VARCHAR", - &[ - (Some("hello world".to_owned()), "'hello world'"), - ( - Some("イロハニホヘト チリヌルヲ".to_owned()), - "'イロハニホヘト チリヌルヲ'", - ), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_text_params() { - test_type( - "TEXT", - &[ - (Some("hello world".to_owned()), "'hello world'"), - ( - Some("イロハニホヘト チリヌルヲ".to_owned()), - "'イロハニホヘト チリヌルヲ'", - ), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_borrowed_text() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - - let rows = or_panic!(conn.query("SELECT 'foo'", &[])); - let row = rows.get(0); - let s: &str = row.get(0); - assert_eq!(s, "foo"); -} - -#[test] -fn test_bpchar_params() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute( - "CREATE TEMPORARY TABLE foo ( - id SERIAL PRIMARY KEY, - b CHAR(5) - )", - &[], - )); - or_panic!(conn.execute( - "INSERT INTO foo (b) VALUES ($1), ($2), ($3)", - &[&Some("12345"), &Some("123"), &None::<&'static str>], - )); - let stmt = or_panic!(conn.prepare("SELECT b FROM foo ORDER BY id")); - let res = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![Some("12345".to_owned()), Some("123 ".to_owned()), None], - res.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -fn test_citext_params() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - or_panic!(conn.execute( - "CREATE TEMPORARY TABLE foo ( - id SERIAL PRIMARY KEY, - b CITEXT - )", - &[], - )); - or_panic!(conn.execute( - "INSERT INTO foo (b) VALUES ($1), ($2), ($3)", - &[&Some("foobar"), &Some("FooBar"), &None::<&'static str>], - )); - let stmt = or_panic!(conn.prepare("SELECT id FROM foo WHERE b = 'FOOBAR' ORDER BY id",)); - let res = or_panic!(stmt.query(&[])); - - assert_eq!( - vec![Some(1i32), Some(2i32)], - res.iter().map(|row| row.get(0)).collect::>() - ); -} - -#[test] -fn test_bytea_params() { - test_type( - "BYTEA", - &[ - (Some(vec![0u8, 1, 2, 3, 254, 255]), "'\\x00010203feff'"), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_borrowed_bytea() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - - let rows = or_panic!(conn.query("SELECT 'foo'::BYTEA", &[])); - let row = rows.get(0); - let s: &[u8] = row.get(0); - assert_eq!(s, b"foo"); -} - -#[test] -fn test_hstore_params() { - macro_rules! make_map { - ($($k:expr => $v:expr),+) => ({ - let mut map = HashMap::new(); - $(map.insert($k, $v);)+ - map - }) - } - test_type( - "hstore", - &[ - ( - Some(make_map!("a".to_owned() => Some("1".to_owned()))), - "'a=>1'", - ), - ( - Some(make_map!("hello".to_owned() => Some("world!".to_owned()), - "hola".to_owned() => Some("mundo!".to_owned()), - "what".to_owned() => None)), - "'hello=>world!,hola=>mundo!,what=>NULL'", - ), - (None, "NULL"), - ], - ); -} - -#[test] -fn test_array_params() { - test_type( - "integer[]", - &[ - (Some(vec![1i32, 2i32]), "ARRAY[1,2]"), - (Some(vec![1i32]), "ARRAY[1]"), - (Some(vec![]), "ARRAY[]"), - (None, "NULL"), - ], - ); -} - -fn test_nan_param(sql_type: &str) -where - T: PartialEq + ToSql + FromSqlOwned, -{ - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = or_panic!(conn.prepare(&*format!("SELECT 'NaN'::{}", sql_type))); - let result = or_panic!(stmt.query(&[])); - let val: T = result.iter().next().unwrap().get(0); - assert!(val != val); -} - -#[test] -fn test_f32_nan_param() { - test_nan_param::("REAL"); -} - -#[test] -fn test_f64_nan_param() { - test_nan_param::("DOUBLE PRECISION"); -} - -#[test] -fn test_pg_database_datname() { - let conn = or_panic!(Connection::connect( - "postgres://postgres@localhost:5433", - TlsMode::None, - )); - let stmt = or_panic!(conn.prepare("SELECT datname FROM pg_database")); - let result = or_panic!(stmt.query(&[])); - - let next = result.iter().next().unwrap(); - or_panic!(next.get_opt::<_, String>(0).unwrap()); - or_panic!(next.get_opt::<_, String>("datname").unwrap()); -} - -#[test] -fn test_slice() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - conn.simple_query( - "CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY, f VARCHAR); - INSERT INTO foo (f) VALUES ('a'), ('b'), ('c'), ('d');", - ).unwrap(); - - let stmt = conn - .prepare("SELECT f FROM foo WHERE id = ANY($1)") - .unwrap(); - let result = stmt.query(&[&&[1i32, 3, 4][..]]).unwrap(); - assert_eq!( - vec!["a".to_owned(), "c".to_owned(), "d".to_owned()], - result - .iter() - .map(|r| r.get::<_, String>(0)) - .collect::>() - ); -} - -#[test] -fn test_slice_wrong_type() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - conn.simple_query("CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY)") - .unwrap(); - - let stmt = conn - .prepare("SELECT * FROM foo WHERE id = ANY($1)") - .unwrap(); - let err = stmt.query(&[&&["hi"][..]]).unwrap_err(); - match err.as_conversion() { - Some(e) if e.is::() => {} - _ => panic!("Unexpected error {:?}", err), - }; -} - -#[test] -fn test_slice_range() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - - let stmt = conn.prepare("SELECT $1::INT8RANGE").unwrap(); - let err = stmt.query(&[&&[1i64][..]]).unwrap_err(); - match err.as_conversion() { - Some(e) if e.is::() => {} - _ => panic!("Unexpected error {:?}", err), - }; -} - -#[test] -fn domain() { - #[derive(Debug, PartialEq)] - struct SessionId(Vec); - - impl ToSql for SessionId { - fn to_sql( - &self, - ty: &Type, - out: &mut Vec, - ) -> result::Result> { - let inner = match *ty.kind() { - Kind::Domain(ref inner) => inner, - _ => unreachable!(), - }; - self.0.to_sql(inner, out) - } - - fn accepts(ty: &Type) -> bool { - ty.name() == "session_id" && match *ty.kind() { - Kind::Domain(_) => true, - _ => false, - } - } - - to_sql_checked!(); - } - - impl<'a> FromSql<'a> for SessionId { - fn from_sql( - ty: &Type, - raw: &[u8], - ) -> result::Result> { - Vec::::from_sql(ty, raw).map(SessionId) - } - - fn accepts(ty: &Type) -> bool { - // This is super weird! - as FromSql>::accepts(ty) - } - } - - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - conn.simple_query( - "CREATE DOMAIN pg_temp.session_id AS bytea CHECK(octet_length(VALUE) = 16); - CREATE TABLE pg_temp.foo (id pg_temp.session_id);", - ).unwrap(); - - let id = SessionId(b"0123456789abcdef".to_vec()); - conn.execute("INSERT INTO pg_temp.foo (id) VALUES ($1)", &[&id]) - .unwrap(); - let rows = conn.query("SELECT id FROM pg_temp.foo", &[]).unwrap(); - assert_eq!(id, rows.get(0).get(0)); -} - -#[test] -fn composite() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - conn.simple_query( - "CREATE TYPE pg_temp.inventory_item AS ( - name TEXT, - supplier INTEGER, - price NUMERIC - )", - ).unwrap(); - - let stmt = conn.prepare("SELECT $1::inventory_item").unwrap(); - let type_ = &stmt.param_types()[0]; - assert_eq!(type_.name(), "inventory_item"); - match *type_.kind() { - Kind::Composite(ref fields) => { - assert_eq!(fields[0].name(), "name"); - assert_eq!(fields[0].type_(), &Type::TEXT); - assert_eq!(fields[1].name(), "supplier"); - assert_eq!(fields[1].type_(), &Type::INT4); - assert_eq!(fields[2].name(), "price"); - assert_eq!(fields[2].type_(), &Type::NUMERIC); - } - ref t => panic!("bad type {:?}", t), - } -} - -#[test] -fn enum_() { - let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap(); - conn.simple_query("CREATE TYPE pg_temp.mood AS ENUM ('sad', 'ok', 'happy');") - .unwrap(); - - let stmt = conn.prepare("SELECT $1::mood").unwrap(); - let type_ = &stmt.param_types()[0]; - assert_eq!(type_.name(), "mood"); - match *type_.kind() { - Kind::Enum(ref variants) => { - assert_eq!( - variants, - &["sad".to_owned(), "ok".to_owned(), "happy".to_owned()] - ); - } - _ => panic!("bad type"), - } -} - -#[test] -fn system_time() { - test_type( - "TIMESTAMP", - &[ - ( - Some(UNIX_EPOCH + Duration::from_millis(1_010)), - "'1970-01-01 00:00:01.01'", - ), - ( - Some(UNIX_EPOCH - Duration::from_millis(1_010)), - "'1969-12-31 23:59:58.99'", - ), - ( - Some(UNIX_EPOCH + Duration::from_millis(946684800 * 1000 + 1_010)), - "'2000-01-01 00:00:01.01'", - ), - (None, "NULL"), - ], - ); -} diff --git a/postgres/tests/types/serde_json.rs b/postgres/tests/types/serde_json.rs deleted file mode 100644 index bf62a8cb..00000000 --- a/postgres/tests/types/serde_json.rs +++ /dev/null @@ -1,40 +0,0 @@ -extern crate serde_json; - -use self::serde_json::Value; -use types::test_type; - -#[test] -fn test_json_params() { - test_type( - "JSON", - &[ - ( - Some(serde_json::from_str::("[10, 11, 12]").unwrap()), - "'[10, 11, 12]'", - ), - ( - Some(serde_json::from_str::("{\"f\": \"asd\"}").unwrap()), - "'{\"f\": \"asd\"}'", - ), - (None, "NULL"), - ], - ) -} - -#[test] -fn test_jsonb_params() { - test_type( - "JSONB", - &[ - ( - Some(serde_json::from_str::("[10, 11, 12]").unwrap()), - "'[10, 11, 12]'", - ), - ( - Some(serde_json::from_str::("{\"f\": \"asd\"}").unwrap()), - "'{\"f\": \"asd\"}'", - ), - (None, "NULL"), - ], - ) -} diff --git a/postgres/tests/types/uuid.rs b/postgres/tests/types/uuid.rs deleted file mode 100644 index d1b995ad..00000000 --- a/postgres/tests/types/uuid.rs +++ /dev/null @@ -1,17 +0,0 @@ -extern crate uuid; - -use types::test_type; - -#[test] -fn test_uuid_params() { - test_type( - "UUID", - &[ - ( - Some(uuid::Uuid::parse_str("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11").unwrap()), - "'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'", - ), - (None, "NULL"), - ], - ) -}