├── .gitignore ├── .travis ├── server.der ├── setup.sql ├── setup.sh ├── pg_hba.conf ├── server.key └── server.crt ├── codegen ├── Cargo.toml └── src │ ├── main.rs │ ├── pg_range.h │ ├── sqlstate.rs │ └── types.rs ├── tests └── types │ ├── eui48.rs │ ├── uuid.rs │ ├── bit_vec.rs │ ├── rustc_serialize.rs │ ├── serde_json.rs │ ├── time.rs │ ├── chrono.rs │ └── mod.rs ├── .travis.yml ├── src ├── types │ ├── slice.rs │ ├── uuid.rs │ ├── eui48.rs │ ├── bit_vec.rs │ ├── serde_json.rs │ ├── rustc_serialize.rs │ ├── time.rs │ ├── special.rs │ └── chrono.rs ├── io │ ├── openssl.rs │ ├── security_framework.rs │ └── mod.rs ├── macros.rs ├── notification.rs ├── priv_io.rs ├── transaction.rs ├── error │ └── mod.rs ├── rows.rs ├── url.rs ├── message.rs ├── md5.rs └── stmt.rs ├── benches └── bench.rs ├── LICENSE ├── Cargo.toml ├── THIRD_PARTY └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .cargo/ 4 | -------------------------------------------------------------------------------- /.travis/server.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hy0kl/rust-postgres/master/.travis/server.der -------------------------------------------------------------------------------- /.travis/setup.sql: -------------------------------------------------------------------------------- 1 | CREATE ROLE pass_user PASSWORD 'password' LOGIN; 2 | CREATE ROLE md5_user PASSWORD 'password' LOGIN; 3 | CREATE EXTENSION hstore; 4 | CREATE EXTENSION citext; 5 | -------------------------------------------------------------------------------- /codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "codegen" 3 | version = "0.1.0" 4 | authors = ["Steven Fackler "] 5 | 6 | [dependencies] 7 | phf_codegen = "=0.7.15" 8 | regex = "0.1" 9 | marksman_escape = "0.1" 10 | -------------------------------------------------------------------------------- /tests/types/eui48.rs: -------------------------------------------------------------------------------- 1 | extern crate eui48; 2 | 3 | use types::test_type; 4 | 5 | #[test] 6 | fn test_eui48_params() { 7 | test_type("MACADDR", &[(Some(eui48::MacAddress::parse_str("12-34-56-AB-CD-EF").unwrap()), 8 | "'12-34-56-ab-cd-ef'"), (None, "NULL")]) 9 | } 10 | -------------------------------------------------------------------------------- /.travis/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd "$(dirname "$0")" 5 | 6 | psql -U postgres < setup.sql 7 | 8 | sudo cp pg_hba.conf $(psql -U postgres -c "SHOW hba_file" -At) 9 | 10 | DATA_DIR=$(psql -U postgres -c "SHOW data_directory" -At) 11 | PG_PID=$(sudo head -n1 $DATA_DIR/postmaster.pid) 12 | sudo kill -SIGHUP $PG_PID 13 | -------------------------------------------------------------------------------- /tests/types/uuid.rs: -------------------------------------------------------------------------------- 1 | extern crate uuid; 2 | 3 | use types::test_type; 4 | 5 | #[test] 6 | fn test_uuid_params() { 7 | test_type("UUID", &[(Some(uuid::Uuid::parse_str("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11").unwrap()), 8 | "'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'"), 9 | (None, "NULL")]) 10 | } 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | sudo: required 4 | rust: 5 | - nightly 6 | - beta 7 | - 1.9.0 8 | addons: 9 | postgresql: 9.4 10 | before_script: 11 | - "./.travis/setup.sh" 12 | script: 13 | - cargo test 14 | - cargo test --features "uuid rustc-serialize time unix_socket serde_json chrono openssl bit-vec eui48" 15 | - (test $TRAVIS_RUST_VERSION != "nightly" || cargo test --features nightly) 16 | -------------------------------------------------------------------------------- /src/types/slice.rs: -------------------------------------------------------------------------------- 1 | use std::io::prelude::*; 2 | 3 | use Result; 4 | use types::{Type, ToSql, IsNull, SessionInfo}; 5 | 6 | /// # Deprecated 7 | /// 8 | /// `ToSql` is now implemented directly for slices. 9 | #[derive(Debug)] 10 | pub struct Slice<'a, T: 'a + ToSql>(pub &'a [T]); 11 | 12 | impl<'a, T: 'a + ToSql> ToSql for Slice<'a, T> { 13 | fn to_sql(&self, ty: &Type, w: &mut W, ctx: &SessionInfo) -> Result { 14 | self.0.to_sql(ty, w, ctx) 15 | } 16 | 17 | fn accepts(ty: &Type) -> bool { 18 | <&[T] as ToSql>::accepts(ty) 19 | } 20 | 21 | to_sql_checked!(); 22 | } 23 | -------------------------------------------------------------------------------- /.travis/pg_hba.conf: -------------------------------------------------------------------------------- 1 | # TYPE DATABASE USER ADDRESS METHOD 2 | host all pass_user 127.0.0.1/32 password 3 | host all md5_user 127.0.0.1/32 md5 4 | host all pass_user ::1/128 password 5 | host all md5_user ::1/128 md5 6 | 7 | # IPv4 local connections: 8 | host all postgres 127.0.0.1/32 trust 9 | # IPv6 local connections: 10 | host all postgres ::1/128 trust 11 | # Unix socket connections: 12 | local all postgres trust 13 | -------------------------------------------------------------------------------- /tests/types/bit_vec.rs: -------------------------------------------------------------------------------- 1 | extern crate bit_vec; 2 | 3 | use self::bit_vec::BitVec; 4 | use types::test_type; 5 | 6 | #[test] 7 | fn test_bit_params() { 8 | let mut bv = BitVec::from_bytes(&[0b0110_1001, 0b0000_0111]); 9 | bv.pop(); 10 | bv.pop(); 11 | test_type("BIT(14)", &[(Some(bv), "B'01101001000001'"), 12 | (None, "NULL")]) 13 | } 14 | 15 | #[test] 16 | fn test_varbit_params() { 17 | let mut bv = BitVec::from_bytes(&[0b0110_1001, 0b0000_0111]); 18 | bv.pop(); 19 | bv.pop(); 20 | test_type("VARBIT", &[(Some(bv), "B'01101001000001'"), 21 | (Some(BitVec::from_bytes(&[])), "B''"), 22 | (None, "NULL")]) 23 | } 24 | -------------------------------------------------------------------------------- /src/io/openssl.rs: -------------------------------------------------------------------------------- 1 | extern crate openssl; 2 | 3 | use std::error::Error; 4 | 5 | use self::openssl::ssl::{SslContext, SslStream}; 6 | use io::{StreamWrapper, Stream, NegotiateSsl}; 7 | 8 | impl StreamWrapper for SslStream { 9 | fn get_ref(&self) -> &Stream { 10 | self.get_ref() 11 | } 12 | 13 | fn get_mut(&mut self) -> &mut Stream { 14 | self.get_mut() 15 | } 16 | } 17 | 18 | impl NegotiateSsl for SslContext { 19 | fn negotiate_ssl(&self, 20 | _: &str, 21 | stream: Stream) 22 | -> Result, Box> { 23 | let stream = try!(SslStream::connect(self, stream)); 24 | Ok(Box::new(stream)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! try_desync { 2 | ($s:expr, $e:expr) => ( 3 | match $e { 4 | Ok(ok) => ok, 5 | Err(err) => { 6 | $s.desynchronized = true; 7 | return Err(::std::convert::From::from(err)); 8 | } 9 | } 10 | ) 11 | } 12 | 13 | macro_rules! check_desync { 14 | ($e:expr) => ({ 15 | if $e.is_desynchronized() { 16 | return Err(::error::Error::Io(::desynchronized())); 17 | } 18 | }) 19 | } 20 | 21 | macro_rules! bad_response { 22 | ($s:expr) => ({ 23 | debug!("Bad response at {}:{}", file!(), line!()); 24 | $s.desynchronized = true; 25 | return Err(::error::Error::Io(::bad_response())); 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /src/types/uuid.rs: -------------------------------------------------------------------------------- 1 | extern crate uuid; 2 | 3 | use std::io::prelude::*; 4 | 5 | use self::uuid::Uuid; 6 | use types::{FromSql, ToSql, Type, IsNull, SessionInfo}; 7 | use Result; 8 | 9 | impl FromSql for Uuid { 10 | fn from_sql(_: &Type, raw: &mut R, _: &SessionInfo) -> Result { 11 | let mut bytes = [0; 16]; 12 | try!(raw.read_exact(&mut bytes)); 13 | Ok(Uuid::from_bytes(&bytes).unwrap()) 14 | } 15 | 16 | accepts!(Type::Uuid); 17 | } 18 | 19 | impl ToSql for Uuid { 20 | fn to_sql(&self, _: &Type, w: &mut W, _: &SessionInfo) -> Result { 21 | try!(w.write_all(self.as_bytes())); 22 | Ok(IsNull::No) 23 | } 24 | 25 | accepts!(Type::Uuid); 26 | to_sql_checked!(); 27 | } 28 | -------------------------------------------------------------------------------- /src/types/eui48.rs: -------------------------------------------------------------------------------- 1 | extern crate eui48; 2 | 3 | use std::io::prelude::*; 4 | 5 | use self::eui48::MacAddress; 6 | 7 | use types::{FromSql, ToSql, Type, IsNull, SessionInfo}; 8 | use Result; 9 | 10 | impl FromSql for MacAddress { 11 | fn from_sql(_: &Type, raw: &mut R, _: &SessionInfo) -> Result { 12 | let mut bytes = [0; 6]; 13 | try!(raw.read_exact(&mut bytes)); 14 | Ok(MacAddress::new(bytes)) 15 | } 16 | 17 | accepts!(Type::Macaddr); 18 | } 19 | 20 | impl ToSql for MacAddress { 21 | fn to_sql(&self, _: &Type, w: &mut W, _: &SessionInfo) -> Result { 22 | try!(w.write_all(self.as_bytes())); 23 | Ok(IsNull::No) 24 | } 25 | 26 | accepts!(Type::Macaddr); 27 | to_sql_checked!(); 28 | } 29 | -------------------------------------------------------------------------------- /src/io/security_framework.rs: -------------------------------------------------------------------------------- 1 | extern crate security_framework; 2 | 3 | use self::security_framework::secure_transport::{SslStream, ClientBuilder}; 4 | use io::{Stream, StreamWrapper, NegotiateSsl}; 5 | use std::error::Error; 6 | 7 | impl StreamWrapper for SslStream { 8 | fn get_ref(&self) -> &Stream { 9 | self.get_ref() 10 | } 11 | 12 | fn get_mut(&mut self) -> &mut Stream { 13 | self.get_mut() 14 | } 15 | } 16 | 17 | impl NegotiateSsl for ClientBuilder { 18 | fn negotiate_ssl(&self, 19 | domain: &str, 20 | stream: Stream) 21 | -> Result, Box> { 22 | let stream = try!(self.handshake(domain, stream)); 23 | Ok(Box::new(stream)) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /codegen/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate phf_codegen; 2 | extern crate regex; 3 | extern crate marksman_escape; 4 | 5 | use std::ascii::AsciiExt; 6 | use std::path::Path; 7 | 8 | mod sqlstate; 9 | mod types; 10 | 11 | fn main() { 12 | let path = Path::new("../src"); 13 | sqlstate::build(path); 14 | types::build(path); 15 | } 16 | 17 | fn snake_to_camel(s: &str) -> String { 18 | let mut out = String::new(); 19 | 20 | let mut upper = true; 21 | for ch in s.chars() { 22 | if ch == '_' { 23 | upper = true; 24 | } else { 25 | let ch = if upper { 26 | upper = false; 27 | ch.to_ascii_uppercase() 28 | } else { 29 | ch 30 | }; 31 | out.push(ch); 32 | } 33 | } 34 | 35 | out 36 | } 37 | -------------------------------------------------------------------------------- /tests/types/rustc_serialize.rs: -------------------------------------------------------------------------------- 1 | extern crate rustc_serialize; 2 | 3 | use self::rustc_serialize::json::Json; 4 | 5 | use types::test_type; 6 | 7 | #[test] 8 | fn test_json_params() { 9 | test_type("JSON", &[(Some(Json::from_str("[10, 11, 12]").unwrap()), 10 | "'[10, 11, 12]'"), 11 | (Some(Json::from_str("{\"f\": \"asd\"}").unwrap()), 12 | "'{\"f\": \"asd\"}'"), 13 | (None, "NULL")]) 14 | } 15 | 16 | #[test] 17 | fn test_jsonb_params() { 18 | test_type("JSONB", &[(Some(Json::from_str("[10, 11, 12]").unwrap()), 19 | "'[10, 11, 12]'"), 20 | (Some(Json::from_str("{\"f\": \"asd\"}").unwrap()), 21 | "'{\"f\": \"asd\"}'"), 22 | (None, "NULL")]) 23 | } 24 | -------------------------------------------------------------------------------- /tests/types/serde_json.rs: -------------------------------------------------------------------------------- 1 | extern crate serde_json; 2 | 3 | use self::serde_json::Value; 4 | use types::test_type; 5 | 6 | #[test] 7 | fn test_json_params() { 8 | test_type("JSON", &[(Some(serde_json::from_str::("[10, 11, 12]").unwrap()), 9 | "'[10, 11, 12]'"), 10 | (Some(serde_json::from_str::("{\"f\": \"asd\"}").unwrap()), 11 | "'{\"f\": \"asd\"}'"), 12 | (None, "NULL")]) 13 | } 14 | 15 | #[test] 16 | fn test_jsonb_params() { 17 | test_type("JSONB", &[(Some(serde_json::from_str::("[10, 11, 12]").unwrap()), 18 | "'[10, 11, 12]'"), 19 | (Some(serde_json::from_str::("{\"f\": \"asd\"}").unwrap()), 20 | "'{\"f\": \"asd\"}'"), 21 | (None, "NULL")]) 22 | } 23 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | extern crate test; 2 | extern crate postgres; 3 | 4 | use test::Bencher; 5 | 6 | use postgres::{Connection, SslMode}; 7 | 8 | #[bench] 9 | fn bench_naiive_execute(b: &mut test::Bencher) { 10 | let conn = Connection::connect("postgres://postgres@localhost", &SslMode::None).unwrap(); 11 | conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[]).unwrap(); 12 | 13 | b.iter(|| { 14 | let stmt = conn.prepare("UPDATE foo SET id = 1").unwrap(); 15 | let out = stmt.execute(&[]).unwrap(); 16 | stmt.finish().unwrap(); 17 | out 18 | }); 19 | } 20 | 21 | #[bench] 22 | fn bench_execute(b: &mut test::Bencher) { 23 | let conn = Connection::connect("postgres://postgres@localhost", &SslMode::None).unwrap(); 24 | conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[]).unwrap(); 25 | 26 | b.iter(|| { 27 | conn.execute("UPDATE foo SET id = 1", &[]).unwrap() 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /.travis/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDxm7YkZGa/Xtp3Kjm9OZNWKCbx1x/JYBzjgqQHog/I1mj8MC8X 3 | MDRpzdjx58eE+cWQsSxC1SMg0h3XGBVwCqMdxy7fA8CfXMsCJdp9KxoJeOUjisRk 4 | OVkNThULdXt1+YpMw535MQjV2gCl2wzfCeXkFNEXD7v2z708XRRqy8Hc4QIDAQAB 5 | AoGBAIBsJuWzJFYmQfNDU4t8Fg+eqgy0LyYn21Mm9q9D+iXjqcwahH1L1yBCFUWH 6 | 0Kqi5NujAQbJKbHhXZEeMQ7r6IT8HjAW800F+M3eRLaMGVbh02L/EpEgUspb8VH+ 7 | SZDolJvxCGmkBBgglJwYpFQG6ANXaEU0/uS+aHz0Wptip2NNAkEA+UdCmpY7whXS 8 | 5F3LrZE8qjwjEs86RxQoe7+wF7eT4CbXmxvQBwgxMO9ZUhwdUJ3Cm5T4Qu943gp/ 9 | hiRIXunrdwJBAPgfgWNE1KpmJALr3opq+mu92D6YWk2aLFQj01kJI1lomRq/ptXB 10 | niMPzzvauiFuNgpGtKKoxzBPM3l8Ii5E4GcCQCBTuHR5tSg3UlEhRM+ufRKKl/XR 11 | f/pFx/Y8Zqa8vOWdw+oukizHSDHTaF74nGie/OTWTdfIXIFXFTCdNfFxHoMCQQDs 12 | k2WT1/IJkp/tZSXnxn6Esht3+13GtiRkCVCfiRX6TsAEgA27rANynMVT5YYpD+NY 13 | wvfCS7i4OBv1TkVs5mErAkAQmGseTKaye5ABFxBOEHT00hRtIE0yojuL6oPEDhkk 14 | SJIBC5XE0vzmMKq9sQ7foqgPork9O4VYBo0q//BO0RWG 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2016 Steven Fackler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/types/bit_vec.rs: -------------------------------------------------------------------------------- 1 | extern crate bit_vec; 2 | 3 | use std::io::prelude::*; 4 | use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; 5 | use self::bit_vec::BitVec; 6 | 7 | use Result; 8 | use types::{FromSql, ToSql, IsNull, Type, SessionInfo, downcast}; 9 | 10 | impl FromSql for BitVec { 11 | fn from_sql(_: &Type, raw: &mut R, _: &SessionInfo) -> Result { 12 | let len = try!(raw.read_i32::()) as usize; 13 | let mut bytes = vec![]; 14 | try!(raw.take(((len + 7) / 8) as u64).read_to_end(&mut bytes)); 15 | 16 | let mut bitvec = BitVec::from_bytes(&bytes); 17 | while bitvec.len() > len { 18 | bitvec.pop(); 19 | } 20 | 21 | Ok(bitvec) 22 | } 23 | 24 | accepts!(Type::Bit, Type::Varbit); 25 | } 26 | 27 | impl ToSql for BitVec { 28 | fn to_sql(&self, 29 | _: &Type, 30 | mut out: &mut W, 31 | _: &SessionInfo) 32 | -> Result { 33 | try!(out.write_i32::(try!(downcast(self.len())))); 34 | try!(out.write_all(&self.to_bytes())); 35 | 36 | Ok(IsNull::No) 37 | } 38 | 39 | accepts!(Type::Bit, Type::Varbit); 40 | to_sql_checked!(); 41 | } 42 | -------------------------------------------------------------------------------- /src/types/serde_json.rs: -------------------------------------------------------------------------------- 1 | extern crate serde_json; 2 | 3 | use std::io::prelude::*; 4 | use byteorder::{ReadBytesExt, WriteBytesExt}; 5 | use self::serde_json::Value; 6 | 7 | use Result; 8 | use error::Error; 9 | use types::{FromSql, ToSql, IsNull, Type, SessionInfo}; 10 | 11 | impl FromSql for Value { 12 | fn from_sql(ty: &Type, raw: &mut R, _: &SessionInfo) -> Result { 13 | if let Type::Jsonb = *ty { 14 | // We only support version 1 of the jsonb binary format 15 | if try!(raw.read_u8()) != 1 { 16 | return Err(Error::Conversion("unsupported JSONB encoding version".into())); 17 | } 18 | } 19 | serde_json::de::from_reader(raw).map_err(|err| Error::Conversion(Box::new(err))) 20 | } 21 | 22 | accepts!(Type::Json, Type::Jsonb); 23 | } 24 | 25 | impl ToSql for Value { 26 | fn to_sql(&self, 27 | ty: &Type, 28 | mut out: &mut W, 29 | _: &SessionInfo) 30 | -> Result { 31 | if let Type::Jsonb = *ty { 32 | try!(out.write_u8(1)); 33 | } 34 | 35 | try!(write!(out, "{:?}", self)); 36 | 37 | Ok(IsNull::No) 38 | } 39 | 40 | accepts!(Type::Json, Type::Jsonb); 41 | to_sql_checked!(); 42 | } 43 | -------------------------------------------------------------------------------- /src/types/rustc_serialize.rs: -------------------------------------------------------------------------------- 1 | extern crate rustc_serialize; 2 | 3 | use self::rustc_serialize::json; 4 | use std::io::prelude::*; 5 | use byteorder::{ReadBytesExt, WriteBytesExt}; 6 | 7 | use Result; 8 | use error::Error; 9 | use types::{FromSql, ToSql, IsNull, Type, SessionInfo}; 10 | 11 | impl FromSql for json::Json { 12 | fn from_sql(ty: &Type, raw: &mut R, _: &SessionInfo) -> Result { 13 | if let Type::Jsonb = *ty { 14 | // We only support version 1 of the jsonb binary format 15 | if try!(raw.read_u8()) != 1 { 16 | return Err(Error::Conversion("unsupported JSONB encoding version".into())); 17 | } 18 | } 19 | json::Json::from_reader(raw).map_err(|err| Error::Conversion(Box::new(err))) 20 | } 21 | 22 | accepts!(Type::Json, Type::Jsonb); 23 | } 24 | 25 | impl ToSql for json::Json { 26 | fn to_sql(&self, 27 | ty: &Type, 28 | mut out: &mut W, 29 | _: &SessionInfo) 30 | -> Result { 31 | if let Type::Jsonb = *ty { 32 | try!(out.write_u8(1)); 33 | } 34 | 35 | try!(write!(out, "{}", self)); 36 | 37 | Ok(IsNull::No) 38 | } 39 | 40 | accepts!(Type::Json, Type::Jsonb); 41 | to_sql_checked!(); 42 | } 43 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "postgres" 3 | version = "0.11.10" 4 | authors = ["Steven Fackler "] 5 | license = "MIT" 6 | description = "A native PostgreSQL driver" 7 | repository = "https://github.com/sfackler/rust-postgres" 8 | documentation = "https://sfackler.github.io/rust-postgres/doc/v0.11.10/postgres" 9 | readme = "README.md" 10 | keywords = ["database", "postgres", "postgresql", "sql"] 11 | include = ["src/*", "Cargo.toml", "LICENSE", "README.md", "THIRD_PARTY"] 12 | 13 | [lib] 14 | name = "postgres" 15 | path = "src/lib.rs" 16 | test = false 17 | bench = false 18 | 19 | [[test]] 20 | name = "test" 21 | path = "tests/test.rs" 22 | 23 | [features] 24 | nightly = [] 25 | 26 | [dependencies] 27 | bufstream = "0.1" 28 | byteorder = "0.5" 29 | log = "0.3" 30 | phf = "=0.7.15" 31 | hex = "0.2" 32 | rustc-serialize = { version = "0.3", optional = true } 33 | chrono = { version = "0.2.14", optional = true } 34 | openssl = { version = ">= 0.6.4, < 0.8", optional = true } 35 | serde_json = { version = ">= 0.6, < 0.8", optional = true } 36 | time = { version = "0.1.14", optional = true } 37 | unix_socket = { version = "0.5", optional = true } 38 | uuid = { version = ">= 0.1, < 0.3", optional = true } 39 | security-framework = { version = "0.1.2", optional = true } 40 | bit-vec = { version = "0.4", optional = true } 41 | eui48 = { version = "0.1", optional = true } 42 | 43 | [dev-dependencies] 44 | url = "1.0" 45 | -------------------------------------------------------------------------------- /src/io/mod.rs: -------------------------------------------------------------------------------- 1 | //! Types and traits for SSL adaptors. 2 | pub use priv_io::Stream; 3 | 4 | use std::error::Error; 5 | use std::io::prelude::*; 6 | use std::fmt; 7 | 8 | #[cfg(feature = "openssl")] 9 | mod openssl; 10 | #[cfg(feature = "security-framework")] 11 | mod security_framework; 12 | 13 | /// A trait implemented by SSL adaptors. 14 | pub trait StreamWrapper: fmt::Debug + Read + Write + Send { 15 | /// Returns a reference to the underlying `Stream`. 16 | fn get_ref(&self) -> &Stream; 17 | 18 | /// Returns a mutable reference to the underlying `Stream`. 19 | fn get_mut(&mut self) -> &mut Stream; 20 | } 21 | 22 | /// A trait implemented by types that can negotiate SSL over a Postgres stream. 23 | /// 24 | /// If the `openssl` Cargo feature is enabled, this trait will be implemented 25 | /// for `openssl::ssl::SslContext`. 26 | /// 27 | /// If the `security-framework` Cargo feature is enabled, this trait will be 28 | /// implemented for `security_framework::secure_transport::ClientBuilder`. 29 | pub trait NegotiateSsl: fmt::Debug { 30 | /// Negotiates an SSL session, returning a wrapper around the provided 31 | /// stream. 32 | /// 33 | /// The host portion of the connection parameters is provided for hostname 34 | /// verification. 35 | fn negotiate_ssl(&self, 36 | host: &str, 37 | stream: Stream) 38 | -> Result, Box>; 39 | } 40 | -------------------------------------------------------------------------------- /src/types/time.rs: -------------------------------------------------------------------------------- 1 | extern crate time; 2 | 3 | use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; 4 | use self::time::Timespec; 5 | use std::io::prelude::*; 6 | 7 | use Result; 8 | use types::{Type, FromSql, ToSql, IsNull, SessionInfo}; 9 | 10 | const USEC_PER_SEC: i64 = 1_000_000; 11 | const NSEC_PER_USEC: i64 = 1_000; 12 | 13 | // Number of seconds from 1970-01-01 to 2000-01-01 14 | const TIME_SEC_CONVERSION: i64 = 946684800; 15 | 16 | impl FromSql for Timespec { 17 | fn from_sql(_: &Type, raw: &mut R, _: &SessionInfo) -> Result { 18 | let t = try!(raw.read_i64::()); 19 | let mut sec = t / USEC_PER_SEC + TIME_SEC_CONVERSION; 20 | let mut usec = t % USEC_PER_SEC; 21 | 22 | if usec < 0 { 23 | sec -= 1; 24 | usec = USEC_PER_SEC + usec; 25 | } 26 | 27 | Ok(Timespec::new(sec, (usec * NSEC_PER_USEC) as i32)) 28 | } 29 | 30 | accepts!(Type::Timestamp, Type::TimestampTZ); 31 | } 32 | 33 | impl ToSql for Timespec { 34 | fn to_sql(&self, 35 | _: &Type, 36 | mut w: &mut W, 37 | _: &SessionInfo) 38 | -> Result { 39 | let t = (self.sec - TIME_SEC_CONVERSION) * USEC_PER_SEC + self.nsec as i64 / NSEC_PER_USEC; 40 | try!(w.write_i64::(t)); 41 | Ok(IsNull::No) 42 | } 43 | 44 | accepts!(Type::Timestamp, Type::TimestampTZ); 45 | to_sql_checked!(); 46 | } 47 | -------------------------------------------------------------------------------- /tests/types/time.rs: -------------------------------------------------------------------------------- 1 | extern crate time; 2 | 3 | use self::time::Timespec; 4 | use types::test_type; 5 | 6 | use postgres::types::Timestamp; 7 | 8 | #[test] 9 | fn test_tm_params() { 10 | fn make_check<'a>(time: &'a str) -> (Option, &'a str) { 11 | (Some(time::strptime(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap().to_timespec()), time) 12 | } 13 | test_type("TIMESTAMP", 14 | &[make_check("'1970-01-01 00:00:00.01'"), 15 | make_check("'1965-09-25 11:19:33.100314'"), 16 | make_check("'2010-02-09 23:11:45.1202'"), 17 | (None, "NULL")]); 18 | test_type("TIMESTAMP WITH TIME ZONE", 19 | &[make_check("'1970-01-01 00:00:00.01'"), 20 | make_check("'1965-09-25 11:19:33.100314'"), 21 | make_check("'2010-02-09 23:11:45.1202'"), 22 | (None, "NULL")]); 23 | } 24 | 25 | #[test] 26 | fn test_with_special_tm_params() { 27 | fn make_check<'a>(time: &'a str) -> (Timestamp, &'a str) { 28 | (Timestamp::Value(time::strptime(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap().to_timespec()), 29 | time) 30 | } 31 | test_type("TIMESTAMP", 32 | &[make_check("'1970-01-01 00:00:00.01'"), 33 | make_check("'1965-09-25 11:19:33.100314'"), 34 | make_check("'2010-02-09 23:11:45.1202'"), 35 | (Timestamp::PosInfinity, "'infinity'"), 36 | (Timestamp::NegInfinity, "'-infinity'")]); 37 | test_type("TIMESTAMP WITH TIME ZONE", 38 | &[make_check("'1970-01-01 00:00:00.01'"), 39 | make_check("'1965-09-25 11:19:33.100314'"), 40 | make_check("'2010-02-09 23:11:45.1202'"), 41 | (Timestamp::PosInfinity, "'infinity'"), 42 | (Timestamp::NegInfinity, "'-infinity'")]); 43 | } 44 | -------------------------------------------------------------------------------- /codegen/src/pg_range.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pg_range.h 4 | * definition of the system "range" relation (pg_range) 5 | * along with the relation's initial contents. 6 | * 7 | * 8 | * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group 9 | * Portions Copyright (c) 1994, Regents of the University of California 10 | * 11 | * src/include/catalog/pg_range.h 12 | * 13 | * NOTES 14 | * the genbki.pl script reads this file and generates .bki 15 | * information from the DATA() statements. 16 | * 17 | * XXX do NOT break up DATA() statements into multiple lines! 18 | * the scripts are not as smart as you might think... 19 | * 20 | *------------------------------------------------------------------------- 21 | */ 22 | #ifndef PG_RANGE_H 23 | #define PG_RANGE_H 24 | 25 | #include "catalog/genbki.h" 26 | 27 | /* ---------------- 28 | * pg_range definition. cpp turns this into 29 | * typedef struct FormData_pg_range 30 | * ---------------- 31 | */ 32 | #define RangeRelationId 3541 33 | 34 | CATALOG(pg_range,3541) BKI_WITHOUT_OIDS 35 | { 36 | Oid rngtypid; /* OID of owning range type */ 37 | Oid rngsubtype; /* OID of range's element type (subtype) */ 38 | Oid rngcollation; /* collation for this range type, or 0 */ 39 | Oid rngsubopc; /* subtype's btree opclass */ 40 | regproc rngcanonical; /* canonicalize range, or 0 */ 41 | regproc rngsubdiff; /* subtype difference as a float8, or 0 */ 42 | } FormData_pg_range; 43 | 44 | /* ---------------- 45 | * Form_pg_range corresponds to a pointer to a tuple with 46 | * the format of pg_range relation. 47 | * ---------------- 48 | */ 49 | typedef FormData_pg_range *Form_pg_range; 50 | 51 | /* ---------------- 52 | * compiler constants for pg_range 53 | * ---------------- 54 | */ 55 | #define Natts_pg_range 6 56 | #define Anum_pg_range_rngtypid 1 57 | #define Anum_pg_range_rngsubtype 2 58 | #define Anum_pg_range_rngcollation 3 59 | #define Anum_pg_range_rngsubopc 4 60 | #define Anum_pg_range_rngcanonical 5 61 | #define Anum_pg_range_rngsubdiff 6 62 | 63 | 64 | /* ---------------- 65 | * initial contents of pg_range 66 | * ---------------- 67 | */ 68 | DATA(insert ( 3904 23 0 1978 int4range_canonical int4range_subdiff)); 69 | DATA(insert ( 3906 1700 0 3125 - numrange_subdiff)); 70 | DATA(insert ( 3908 1114 0 3128 - tsrange_subdiff)); 71 | DATA(insert ( 3910 1184 0 3127 - tstzrange_subdiff)); 72 | DATA(insert ( 3912 1082 0 3122 daterange_canonical daterange_subdiff)); 73 | DATA(insert ( 3926 20 0 3124 int8range_canonical int8range_subdiff)); 74 | 75 | 76 | /* 77 | * prototypes for functions in pg_range.c 78 | */ 79 | 80 | extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation, 81 | Oid rangeSubOpclass, RegProcedure rangeCanonical, 82 | RegProcedure rangeSubDiff); 83 | extern void RangeDelete(Oid rangeTypeOid); 84 | 85 | #endif /* PG_RANGE_H */ 86 | -------------------------------------------------------------------------------- /.travis/server.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 | 9a:e5:7a:5f:05:5a:2f:e4 6 | Signature Algorithm: sha1WithRSAEncryption 7 | Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=localhost 8 | Validity 9 | Not Before: Dec 5 21:50:46 2015 GMT 10 | Not After : Jan 4 21:50:46 2016 GMT 11 | Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=localhost 12 | Subject Public Key Info: 13 | Public Key Algorithm: rsaEncryption 14 | RSA Public Key: (1024 bit) 15 | Modulus (1024 bit): 16 | 00:f1:9b:b6:24:64:66:bf:5e:da:77:2a:39:bd:39: 17 | 93:56:28:26:f1:d7:1f:c9:60:1c:e3:82:a4:07:a2: 18 | 0f:c8:d6:68:fc:30:2f:17:30:34:69:cd:d8:f1:e7: 19 | c7:84:f9:c5:90:b1:2c:42:d5:23:20:d2:1d:d7:18: 20 | 15:70:0a:a3:1d:c7:2e:df:03:c0:9f:5c:cb:02:25: 21 | da:7d:2b:1a:09:78:e5:23:8a:c4:64:39:59:0d:4e: 22 | 15:0b:75:7b:75:f9:8a:4c:c3:9d:f9:31:08:d5:da: 23 | 00:a5:db:0c:df:09:e5:e4:14:d1:17:0f:bb:f6:cf: 24 | bd:3c:5d:14:6a:cb:c1:dc:e1 25 | Exponent: 65537 (0x10001) 26 | X509v3 extensions: 27 | X509v3 Subject Key Identifier: 28 | 9E:09:C0:D1:1E:0E:07:B3:49:57:0A:49:47:F9:8A:5F:4E:FE:23:75 29 | X509v3 Authority Key Identifier: 30 | keyid:9E:09:C0:D1:1E:0E:07:B3:49:57:0A:49:47:F9:8A:5F:4E:FE:23:75 31 | DirName:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=localhost 32 | serial:9A:E5:7A:5F:05:5A:2F:E4 33 | 34 | X509v3 Basic Constraints: 35 | CA:TRUE 36 | Signature Algorithm: sha1WithRSAEncryption 37 | 4c:3b:c6:42:96:75:96:a0:9b:f5:d9:b1:9b:1b:4f:bd:d2:8d: 38 | f1:53:ed:87:80:f5:7b:5d:36:6e:38:c8:ae:1a:58:e5:39:9e: 39 | 42:49:12:35:76:ab:0f:fa:b1:1f:4e:b1:85:f3:a3:6f:60:e3: 40 | 6c:0e:a8:95:0d:c8:38:7f:e3:e3:ff:64:74:73:50:46:65:83: 41 | 5f:1a:72:f9:69:44:07:cd:36:01:90:b9:b3:ed:d8:d7:bc:68: 42 | 97:dd:11:ac:2b:ec:5d:a4:d4:d5:e8:8b:60:12:54:b9:c4:5f: 43 | 00:f8:ce:5b:72:28:58:43:7c:d5:25:b7:dd:ec:71:da:aa:3a: 44 | f2:6c 45 | -----BEGIN CERTIFICATE----- 46 | MIIC7zCCAligAwIBAgIJAJrlel8FWi/kMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV 47 | BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX 48 | aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNTEyMDUyMTUw 49 | NDZaFw0xNjAxMDQyMTUwNDZaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21l 50 | LVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV 51 | BAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA8Zu2JGRm 52 | v17adyo5vTmTVigm8dcfyWAc44KkB6IPyNZo/DAvFzA0ac3Y8efHhPnFkLEsQtUj 53 | INId1xgVcAqjHccu3wPAn1zLAiXafSsaCXjlI4rEZDlZDU4VC3V7dfmKTMOd+TEI 54 | 1doApdsM3wnl5BTRFw+79s+9PF0UasvB3OECAwEAAaOBvjCBuzAdBgNVHQ4EFgQU 55 | ngnA0R4OB7NJVwpJR/mKX07+I3UwgYsGA1UdIwSBgzCBgIAUngnA0R4OB7NJVwpJ 56 | R/mKX07+I3WhXaRbMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRl 57 | MSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCWxv 58 | Y2FsaG9zdIIJAJrlel8FWi/kMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD 59 | gYEATDvGQpZ1lqCb9dmxmxtPvdKN8VPth4D1e102bjjIrhpY5TmeQkkSNXarD/qx 60 | H06xhfOjb2DjbA6olQ3IOH/j4/9kdHNQRmWDXxpy+WlEB802AZC5s+3Y17xol90R 61 | rCvsXaTU1eiLYBJUucRfAPjOW3IoWEN81SW33exx2qo68mw= 62 | -----END CERTIFICATE----- 63 | -------------------------------------------------------------------------------- /tests/types/chrono.rs: -------------------------------------------------------------------------------- 1 | extern crate chrono; 2 | 3 | use self::chrono::{TimeZone, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC}; 4 | use types::test_type; 5 | 6 | use postgres::types::{Date, Timestamp}; 7 | 8 | #[test] 9 | fn test_naive_date_time_params() { 10 | fn make_check<'a>(time: &'a str) -> (Option, &'a str) { 11 | (Some(NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()), time) 12 | } 13 | test_type("TIMESTAMP", 14 | &[make_check("'1970-01-01 00:00:00.010000000'"), 15 | make_check("'1965-09-25 11:19:33.100314000'"), 16 | make_check("'2010-02-09 23:11:45.120200000'"), 17 | (None, "NULL")]); 18 | } 19 | 20 | #[test] 21 | fn test_with_special_naive_date_time_params() { 22 | fn make_check<'a>(time: &'a str) -> (Timestamp, &'a str) { 23 | (Timestamp::Value(NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()), 24 | time) 25 | } 26 | test_type("TIMESTAMP", 27 | &[make_check("'1970-01-01 00:00:00.010000000'"), 28 | make_check("'1965-09-25 11:19:33.100314000'"), 29 | make_check("'2010-02-09 23:11:45.120200000'"), 30 | (Timestamp::PosInfinity, "'infinity'"), 31 | (Timestamp::NegInfinity, "'-infinity'")]); 32 | } 33 | 34 | #[test] 35 | fn test_date_time_params() { 36 | fn make_check<'a>(time: &'a str) -> (Option>, &'a str) { 37 | (Some(UTC.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()), time) 38 | } 39 | test_type("TIMESTAMP WITH TIME ZONE", 40 | &[make_check("'1970-01-01 00:00:00.010000000'"), 41 | make_check("'1965-09-25 11:19:33.100314000'"), 42 | make_check("'2010-02-09 23:11:45.120200000'"), 43 | (None, "NULL")]); 44 | } 45 | 46 | #[test] 47 | fn test_with_special_date_time_params() { 48 | fn make_check<'a>(time: &'a str) -> (Timestamp>, &'a str) { 49 | (Timestamp::Value(UTC.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()), time) 50 | } 51 | test_type("TIMESTAMP WITH TIME ZONE", 52 | &[make_check("'1970-01-01 00:00:00.010000000'"), 53 | make_check("'1965-09-25 11:19:33.100314000'"), 54 | make_check("'2010-02-09 23:11:45.120200000'"), 55 | (Timestamp::PosInfinity, "'infinity'"), 56 | (Timestamp::NegInfinity, "'-infinity'")]); 57 | } 58 | 59 | #[test] 60 | fn test_date_params() { 61 | fn make_check<'a>(time: &'a str) -> (Option, &'a str) { 62 | (Some(NaiveDate::parse_from_str(time, "'%Y-%m-%d'").unwrap()), time) 63 | } 64 | test_type("DATE", 65 | &[make_check("'1970-01-01'"), 66 | make_check("'1965-09-25'"), 67 | make_check("'2010-02-09'"), 68 | (None, "NULL")]); 69 | } 70 | 71 | #[test] 72 | fn test_with_special_date_params() { 73 | fn make_check<'a>(date: &'a str) -> (Date, &'a str) { 74 | (Date::Value(NaiveDate::parse_from_str(date, "'%Y-%m-%d'").unwrap()), date) 75 | } 76 | test_type("DATE", 77 | &[make_check("'1970-01-01'"), 78 | make_check("'1965-09-25'"), 79 | make_check("'2010-02-09'"), 80 | (Date::PosInfinity, "'infinity'"), 81 | (Date::NegInfinity, "'-infinity'")]); 82 | } 83 | 84 | #[test] 85 | fn test_time_params() { 86 | fn make_check<'a>(time: &'a str) -> (Option, &'a str) { 87 | (Some(NaiveTime::parse_from_str(time, "'%H:%M:%S.%f'").unwrap()), time) 88 | } 89 | test_type("TIME", 90 | &[make_check("'00:00:00.010000000'"), 91 | make_check("'11:19:33.100314000'"), 92 | make_check("'23:11:45.120200000'"), 93 | (None, "NULL")]); 94 | } 95 | -------------------------------------------------------------------------------- /THIRD_PARTY: -------------------------------------------------------------------------------- 1 | Rust-Postgres contains some third-party content. 2 | 3 | ------------------------------------------------------------------------------- 4 | 5 | * Some documentation has been copied from PostgreSQL 6 | 7 | PostgreSQL Database Management System 8 | (formerly known as Postgres, then as Postgres95) 9 | 10 | Portions Copyright (c) 1996-2013, The PostgreSQL Global Development Group 11 | 12 | Portions Copyright (c) 1994, The Regents of the University of California 13 | 14 | Permission to use, copy, modify, and distribute this software and its 15 | documentation for any purpose, without fee, and without a written agreement is 16 | hereby granted, provided that the above copyright notice and this paragraph and 17 | the following two paragraphs appear in all copies. 18 | 19 | IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR 20 | DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 21 | PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 22 | THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 23 | DAMAGE. 24 | 25 | THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 26 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27 | A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, 28 | AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, 29 | SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 30 | 31 | ------------------------------------------------------------------------------- 32 | 33 | * src/url.rs has been copied from Rust 34 | 35 | Copyright (c) 2014 The Rust Project Developers 36 | 37 | Permission is hereby granted, free of charge, to any 38 | person obtaining a copy of this software and associated 39 | documentation files (the "Software"), to deal in the 40 | Software without restriction, including without 41 | limitation the rights to use, copy, modify, merge, 42 | publish, distribute, sublicense, and/or sell copies of 43 | the Software, and to permit persons to whom the Software 44 | is furnished to do so, subject to the following 45 | conditions: 46 | 47 | The above copyright notice and this permission notice 48 | shall be included in all copies or substantial portions 49 | of the Software. 50 | 51 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 52 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 53 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 54 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 55 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 56 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 57 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 58 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 59 | DEALINGS IN THE SOFTWARE. 60 | 61 | ------------------------------------------------------------------------------- 62 | 63 | * src/md5.rs has been copied from rust-crypto 64 | 65 | Copyright (c) 2006-2009 Graydon Hoare 66 | Copyright (c) 2009-2013 Mozilla Foundation 67 | 68 | Permission is hereby granted, free of charge, to any 69 | person obtaining a copy of this software and associated 70 | documentation files (the "Software"), to deal in the 71 | Software without restriction, including without 72 | limitation the rights to use, copy, modify, merge, 73 | publish, distribute, sublicense, and/or sell copies of 74 | the Software, and to permit persons to whom the Software 75 | is furnished to do so, subject to the following 76 | conditions: 77 | 78 | The above copyright notice and this permission notice 79 | shall be included in all copies or substantial portions 80 | of the Software. 81 | 82 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 83 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 84 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 85 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 86 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 87 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 88 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 89 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 90 | DEALINGS IN THE SOFTWARE. 91 | -------------------------------------------------------------------------------- /src/types/special.rs: -------------------------------------------------------------------------------- 1 | use std::io::prelude::*; 2 | use std::{i32, i64}; 3 | 4 | use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; 5 | 6 | use Result; 7 | use error::Error; 8 | use types::{Type, FromSql, ToSql, IsNull, SessionInfo}; 9 | 10 | /// A wrapper that can be used to represent infinity with `Type::Date` types. 11 | #[derive(Debug, Clone, Copy, PartialEq)] 12 | pub enum Date { 13 | /// Represents `infinity`, a date that is later than all other dates. 14 | PosInfinity, 15 | /// Represents `-infinity`, a date that is earlier than all other dates. 16 | NegInfinity, 17 | /// The wrapped date. 18 | Value(T), 19 | } 20 | 21 | impl FromSql for Date { 22 | fn from_sql(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result { 23 | if *ty != Type::Date { 24 | return Err(Error::Conversion("expected date type".into())); 25 | } 26 | 27 | let mut buf = [0; 4]; 28 | try!(raw.read_exact(buf.as_mut())); 29 | 30 | match try!(buf.as_ref().read_i32::()) { 31 | i32::MAX => Ok(Date::PosInfinity), 32 | i32::MIN => Ok(Date::NegInfinity), 33 | _ => T::from_sql(ty, &mut &mut buf.as_ref(), ctx).map(Date::Value), 34 | } 35 | } 36 | 37 | fn accepts(ty: &Type) -> bool { 38 | *ty == Type::Date && T::accepts(ty) 39 | } 40 | } 41 | impl ToSql for Date { 42 | fn to_sql(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result { 43 | if *ty != Type::Date { 44 | return Err(Error::Conversion("expected date type".into())); 45 | } 46 | 47 | let value = match *self { 48 | Date::PosInfinity => i32::MAX, 49 | Date::NegInfinity => i32::MIN, 50 | Date::Value(ref v) => return v.to_sql(ty, out, ctx), 51 | }; 52 | 53 | try!(out.write_i32::(value)); 54 | Ok(IsNull::No) 55 | } 56 | 57 | fn accepts(ty: &Type) -> bool { 58 | *ty == Type::Date && T::accepts(ty) 59 | } 60 | 61 | to_sql_checked!(); 62 | } 63 | 64 | /// A wrapper that can be used to represent infinity with `Type::Timestamp` and `Type::TimestampTZ` 65 | /// types. 66 | #[derive(Debug, Clone, Copy, PartialEq)] 67 | pub enum Timestamp { 68 | /// Represents `infinity`, a timestamp that is later than all other timestamps. 69 | PosInfinity, 70 | /// Represents `-infinity`, a timestamp that is earlier than all other timestamps. 71 | NegInfinity, 72 | /// The wrapped timestamp. 73 | Value(T), 74 | } 75 | 76 | impl FromSql for Timestamp { 77 | fn from_sql(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result { 78 | if *ty != Type::Timestamp && *ty != Type::TimestampTZ { 79 | return Err(Error::Conversion("expected timestamp or timestamptz type".into())); 80 | } 81 | 82 | let mut buf = [0; 8]; 83 | try!(raw.read_exact(buf.as_mut())); 84 | 85 | match try!(buf.as_ref().read_i64::()) { 86 | i64::MAX => Ok(Timestamp::PosInfinity), 87 | i64::MIN => Ok(Timestamp::NegInfinity), 88 | _ => T::from_sql(ty, &mut &mut buf.as_ref(), ctx).map(Timestamp::Value), 89 | } 90 | } 91 | 92 | fn accepts(ty: &Type) -> bool { 93 | (*ty == Type::Timestamp || *ty == Type::TimestampTZ) && T::accepts(ty) 94 | } 95 | } 96 | 97 | impl ToSql for Timestamp { 98 | fn to_sql(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result { 99 | if *ty != Type::Timestamp && *ty != Type::TimestampTZ { 100 | return Err(Error::Conversion("expected timestamp or timestamptz type".into())); 101 | } 102 | 103 | let value = match *self { 104 | Timestamp::PosInfinity => i64::MAX, 105 | Timestamp::NegInfinity => i64::MIN, 106 | Timestamp::Value(ref v) => return v.to_sql(ty, out, ctx), 107 | }; 108 | 109 | try!(out.write_i64::(value)); 110 | Ok(IsNull::No) 111 | } 112 | 113 | fn accepts(ty: &Type) -> bool { 114 | (*ty == Type::Timestamp || *ty == Type::TimestampTZ) && T::accepts(ty) 115 | } 116 | 117 | to_sql_checked!(); 118 | } 119 | -------------------------------------------------------------------------------- /codegen/src/sqlstate.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{Write, BufWriter}; 3 | use std::path::Path; 4 | use phf_codegen; 5 | 6 | use snake_to_camel; 7 | 8 | const ERRCODES_TXT: &'static str = include_str!("errcodes.txt"); 9 | 10 | struct Code { 11 | code: String, 12 | variant: String, 13 | } 14 | 15 | pub fn build(path: &Path) { 16 | let mut file = BufWriter::new(File::create(path.join("error/sqlstate.rs")).unwrap()); 17 | 18 | let codes = parse_codes(); 19 | 20 | make_header(&mut file); 21 | make_enum(&codes, &mut file); 22 | make_map(&codes, &mut file); 23 | make_impl(&codes, &mut file); 24 | } 25 | 26 | fn parse_codes() -> Vec { 27 | let mut codes = vec![]; 28 | 29 | for line in ERRCODES_TXT.lines() { 30 | if line.starts_with("#") || line.starts_with("Section") || line.trim().is_empty() { 31 | continue; 32 | } 33 | 34 | let mut it = line.split_whitespace(); 35 | let code = it.next().unwrap().to_owned(); 36 | it.next(); 37 | it.next(); 38 | // for 2202E 39 | let name = match it.next() { 40 | Some(name) => name, 41 | None => continue, 42 | }; 43 | let variant = match variant_name(&code) { 44 | Some(variant) => variant, 45 | None => snake_to_camel(&name), 46 | }; 47 | 48 | codes.push(Code { 49 | code: code, 50 | variant: variant, 51 | }); 52 | } 53 | 54 | codes 55 | } 56 | 57 | fn variant_name(code: &str) -> Option { 58 | match code { 59 | "01004" => Some("WarningStringDataRightTruncation".to_owned()), 60 | "22001" => Some("DataStringDataRightTruncation".to_owned()), 61 | "2F002" => Some("SqlRoutineModifyingSqlDataNotPermitted".to_owned()), 62 | "38002" => Some("ForeignRoutineModifyingSqlDataNotPermitted".to_owned()), 63 | "2F003" => Some("SqlRoutineProhibitedSqlStatementAttempted".to_owned()), 64 | "38003" => Some("ForeignRoutineProhibitedSqlStatementAttempted".to_owned()), 65 | "2F004" => Some("SqlRoutineReadingSqlDataNotPermitted".to_owned()), 66 | "38004" => Some("ForeignRoutineReadingSqlDataNotPermitted".to_owned()), 67 | "22004" => Some("DataNullValueNotAllowed".to_owned()), 68 | "39004" => Some("ExternalRoutineInvocationNullValueNotAllowed".to_owned()), 69 | _ => None, 70 | } 71 | } 72 | 73 | fn make_header(file: &mut BufWriter) { 74 | write!(file, 75 | "// Autogenerated file - DO NOT EDIT 76 | use phf; 77 | 78 | " 79 | ).unwrap(); 80 | } 81 | 82 | fn make_enum(codes: &[Code], file: &mut BufWriter) { 83 | write!(file, 84 | r#"/// SQLSTATE error codes 85 | #[derive(PartialEq, Eq, Clone, Debug)] 86 | pub enum SqlState {{ 87 | "# 88 | ).unwrap(); 89 | 90 | for code in codes { 91 | write!(file, 92 | " /// `{}` 93 | {},\n", 94 | code.code, code.variant).unwrap(); 95 | } 96 | 97 | write!(file, 98 | " /// An unknown code 99 | Other(String), 100 | }} 101 | 102 | " 103 | ).unwrap(); 104 | } 105 | 106 | fn make_map(codes: &[Code], file: &mut BufWriter) { 107 | write!(file, 108 | "#[cfg_attr(rustfmt, rustfmt_skip)] 109 | static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = ").unwrap(); 110 | let mut builder = phf_codegen::Map::new(); 111 | for code in codes { 112 | builder.entry(&*code.code, &format!("SqlState::{}", code.variant)); 113 | } 114 | builder.build(file).unwrap(); 115 | write!(file, ";\n").unwrap(); 116 | } 117 | 118 | fn make_impl(codes: &[Code], file: &mut BufWriter) { 119 | write!(file, r#" 120 | impl SqlState {{ 121 | /// Creates a `SqlState` from its error code. 122 | pub fn from_code(s: String) -> SqlState {{ 123 | match SQLSTATE_MAP.get(&*s) {{ 124 | Some(state) => state.clone(), 125 | None => SqlState::Other(s), 126 | }} 127 | }} 128 | 129 | /// Returns the error code corresponding to the `SqlState`. 130 | pub fn code(&self) -> &str {{ 131 | match *self {{"# 132 | ).unwrap(); 133 | 134 | for code in codes { 135 | write!(file, r#" 136 | SqlState::{} => "{}","#, 137 | code.variant, code.code).unwrap(); 138 | } 139 | 140 | write!(file, r#" 141 | SqlState::Other(ref s) => s, 142 | }} 143 | }} 144 | }} 145 | "# 146 | ).unwrap(); 147 | } 148 | 149 | -------------------------------------------------------------------------------- /src/types/chrono.rs: -------------------------------------------------------------------------------- 1 | extern crate chrono; 2 | 3 | use std::io::prelude::*; 4 | use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; 5 | use self::chrono::{Duration, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC, Local, 6 | FixedOffset}; 7 | 8 | use Result; 9 | use error::Error; 10 | use types::{FromSql, ToSql, IsNull, Type, SessionInfo}; 11 | 12 | fn base() -> NaiveDateTime { 13 | NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0) 14 | } 15 | 16 | impl FromSql for NaiveDateTime { 17 | fn from_sql(_: &Type, raw: &mut R, _: &SessionInfo) -> Result { 18 | let t = try!(raw.read_i64::()); 19 | Ok(base() + Duration::microseconds(t)) 20 | } 21 | 22 | accepts!(Type::Timestamp); 23 | } 24 | 25 | impl ToSql for NaiveDateTime { 26 | fn to_sql(&self, 27 | _: &Type, 28 | mut w: &mut W, 29 | _: &SessionInfo) 30 | -> Result { 31 | let time = match (*self - base()).num_microseconds() { 32 | Some(time) => time, 33 | None => return Err(Error::Conversion("value too large to transmit".into())), 34 | }; 35 | try!(w.write_i64::(time)); 36 | Ok(IsNull::No) 37 | } 38 | 39 | accepts!(Type::Timestamp); 40 | to_sql_checked!(); 41 | } 42 | 43 | impl FromSql for DateTime { 44 | fn from_sql(type_: &Type, raw: &mut R, info: &SessionInfo) -> Result> { 45 | let naive = try!(NaiveDateTime::from_sql(type_, raw, info)); 46 | Ok(DateTime::from_utc(naive, UTC)) 47 | } 48 | 49 | accepts!(Type::TimestampTZ); 50 | } 51 | 52 | impl ToSql for DateTime { 53 | fn to_sql(&self, 54 | type_: &Type, 55 | mut w: &mut W, 56 | info: &SessionInfo) 57 | -> Result { 58 | self.naive_utc().to_sql(type_, w, info) 59 | } 60 | 61 | accepts!(Type::TimestampTZ); 62 | to_sql_checked!(); 63 | } 64 | 65 | impl FromSql for DateTime { 66 | fn from_sql(type_: &Type, raw: &mut R, info: &SessionInfo) -> Result> { 67 | let utc = try!(DateTime::::from_sql(type_, raw, info)); 68 | Ok(utc.with_timezone(&Local)) 69 | } 70 | 71 | accepts!(Type::TimestampTZ); 72 | } 73 | 74 | impl ToSql for DateTime { 75 | fn to_sql(&self, 76 | type_: &Type, 77 | mut w: &mut W, 78 | info: &SessionInfo) 79 | -> Result { 80 | self.with_timezone(&UTC).to_sql(type_, w, info) 81 | } 82 | 83 | accepts!(Type::TimestampTZ); 84 | to_sql_checked!(); 85 | } 86 | 87 | impl FromSql for DateTime { 88 | fn from_sql(type_: &Type, 89 | raw: &mut R, 90 | info: &SessionInfo) 91 | -> Result> { 92 | let utc = try!(DateTime::::from_sql(type_, raw, info)); 93 | Ok(utc.with_timezone(&FixedOffset::east(0))) 94 | } 95 | 96 | accepts!(Type::TimestampTZ); 97 | } 98 | 99 | impl ToSql for DateTime { 100 | fn to_sql(&self, 101 | type_: &Type, 102 | mut w: &mut W, 103 | info: &SessionInfo) 104 | -> Result { 105 | self.with_timezone(&UTC).to_sql(type_, w, info) 106 | } 107 | 108 | accepts!(Type::TimestampTZ); 109 | to_sql_checked!(); 110 | } 111 | 112 | impl FromSql for NaiveDate { 113 | fn from_sql(_: &Type, raw: &mut R, _: &SessionInfo) -> Result { 114 | let jd = try!(raw.read_i32::()); 115 | Ok(base().date() + Duration::days(jd as i64)) 116 | } 117 | 118 | accepts!(Type::Date); 119 | } 120 | 121 | impl ToSql for NaiveDate { 122 | fn to_sql(&self, 123 | _: &Type, 124 | mut w: &mut W, 125 | _: &SessionInfo) 126 | -> Result { 127 | let jd = (*self - base().date()).num_days(); 128 | if jd > i32::max_value() as i64 || jd < i32::min_value() as i64 { 129 | return Err(Error::Conversion("value too large to transmit".into())); 130 | } 131 | 132 | try!(w.write_i32::(jd as i32)); 133 | Ok(IsNull::No) 134 | } 135 | 136 | accepts!(Type::Date); 137 | to_sql_checked!(); 138 | } 139 | 140 | impl FromSql for NaiveTime { 141 | fn from_sql(_: &Type, raw: &mut R, _: &SessionInfo) -> Result { 142 | let usec = try!(raw.read_i64::()); 143 | Ok(NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(usec)) 144 | } 145 | 146 | accepts!(Type::Time); 147 | } 148 | 149 | impl ToSql for NaiveTime { 150 | fn to_sql(&self, 151 | _: &Type, 152 | mut w: &mut W, 153 | _: &SessionInfo) 154 | -> Result { 155 | let delta = *self - NaiveTime::from_hms(0, 0, 0); 156 | let time = match delta.num_microseconds() { 157 | Some(time) => time, 158 | None => return Err(Error::Conversion("value too large to transmit".into())), 159 | }; 160 | try!(w.write_i64::(time)); 161 | Ok(IsNull::No) 162 | } 163 | 164 | accepts!(Type::Time); 165 | to_sql_checked!(); 166 | } 167 | -------------------------------------------------------------------------------- /src/notification.rs: -------------------------------------------------------------------------------- 1 | //! Asynchronous notifications. 2 | 3 | use std::fmt; 4 | use std::time::Duration; 5 | 6 | use {desynchronized, Result, Connection, NotificationsNew}; 7 | use message::Backend; 8 | use error::Error; 9 | 10 | /// An asynchronous notification. 11 | #[derive(Clone, Debug)] 12 | pub struct Notification { 13 | /// The process ID of the notifying backend process. 14 | pub pid: u32, 15 | /// The name of the channel that the notify has been raised on. 16 | pub channel: String, 17 | /// The "payload" string passed from the notifying process. 18 | pub payload: String, 19 | } 20 | 21 | /// Notifications from the Postgres backend. 22 | pub struct Notifications<'conn> { 23 | conn: &'conn Connection, 24 | } 25 | 26 | impl<'a> fmt::Debug for Notifications<'a> { 27 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 28 | fmt.debug_struct("Notifications") 29 | .field("pending", &self.len()) 30 | .finish() 31 | } 32 | } 33 | 34 | impl<'conn> Notifications<'conn> { 35 | /// Returns the number of pending notifications. 36 | pub fn len(&self) -> usize { 37 | self.conn.conn.borrow().notifications.len() 38 | } 39 | 40 | /// Determines if there are any pending notifications. 41 | pub fn is_empty(&self) -> bool { 42 | self.len() == 0 43 | } 44 | 45 | /// Returns an iterator over pending notifications. 46 | /// 47 | /// # Note 48 | /// 49 | /// This iterator may start returning `Some` after previously returning 50 | /// `None` if more notifications are received. 51 | pub fn iter<'a>(&'a self) -> Iter<'a> { 52 | Iter { conn: self.conn } 53 | } 54 | 55 | /// Returns an iterator over notifications that blocks until one is 56 | /// received if none are pending. 57 | /// 58 | /// The iterator will never return `None`. 59 | pub fn blocking_iter<'a>(&'a self) -> BlockingIter<'a> { 60 | BlockingIter { conn: self.conn } 61 | } 62 | 63 | /// Returns an iterator over notifications that blocks for a limited time 64 | /// waiting to receive one if none are pending. 65 | /// 66 | /// # Note 67 | /// 68 | /// This iterator may start returning `Some` after previously returning 69 | /// `None` if more notifications are received. 70 | pub fn timeout_iter<'a>(&'a self, timeout: Duration) -> TimeoutIter<'a> { 71 | TimeoutIter { 72 | conn: self.conn, 73 | timeout: timeout, 74 | } 75 | } 76 | } 77 | 78 | impl<'a, 'conn> IntoIterator for &'a Notifications<'conn> { 79 | type Item = Result; 80 | type IntoIter = Iter<'a>; 81 | 82 | fn into_iter(self) -> Iter<'a> { 83 | self.iter() 84 | } 85 | } 86 | 87 | impl<'conn> NotificationsNew<'conn> for Notifications<'conn> { 88 | fn new(conn: &'conn Connection) -> Notifications<'conn> { 89 | Notifications { conn: conn } 90 | } 91 | } 92 | 93 | /// An iterator over pending notifications. 94 | pub struct Iter<'a> { 95 | conn: &'a Connection, 96 | } 97 | 98 | impl<'a> Iterator for Iter<'a> { 99 | type Item = Result; 100 | 101 | fn next(&mut self) -> Option> { 102 | let mut conn = self.conn.conn.borrow_mut(); 103 | 104 | if let Some(notification) = conn.notifications.pop_front() { 105 | return Some(Ok(notification)); 106 | } 107 | 108 | if conn.is_desynchronized() { 109 | return Some(Err(Error::Io(desynchronized()))); 110 | } 111 | 112 | match conn.read_message_with_notification_nonblocking() { 113 | Ok(Some(Backend::NotificationResponse { pid, channel, payload })) => { 114 | Some(Ok(Notification { 115 | pid: pid, 116 | channel: channel, 117 | payload: payload, 118 | })) 119 | } 120 | Ok(None) => None, 121 | Err(err) => Some(Err(Error::Io(err))), 122 | _ => unreachable!(), 123 | } 124 | } 125 | 126 | fn size_hint(&self) -> (usize, Option) { 127 | (self.conn.conn.borrow().notifications.len(), None) 128 | } 129 | } 130 | 131 | /// An iterator over notifications which will block if none are pending. 132 | pub struct BlockingIter<'a> { 133 | conn: &'a Connection, 134 | } 135 | 136 | impl<'a> Iterator for BlockingIter<'a> { 137 | type Item = Result; 138 | 139 | fn next(&mut self) -> Option> { 140 | let mut conn = self.conn.conn.borrow_mut(); 141 | 142 | if let Some(notification) = conn.notifications.pop_front() { 143 | return Some(Ok(notification)); 144 | } 145 | 146 | if conn.is_desynchronized() { 147 | return Some(Err(Error::Io(desynchronized()))); 148 | } 149 | 150 | match conn.read_message_with_notification() { 151 | Ok(Backend::NotificationResponse { pid, channel, payload }) => { 152 | Some(Ok(Notification { 153 | pid: pid, 154 | channel: channel, 155 | payload: payload, 156 | })) 157 | } 158 | Err(err) => Some(Err(Error::Io(err))), 159 | _ => unreachable!(), 160 | } 161 | } 162 | 163 | fn size_hint(&self) -> (usize, Option) { 164 | (usize::max_value(), None) 165 | } 166 | } 167 | 168 | /// An iterator over notifications which will block for a period of time if 169 | /// none are pending. 170 | pub struct TimeoutIter<'a> { 171 | conn: &'a Connection, 172 | timeout: Duration, 173 | } 174 | 175 | impl<'a> Iterator for TimeoutIter<'a> { 176 | type Item = Result; 177 | 178 | fn next(&mut self) -> Option> { 179 | let mut conn = self.conn.conn.borrow_mut(); 180 | 181 | if let Some(notification) = conn.notifications.pop_front() { 182 | return Some(Ok(notification)); 183 | } 184 | 185 | if conn.is_desynchronized() { 186 | return Some(Err(Error::Io(desynchronized()))); 187 | } 188 | 189 | match conn.read_message_with_notification_timeout(self.timeout) { 190 | Ok(Some(Backend::NotificationResponse { pid, channel, payload })) => { 191 | Some(Ok(Notification { 192 | pid: pid, 193 | channel: channel, 194 | payload: payload, 195 | })) 196 | } 197 | Ok(None) => None, 198 | Err(err) => Some(Err(Error::Io(err))), 199 | _ => unreachable!(), 200 | } 201 | } 202 | 203 | fn size_hint(&self) -> (usize, Option) { 204 | (self.conn.conn.borrow().notifications.len(), None) 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/priv_io.rs: -------------------------------------------------------------------------------- 1 | use byteorder::ReadBytesExt; 2 | use std::error::Error; 3 | use std::io; 4 | use std::io::prelude::*; 5 | use std::fmt; 6 | use std::net::TcpStream; 7 | use std::time::Duration; 8 | use bufstream::BufStream; 9 | #[cfg(feature = "unix_socket")] 10 | use unix_socket::UnixStream; 11 | #[cfg(all(not(feature = "unix_socket"), all(unix, feature = "nightly")))] 12 | use std::os::unix::net::UnixStream; 13 | #[cfg(unix)] 14 | use std::os::unix::io::{AsRawFd, RawFd}; 15 | #[cfg(windows)] 16 | use std::os::windows::io::{AsRawSocket, RawSocket}; 17 | 18 | use {SslMode, ConnectParams, ConnectTarget}; 19 | use error::ConnectError; 20 | use io::StreamWrapper; 21 | use message::{self, WriteMessage}; 22 | use message::Frontend; 23 | 24 | const DEFAULT_PORT: u16 = 5432; 25 | 26 | #[doc(hidden)] 27 | pub trait StreamOptions { 28 | fn set_read_timeout(&self, timeout: Option) -> io::Result<()>; 29 | fn set_nonblocking(&self, nonblock: bool) -> io::Result<()>; 30 | } 31 | 32 | impl StreamOptions for BufStream> { 33 | fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { 34 | match self.get_ref().get_ref().0 { 35 | InternalStream::Tcp(ref s) => s.set_read_timeout(timeout), 36 | #[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))] 37 | InternalStream::Unix(ref s) => s.set_read_timeout(timeout), 38 | } 39 | } 40 | 41 | fn set_nonblocking(&self, nonblock: bool) -> io::Result<()> { 42 | match self.get_ref().get_ref().0 { 43 | InternalStream::Tcp(ref s) => s.set_nonblocking(nonblock), 44 | #[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))] 45 | InternalStream::Unix(ref s) => s.set_nonblocking(nonblock), 46 | } 47 | } 48 | } 49 | 50 | /// A connection to the Postgres server. 51 | /// 52 | /// It implements `Read`, `Write` and `StreamWrapper`, as well as `AsRawFd` on 53 | /// Unix platforms and `AsRawSocket` on Windows platforms. 54 | pub struct Stream(InternalStream); 55 | 56 | impl fmt::Debug for Stream { 57 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 58 | match self.0 { 59 | InternalStream::Tcp(ref s) => fmt::Debug::fmt(s, fmt), 60 | #[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))] 61 | InternalStream::Unix(ref s) => fmt::Debug::fmt(s, fmt), 62 | } 63 | } 64 | } 65 | 66 | impl Read for Stream { 67 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 68 | self.0.read(buf) 69 | } 70 | } 71 | 72 | impl Write for Stream { 73 | fn write(&mut self, buf: &[u8]) -> io::Result { 74 | self.0.write(buf) 75 | } 76 | 77 | fn flush(&mut self) -> io::Result<()> { 78 | self.0.flush() 79 | } 80 | } 81 | 82 | impl StreamWrapper for Stream { 83 | fn get_ref(&self) -> &Stream { 84 | self 85 | } 86 | 87 | fn get_mut(&mut self) -> &mut Stream { 88 | self 89 | } 90 | } 91 | 92 | #[cfg(unix)] 93 | impl AsRawFd for Stream { 94 | fn as_raw_fd(&self) -> RawFd { 95 | match self.0 { 96 | InternalStream::Tcp(ref s) => s.as_raw_fd(), 97 | #[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))] 98 | InternalStream::Unix(ref s) => s.as_raw_fd(), 99 | } 100 | } 101 | } 102 | 103 | #[cfg(windows)] 104 | impl AsRawSocket for Stream { 105 | fn as_raw_socket(&self) -> RawSocket { 106 | // Unix sockets aren't supported on windows, so no need to match 107 | match self.0 { 108 | InternalStream::Tcp(ref s) => s.as_raw_socket(), 109 | } 110 | } 111 | } 112 | 113 | enum InternalStream { 114 | Tcp(TcpStream), 115 | #[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))] 116 | Unix(UnixStream), 117 | } 118 | 119 | impl Read for InternalStream { 120 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 121 | match *self { 122 | InternalStream::Tcp(ref mut s) => s.read(buf), 123 | #[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))] 124 | InternalStream::Unix(ref mut s) => s.read(buf), 125 | } 126 | } 127 | } 128 | 129 | impl Write for InternalStream { 130 | fn write(&mut self, buf: &[u8]) -> io::Result { 131 | match *self { 132 | InternalStream::Tcp(ref mut s) => s.write(buf), 133 | #[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))] 134 | InternalStream::Unix(ref mut s) => s.write(buf), 135 | } 136 | } 137 | 138 | fn flush(&mut self) -> io::Result<()> { 139 | match *self { 140 | InternalStream::Tcp(ref mut s) => s.flush(), 141 | #[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))] 142 | InternalStream::Unix(ref mut s) => s.flush(), 143 | } 144 | } 145 | } 146 | 147 | fn open_socket(params: &ConnectParams) -> Result { 148 | let port = params.port.unwrap_or(DEFAULT_PORT); 149 | match params.target { 150 | ConnectTarget::Tcp(ref host) => { 151 | Ok(try!(TcpStream::connect(&(&**host, port)).map(InternalStream::Tcp))) 152 | } 153 | #[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))] 154 | ConnectTarget::Unix(ref path) => { 155 | let path = path.join(&format!(".s.PGSQL.{}", port)); 156 | Ok(try!(UnixStream::connect(&path).map(InternalStream::Unix))) 157 | } 158 | } 159 | } 160 | 161 | pub fn initialize_stream(params: &ConnectParams, 162 | ssl: SslMode) 163 | -> Result, ConnectError> { 164 | let mut socket = Stream(try!(open_socket(params))); 165 | 166 | let (ssl_required, negotiator) = match ssl { 167 | SslMode::None => return Ok(Box::new(socket)), 168 | SslMode::Prefer(negotiator) => (false, negotiator), 169 | SslMode::Require(negotiator) => (true, negotiator), 170 | }; 171 | 172 | try!(socket.write_message(&Frontend::SslRequest { code: message::SSL_CODE })); 173 | try!(socket.flush()); 174 | 175 | if try!(socket.read_u8()) == b'N' { 176 | if ssl_required { 177 | let err: Box = "The server does not support SSL".into(); 178 | return Err(ConnectError::Ssl(err)); 179 | } else { 180 | return Ok(Box::new(socket)); 181 | } 182 | } 183 | 184 | // Postgres doesn't support SSL over unix sockets 185 | let host = match params.target { 186 | ConnectTarget::Tcp(ref host) => host, 187 | #[cfg(any(feature = "unix_socket", all(unix, feature = "nightly")))] 188 | ConnectTarget::Unix(_) => return Err(ConnectError::Io(::bad_response())), 189 | }; 190 | 191 | negotiator.negotiate_ssl(host, socket).map_err(ConnectError::Ssl) 192 | } 193 | -------------------------------------------------------------------------------- /codegen/src/types.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | use std::ascii::AsciiExt; 3 | use std::collections::BTreeMap; 4 | use std::fs::File; 5 | use std::io::{Write, BufWriter}; 6 | use std::path::Path; 7 | use marksman_escape::Escape; 8 | 9 | use snake_to_camel; 10 | 11 | const PG_TYPE_H: &'static str = include_str!("pg_type.h"); 12 | const PG_RANGE_H: &'static str = include_str!("pg_range.h"); 13 | 14 | struct Type { 15 | name: &'static str, 16 | variant: String, 17 | kind: &'static str, 18 | element: u32, 19 | doc: String, 20 | } 21 | 22 | pub fn build(path: &Path) { 23 | let mut file = BufWriter::new(File::create(path.join("types/types.rs")).unwrap()); 24 | 25 | let ranges = parse_ranges(); 26 | let types = parse_types(&ranges); 27 | 28 | make_header(&mut file); 29 | make_enum(&mut file, &types); 30 | make_display_impl(&mut file); 31 | make_impl(&mut file, &types); 32 | } 33 | 34 | fn parse_ranges() -> BTreeMap { 35 | let mut ranges = BTreeMap::new(); 36 | 37 | for line in PG_RANGE_H.lines() { 38 | if !line.starts_with("DATA") { 39 | continue; 40 | } 41 | 42 | let split = line.split_whitespace().collect::>(); 43 | 44 | let oid = split[2].parse().unwrap(); 45 | let element = split[3].parse().unwrap(); 46 | 47 | ranges.insert(oid, element); 48 | } 49 | 50 | ranges 51 | } 52 | 53 | fn parse_types(ranges: &BTreeMap) -> BTreeMap { 54 | let doc_re = Regex::new(r#"DESCR\("([^"]+)"\)"#).unwrap(); 55 | let range_vector_re = Regex::new("(range|vector)$").unwrap(); 56 | let array_re = Regex::new("^_(.*)").unwrap(); 57 | 58 | let mut types = BTreeMap::new(); 59 | 60 | let mut lines = PG_TYPE_H.lines().peekable(); 61 | while let Some(line) = lines.next() { 62 | if !line.starts_with("DATA") { 63 | continue; 64 | } 65 | 66 | let split = line.split_whitespace().collect::>(); 67 | 68 | let oid = split[3].parse().unwrap(); 69 | 70 | let name = split[5]; 71 | 72 | let variant = match name { 73 | "anyarray" => "AnyArray".to_owned(), 74 | // FIXME remove following overrides for 0.12 75 | "anyrange" => "Anyrange".to_owned(), 76 | "tsvector" => "Tsvector".to_owned(), 77 | "gtsvector" => "Gtsvector".to_owned(), 78 | "_tsvector" => "TsvectorArray".to_owned(), 79 | "_gtsvector" => "GtsvectorArray".to_owned(), 80 | "timestamptz" => "TimestampTZ".to_owned(), 81 | "_timestamptz" => "TimestampTZArray".to_owned(), 82 | name => { 83 | let variant = range_vector_re.replace(name, "_$1"); 84 | let variant = array_re.replace(&variant, "$1_array"); 85 | snake_to_camel(&variant) 86 | } 87 | }; 88 | 89 | let kind = split[11]; 90 | 91 | // FIXME enable for 0.12 92 | /* 93 | // we need to be able to pull composite fields and enum variants at runtime 94 | if kind == "C" || kind == "E" { 95 | continue; 96 | } 97 | */ 98 | 99 | let element = if let Some(&element) = ranges.get(&oid) { 100 | element 101 | } else { 102 | split[16].parse().unwrap() 103 | }; 104 | 105 | let doc = array_re.replace(name, "$1[]"); 106 | let mut doc = doc.to_ascii_uppercase(); 107 | 108 | let descr = lines.peek() 109 | .and_then(|line| doc_re.captures(line)) 110 | .and_then(|captures| captures.at(1)); 111 | if let Some(descr) = descr { 112 | doc.push_str(" - "); 113 | doc.push_str(descr); 114 | } 115 | let doc = Escape::new(doc.as_bytes().iter().cloned()).collect(); 116 | let doc = String::from_utf8(doc).unwrap(); 117 | 118 | let type_ = Type { 119 | name: name, 120 | variant: variant, 121 | kind: kind, 122 | element: element, 123 | doc: doc, 124 | }; 125 | 126 | types.insert(oid, type_); 127 | } 128 | 129 | types 130 | } 131 | 132 | fn make_header(w: &mut BufWriter) { 133 | write!(w, 134 | "// Autogenerated file - DO NOT EDIT 135 | use std::fmt; 136 | 137 | use types::{{Oid, Kind, Other}}; 138 | 139 | " 140 | ).unwrap(); 141 | } 142 | 143 | fn make_enum(w: &mut BufWriter, types: &BTreeMap) { 144 | write!(w, 145 | "/// A Postgres type. 146 | #[derive(PartialEq, Eq, Clone, Debug)] 147 | pub enum Type {{ 148 | " 149 | ).unwrap(); 150 | 151 | for type_ in types.values() { 152 | write!(w, 153 | " /// {} 154 | {}, 155 | " 156 | , type_.doc, type_.variant).unwrap(); 157 | } 158 | 159 | write!(w, 160 | r" /// An unknown type. 161 | Other(Other), 162 | }} 163 | 164 | " ).unwrap(); 165 | } 166 | 167 | fn make_display_impl(w: &mut BufWriter) { 168 | write!(w, 169 | r#"impl fmt::Display for Type {{ 170 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {{ 171 | match self.schema() {{ 172 | "public" | "pg_catalog" => {{}} 173 | schema => try!(write!(fmt, "{{}}.", schema)), 174 | }} 175 | fmt.write_str(self.name()) 176 | }} 177 | }} 178 | 179 | "#, 180 | ).unwrap(); 181 | } 182 | 183 | fn make_impl(w: &mut BufWriter, types: &BTreeMap) { 184 | write!(w, 185 | "impl Type {{ 186 | /// Returns the `Type` corresponding to the provided `Oid` if it 187 | /// corresponds to a built-in type. 188 | pub fn from_oid(oid: Oid) -> Option {{ 189 | match oid {{ 190 | ", 191 | ).unwrap(); 192 | 193 | for (oid, type_) in types { 194 | write!(w, 195 | " {} => Some(Type::{}), 196 | ", 197 | oid, type_.variant).unwrap(); 198 | } 199 | 200 | write!(w, 201 | " _ => None, 202 | }} 203 | }} 204 | 205 | /// Returns the OID of the `Type`. 206 | pub fn oid(&self) -> Oid {{ 207 | match *self {{ 208 | ", 209 | ).unwrap(); 210 | 211 | 212 | for (oid, type_) in types { 213 | write!(w, 214 | " Type::{} => {}, 215 | ", 216 | type_.variant, oid).unwrap(); 217 | } 218 | 219 | write!(w, 220 | " Type::Other(ref u) => u.oid(), 221 | }} 222 | }} 223 | 224 | /// Returns the kind of this type. 225 | pub fn kind(&self) -> &Kind {{ 226 | match *self {{ 227 | ", 228 | ).unwrap(); 229 | 230 | for type_ in types.values() { 231 | let kind = match type_.kind { 232 | "P" => "Pseudo".to_owned(), 233 | "A" => format!("Array(Type::{})", types[&type_.element].variant), 234 | "R" => format!("Range(Type::{})", types[&type_.element].variant), 235 | _ => "Simple".to_owned(), 236 | }; 237 | 238 | write!(w, 239 | " Type::{} => {{ 240 | const V: &'static Kind = &Kind::{}; 241 | V 242 | }} 243 | ", 244 | type_.variant, kind).unwrap(); 245 | } 246 | 247 | write!(w, 248 | r#" Type::Other(ref u) => u.kind(), 249 | }} 250 | }} 251 | 252 | /// Returns the schema of this type. 253 | pub fn schema(&self) -> &str {{ 254 | match *self {{ 255 | Type::Other(ref u) => u.schema(), 256 | _ => "pg_catalog", 257 | }} 258 | }} 259 | 260 | /// Returns the name of this type. 261 | pub fn name(&self) -> &str {{ 262 | match *self {{ 263 | "#, 264 | ).unwrap(); 265 | 266 | for type_ in types.values() { 267 | write!(w, 268 | r#" Type::{} => "{}", 269 | "#, 270 | type_.variant, type_.name).unwrap(); 271 | } 272 | 273 | write!(w, 274 | " Type::Other(ref u) => u.name(), 275 | }} 276 | }} 277 | }} 278 | " 279 | ).unwrap(); 280 | } 281 | -------------------------------------------------------------------------------- /src/transaction.rs: -------------------------------------------------------------------------------- 1 | //! Transactions 2 | 3 | use std::cell::Cell; 4 | use std::fmt; 5 | use std::ascii::AsciiExt; 6 | 7 | use {bad_response, Result, Connection, TransactionInternals, ConfigInternals, 8 | IsolationLevelNew}; 9 | use error::Error; 10 | use rows::Rows; 11 | use stmt::Statement; 12 | use types::ToSql; 13 | 14 | /// An enumeration of transaction isolation levels. 15 | /// 16 | /// See the [Postgres documentation](http://www.postgresql.org/docs/9.4/static/transaction-iso.html) 17 | /// for full details on the semantics of each level. 18 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 19 | pub enum IsolationLevel { 20 | /// The "read uncommitted" level. 21 | /// 22 | /// In current versions of Postgres, this behaves identically to 23 | /// `ReadCommitted`. 24 | ReadUncommitted, 25 | /// The "read committed" level. 26 | /// 27 | /// This is the default isolation level in Postgres. 28 | ReadCommitted, 29 | /// The "repeatable read" level. 30 | RepeatableRead, 31 | /// The "serializable" level. 32 | Serializable, 33 | } 34 | 35 | impl IsolationLevelNew for IsolationLevel { 36 | fn new(raw: &str) -> Result { 37 | if raw.eq_ignore_ascii_case("READ UNCOMMITTED") { 38 | Ok(IsolationLevel::ReadUncommitted) 39 | } else if raw.eq_ignore_ascii_case("READ COMMITTED") { 40 | Ok(IsolationLevel::ReadCommitted) 41 | } else if raw.eq_ignore_ascii_case("REPEATABLE READ") { 42 | Ok(IsolationLevel::RepeatableRead) 43 | } else if raw.eq_ignore_ascii_case("SERIALIZABLE") { 44 | Ok(IsolationLevel::Serializable) 45 | } else { 46 | Err(Error::Io(bad_response())) 47 | } 48 | } 49 | } 50 | 51 | impl IsolationLevel { 52 | fn to_sql(&self) -> &'static str { 53 | match *self { 54 | IsolationLevel::ReadUncommitted => "READ UNCOMMITTED", 55 | IsolationLevel::ReadCommitted => "READ COMMITTED", 56 | IsolationLevel::RepeatableRead => "REPEATABLE READ", 57 | IsolationLevel::Serializable => "SERIALIZABLE", 58 | } 59 | } 60 | } 61 | 62 | /// Configuration of a transaction. 63 | #[derive(Debug)] 64 | pub struct Config { 65 | isolation_level: Option, 66 | read_only: Option, 67 | deferrable: Option, 68 | } 69 | 70 | impl Default for Config { 71 | fn default() -> Config { 72 | Config { 73 | isolation_level: None, 74 | read_only: None, 75 | deferrable: None, 76 | } 77 | } 78 | } 79 | 80 | impl ConfigInternals for Config { 81 | fn build_command(&self, s: &mut String) { 82 | let mut first = true; 83 | 84 | if let Some(isolation_level) = self.isolation_level { 85 | s.push_str(" ISOLATION LEVEL "); 86 | s.push_str(isolation_level.to_sql()); 87 | first = false; 88 | } 89 | 90 | if let Some(read_only) = self.read_only { 91 | if !first { 92 | s.push(','); 93 | } 94 | if read_only { 95 | s.push_str(" READ ONLY"); 96 | } else { 97 | s.push_str(" READ WRITE"); 98 | } 99 | first = false; 100 | } 101 | 102 | if let Some(deferrable) = self.deferrable { 103 | if !first { 104 | s.push(','); 105 | } 106 | if deferrable { 107 | s.push_str(" DEFERRABLE"); 108 | } else { 109 | s.push_str(" NOT DEFERRABLE"); 110 | } 111 | } 112 | } 113 | } 114 | 115 | impl Config { 116 | /// Creates a new `Config` with no configuration overrides. 117 | pub fn new() -> Config { 118 | Config::default() 119 | } 120 | 121 | /// Sets the isolation level of the configuration. 122 | pub fn isolation_level(&mut self, isolation_level: IsolationLevel) -> &mut Config { 123 | self.isolation_level = Some(isolation_level); 124 | self 125 | } 126 | 127 | /// Sets the read-only property of a transaction. 128 | /// 129 | /// If enabled, a transaction will be unable to modify any persistent 130 | /// database state. 131 | pub fn read_only(&mut self, read_only: bool) -> &mut Config { 132 | self.read_only = Some(read_only); 133 | self 134 | } 135 | 136 | /// Sets the deferrable property of a transaction. 137 | /// 138 | /// If enabled in a read only, serializable transaction, the transaction may 139 | /// block when created, after which it will run without the normal overhead 140 | /// of a serializable transaction and will not be forced to roll back due 141 | /// to serialization failures. 142 | pub fn deferrable(&mut self, deferrable: bool) -> &mut Config { 143 | self.deferrable = Some(deferrable); 144 | self 145 | } 146 | } 147 | 148 | /// A transaction on a database connection. 149 | /// 150 | /// The transaction will roll back by default. 151 | pub struct Transaction<'conn> { 152 | conn: &'conn Connection, 153 | depth: u32, 154 | savepoint_name: Option, 155 | commit: Cell, 156 | finished: bool, 157 | } 158 | 159 | impl<'a> fmt::Debug for Transaction<'a> { 160 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 161 | fmt.debug_struct("Transaction") 162 | .field("commit", &self.commit.get()) 163 | .field("depth", &self.depth) 164 | .finish() 165 | } 166 | } 167 | 168 | impl<'conn> Drop for Transaction<'conn> { 169 | fn drop(&mut self) { 170 | if !self.finished { 171 | let _ = self.finish_inner(); 172 | } 173 | } 174 | } 175 | 176 | impl<'conn> TransactionInternals<'conn> for Transaction<'conn> { 177 | fn new(conn: &'conn Connection, depth: u32) -> Transaction<'conn> { 178 | Transaction { 179 | conn: conn, 180 | depth: depth, 181 | savepoint_name: None, 182 | commit: Cell::new(false), 183 | finished: false, 184 | } 185 | } 186 | 187 | fn conn(&self) -> &'conn Connection { 188 | self.conn 189 | } 190 | 191 | fn depth(&self) -> u32 { 192 | self.depth 193 | } 194 | } 195 | 196 | impl<'conn> Transaction<'conn> { 197 | fn finish_inner(&mut self) -> Result<()> { 198 | let mut conn = self.conn.conn.borrow_mut(); 199 | debug_assert!(self.depth == conn.trans_depth); 200 | conn.trans_depth -= 1; 201 | match (self.commit.get(), &self.savepoint_name) { 202 | (false, &Some(ref savepoint_name)) => { 203 | conn.quick_query(&format!("ROLLBACK TO {}", savepoint_name)) 204 | } 205 | (false, &None) => conn.quick_query("ROLLBACK"), 206 | (true, &Some(ref savepoint_name)) => { 207 | conn.quick_query(&format!("RELEASE {}", savepoint_name)) 208 | } 209 | (true, &None) => conn.quick_query("COMMIT"), 210 | }.map(|_| ()) 211 | } 212 | 213 | /// Like `Connection::prepare`. 214 | pub fn prepare(&self, query: &str) -> Result> { 215 | self.conn.prepare(query) 216 | } 217 | 218 | /// Like `Connection::prepare_cached`. 219 | /// 220 | /// Note that the statement will be cached for the duration of the 221 | /// connection, not just the duration of this transaction. 222 | pub fn prepare_cached(&self, query: &str) -> Result> { 223 | self.conn.prepare_cached(query) 224 | } 225 | 226 | /// Like `Connection::execute`. 227 | pub fn execute(&self, query: &str, params: &[&ToSql]) -> Result { 228 | self.conn.execute(query, params) 229 | } 230 | 231 | /// Like `Connection::query`. 232 | pub fn query<'a>(&'a self, query: &str, params: &[&ToSql]) -> Result> { 233 | self.conn.query(query, params) 234 | } 235 | 236 | /// Like `Connection::batch_execute`. 237 | pub fn batch_execute(&self, query: &str) -> Result<()> { 238 | self.conn.batch_execute(query) 239 | } 240 | 241 | /// Like `Connection::transaction`, but creates a nested transaction via 242 | /// a savepoint. 243 | /// 244 | /// # Panics 245 | /// 246 | /// Panics if there is an active nested transaction. 247 | pub fn transaction<'a>(&'a self) -> Result> { 248 | self.savepoint("sp") 249 | } 250 | 251 | /// Like `Connection::transaction`, but creates a nested transaction via 252 | /// a savepoint with the specified name. 253 | /// 254 | /// # Panics 255 | /// 256 | /// Panics if there is an active nested transaction. 257 | pub fn savepoint<'a>(&'a self, name: &str) -> Result> { 258 | let mut conn = self.conn.conn.borrow_mut(); 259 | check_desync!(conn); 260 | assert!(conn.trans_depth == self.depth, 261 | "`savepoint` may only be called on the active transaction"); 262 | try!(conn.quick_query(&format!("SAVEPOINT {}", name))); 263 | conn.trans_depth += 1; 264 | Ok(Transaction { 265 | conn: self.conn, 266 | depth: self.depth + 1, 267 | savepoint_name: Some(name.to_owned()), 268 | commit: Cell::new(false), 269 | finished: false, 270 | }) 271 | } 272 | 273 | /// Returns a reference to the `Transaction`'s `Connection`. 274 | pub fn connection(&self) -> &'conn Connection { 275 | self.conn 276 | } 277 | 278 | /// Like `Connection::is_active`. 279 | pub fn is_active(&self) -> bool { 280 | self.conn.conn.borrow().trans_depth == self.depth 281 | } 282 | 283 | /// Alters the configuration of the active transaction. 284 | pub fn set_config(&self, config: &Config) -> Result<()> { 285 | let mut command = "SET TRANSACTION".to_owned(); 286 | config.build_command(&mut command); 287 | self.batch_execute(&command) 288 | } 289 | 290 | /// Determines if the transaction is currently set to commit or roll back. 291 | pub fn will_commit(&self) -> bool { 292 | self.commit.get() 293 | } 294 | 295 | /// Sets the transaction to commit at its completion. 296 | pub fn set_commit(&self) { 297 | self.commit.set(true); 298 | } 299 | 300 | /// Sets the transaction to roll back at its completion. 301 | pub fn set_rollback(&self) { 302 | self.commit.set(false); 303 | } 304 | 305 | /// A convenience method which consumes and commits a transaction. 306 | pub fn commit(self) -> Result<()> { 307 | self.set_commit(); 308 | self.finish() 309 | } 310 | 311 | /// Consumes the transaction, commiting or rolling it back as appropriate. 312 | /// 313 | /// Functionally equivalent to the `Drop` implementation of `Transaction` 314 | /// except that it returns any error to the caller. 315 | pub fn finish(mut self) -> Result<()> { 316 | self.finished = true; 317 | self.finish_inner() 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /src/error/mod.rs: -------------------------------------------------------------------------------- 1 | //! Error types. 2 | 3 | use std::error; 4 | use std::convert::From; 5 | use std::fmt; 6 | use std::io; 7 | use std::result; 8 | use std::collections::HashMap; 9 | 10 | pub use self::sqlstate::SqlState; 11 | use {Result, DbErrorNew}; 12 | 13 | mod sqlstate; 14 | 15 | /// A Postgres error or notice. 16 | #[derive(Clone, PartialEq, Eq)] 17 | pub struct DbError { 18 | /// The field contents are ERROR, FATAL, or PANIC (in an error message), 19 | /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a 20 | /// localized translation of one of these. 21 | pub severity: String, 22 | 23 | /// The SQLSTATE code for the error. 24 | pub code: SqlState, 25 | 26 | /// The primary human-readable error message. This should be accurate but 27 | /// terse (typically one line). 28 | pub message: String, 29 | 30 | /// An optional secondary error message carrying more detail about the 31 | /// problem. Might run to multiple lines. 32 | pub detail: Option, 33 | 34 | /// An optional suggestion what to do about the problem. This is intended 35 | /// to differ from Detail in that it offers advice (potentially 36 | /// inappropriate) rather than hard facts. Might run to multiple lines. 37 | pub hint: Option, 38 | 39 | /// An optional error cursor position into either the original query string 40 | /// or an internally generated query. 41 | pub position: Option, 42 | 43 | /// An indication of the context in which the error occurred. Presently 44 | /// this includes a call stack traceback of active procedural language 45 | /// functions and internally-generated queries. The trace is one entry per 46 | /// line, most recent first. 47 | pub where_: Option, 48 | 49 | /// If the error was associated with a specific database object, the name 50 | /// of the schema containing that object, if any. (PostgreSQL 9.3+) 51 | pub schema: Option, 52 | 53 | /// If the error was associated with a specific table, the name of the 54 | /// table. (Refer to the schema name field for the name of the table's 55 | /// schema.) (PostgreSQL 9.3+) 56 | pub table: Option, 57 | 58 | /// If the error was associated with a specific table column, the name of 59 | /// the column. (Refer to the schema and table name fields to identify the 60 | /// table.) (PostgreSQL 9.3+) 61 | pub column: Option, 62 | 63 | /// If the error was associated with a specific data type, the name of the 64 | /// data type. (Refer to the schema name field for the name of the data 65 | /// type's schema.) (PostgreSQL 9.3+) 66 | pub datatype: Option, 67 | 68 | /// If the error was associated with a specific constraint, the name of the 69 | /// constraint. Refer to fields listed above for the associated table or 70 | /// domain. (For this purpose, indexes are treated as constraints, even if 71 | /// they weren't created with constraint syntax.) (PostgreSQL 9.3+) 72 | pub constraint: Option, 73 | 74 | /// The file name of the source-code location where the error was reported. 75 | pub file: String, 76 | 77 | /// The line number of the source-code location where the error was 78 | /// reported. 79 | pub line: u32, 80 | 81 | /// The name of the source-code routine reporting the error. 82 | pub routine: String, 83 | 84 | _p: (), 85 | } 86 | 87 | impl DbErrorNew for DbError { 88 | fn new_raw(fields: Vec<(u8, String)>) -> result::Result { 89 | let mut map: HashMap<_, _> = fields.into_iter().collect(); 90 | Ok(DbError { 91 | severity: try!(map.remove(&b'S').ok_or(())), 92 | code: SqlState::from_code(try!(map.remove(&b'C').ok_or(()))), 93 | message: try!(map.remove(&b'M').ok_or(())), 94 | detail: map.remove(&b'D'), 95 | hint: map.remove(&b'H'), 96 | position: match map.remove(&b'P') { 97 | Some(pos) => Some(ErrorPosition::Normal(try!(pos.parse().map_err(|_| ())))), 98 | None => { 99 | match map.remove(&b'p') { 100 | Some(pos) => { 101 | Some(ErrorPosition::Internal { 102 | position: try!(pos.parse().map_err(|_| ())), 103 | query: try!(map.remove(&b'q').ok_or(())), 104 | }) 105 | } 106 | None => None, 107 | } 108 | } 109 | }, 110 | where_: map.remove(&b'W'), 111 | schema: map.remove(&b's'), 112 | table: map.remove(&b't'), 113 | column: map.remove(&b'c'), 114 | datatype: map.remove(&b'd'), 115 | constraint: map.remove(&b'n'), 116 | file: try!(map.remove(&b'F').ok_or(())), 117 | line: try!(map.remove(&b'L').and_then(|l| l.parse().ok()).ok_or(())), 118 | routine: try!(map.remove(&b'R').ok_or(())), 119 | _p: (), 120 | }) 121 | } 122 | 123 | fn new_connect(fields: Vec<(u8, String)>) -> result::Result { 124 | match DbError::new_raw(fields) { 125 | Ok(err) => Err(ConnectError::Db(Box::new(err))), 126 | Err(()) => Err(ConnectError::Io(::bad_response())), 127 | } 128 | } 129 | 130 | fn new(fields: Vec<(u8, String)>) -> Result { 131 | match DbError::new_raw(fields) { 132 | Ok(err) => Err(Error::Db(Box::new(err))), 133 | Err(()) => Err(Error::Io(::bad_response())), 134 | } 135 | } 136 | } 137 | 138 | // manual impl to leave out _p 139 | impl fmt::Debug for DbError { 140 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 141 | fmt.debug_struct("DbError") 142 | .field("severity", &self.severity) 143 | .field("code", &self.code) 144 | .field("message", &self.message) 145 | .field("detail", &self.detail) 146 | .field("hint", &self.hint) 147 | .field("position", &self.position) 148 | .field("where_", &self.where_) 149 | .field("schema", &self.schema) 150 | .field("table", &self.table) 151 | .field("column", &self.column) 152 | .field("datatype", &self.datatype) 153 | .field("constraint", &self.constraint) 154 | .field("file", &self.file) 155 | .field("line", &self.line) 156 | .field("routine", &self.routine) 157 | .finish() 158 | } 159 | } 160 | 161 | impl fmt::Display for DbError { 162 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 163 | write!(fmt, "{}: {}", self.severity, self.message) 164 | } 165 | } 166 | 167 | impl error::Error for DbError { 168 | fn description(&self) -> &str { 169 | &self.message 170 | } 171 | } 172 | 173 | /// Reasons a new Postgres connection could fail. 174 | #[derive(Debug)] 175 | pub enum ConnectError { 176 | /// An error relating to connection parameters. 177 | ConnectParams(Box), 178 | /// An error from the Postgres server itself. 179 | Db(Box), 180 | /// An error initializing the SSL session. 181 | Ssl(Box), 182 | /// An error communicating with the server. 183 | Io(io::Error), 184 | } 185 | 186 | impl fmt::Display for ConnectError { 187 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 188 | try!(fmt.write_str(error::Error::description(self))); 189 | match *self { 190 | ConnectError::ConnectParams(ref msg) => write!(fmt, ": {}", msg), 191 | ConnectError::Db(ref err) => write!(fmt, ": {}", err), 192 | ConnectError::Ssl(ref err) => write!(fmt, ": {}", err), 193 | ConnectError::Io(ref err) => write!(fmt, ": {}", err), 194 | } 195 | } 196 | } 197 | 198 | impl error::Error for ConnectError { 199 | fn description(&self) -> &str { 200 | match *self { 201 | ConnectError::ConnectParams(_) => "Invalid connection parameters", 202 | ConnectError::Db(_) => "Error reported by Postgres", 203 | ConnectError::Ssl(_) => "Error initiating SSL session", 204 | ConnectError::Io(_) => "Error communicating with the server", 205 | } 206 | } 207 | 208 | fn cause(&self) -> Option<&error::Error> { 209 | match *self { 210 | ConnectError::ConnectParams(ref err) | ConnectError::Ssl(ref err) => Some(&**err), 211 | ConnectError::Db(ref err) => Some(&**err), 212 | ConnectError::Io(ref err) => Some(err), 213 | } 214 | } 215 | } 216 | 217 | impl From for ConnectError { 218 | fn from(err: io::Error) -> ConnectError { 219 | ConnectError::Io(err) 220 | } 221 | } 222 | 223 | impl From for ConnectError { 224 | fn from(err: DbError) -> ConnectError { 225 | ConnectError::Db(Box::new(err)) 226 | } 227 | } 228 | 229 | /// Represents the position of an error in a query. 230 | #[derive(Clone, PartialEq, Eq, Debug)] 231 | pub enum ErrorPosition { 232 | /// A position in the original query. 233 | Normal(u32), 234 | /// A position in an internally generated query. 235 | Internal { 236 | /// The byte position. 237 | position: u32, 238 | /// A query generated by the Postgres server. 239 | query: String, 240 | }, 241 | } 242 | 243 | /// An error encountered when communicating with the Postgres server. 244 | #[derive(Debug)] 245 | pub enum Error { 246 | /// An error reported by the Postgres server. 247 | Db(Box), 248 | /// An error communicating with the Postgres server. 249 | Io(io::Error), 250 | /// An error converting between Postgres and Rust types. 251 | Conversion(Box), 252 | } 253 | 254 | impl fmt::Display for Error { 255 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 256 | try!(fmt.write_str(error::Error::description(self))); 257 | match *self { 258 | Error::Db(ref err) => write!(fmt, ": {}", err), 259 | Error::Io(ref err) => write!(fmt, ": {}", err), 260 | Error::Conversion(ref err) => write!(fmt, ": {}", err), 261 | } 262 | } 263 | } 264 | 265 | impl error::Error for Error { 266 | fn description(&self) -> &str { 267 | match *self { 268 | Error::Db(_) => "Error reported by Postgres", 269 | Error::Io(_) => "Error communicating with the server", 270 | Error::Conversion(_) => "Error converting between Postgres and Rust types", 271 | } 272 | } 273 | 274 | fn cause(&self) -> Option<&error::Error> { 275 | match *self { 276 | Error::Db(ref err) => Some(&**err), 277 | Error::Io(ref err) => Some(err), 278 | Error::Conversion(ref err) => Some(&**err), 279 | } 280 | } 281 | } 282 | 283 | impl From for Error { 284 | fn from(err: DbError) -> Error { 285 | Error::Db(Box::new(err)) 286 | } 287 | } 288 | 289 | impl From for Error { 290 | fn from(err: io::Error) -> Error { 291 | Error::Io(err) 292 | } 293 | } 294 | 295 | impl From for io::Error { 296 | fn from(err: Error) -> io::Error { 297 | io::Error::new(io::ErrorKind::Other, err) 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust-Postgres 2 | A native PostgreSQL driver for Rust. 3 | 4 | [Documentation](https://sfackler.github.io/rust-postgres/doc/v0.11.10/postgres) 5 | 6 | [![Build Status](https://travis-ci.org/sfackler/rust-postgres.png?branch=master)](https://travis-ci.org/sfackler/rust-postgres) [![Latest Version](https://img.shields.io/crates/v/postgres.svg)](https://crates.io/crates/postgres) 7 | 8 | You can integrate Rust-Postgres into your project through the [releases on crates.io](https://crates.io/crates/postgres): 9 | ```toml 10 | # Cargo.toml 11 | [dependencies] 12 | postgres = "0.11" 13 | ``` 14 | 15 | ## Overview 16 | Rust-Postgres is a pure-Rust frontend for the popular PostgreSQL database. 17 | ```rust 18 | extern crate postgres; 19 | 20 | use postgres::{Connection, SslMode}; 21 | 22 | struct Person { 23 | id: i32, 24 | name: String, 25 | data: Option>, 26 | } 27 | 28 | fn main() { 29 | let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap(); 30 | conn.execute("CREATE TABLE person ( 31 | id SERIAL PRIMARY KEY, 32 | name VARCHAR NOT NULL, 33 | data BYTEA 34 | )", &[]).unwrap(); 35 | let me = Person { 36 | id: 0, 37 | name: "Steven".to_string(), 38 | data: None, 39 | }; 40 | conn.execute("INSERT INTO person (name, data) VALUES ($1, $2)", 41 | &[&me.name, &me.data]).unwrap(); 42 | for row in &conn.query("SELECT id, name, data FROM person", &[]).unwrap() { 43 | let person = Person { 44 | id: row.get(0), 45 | name: row.get(1), 46 | data: row.get(2), 47 | }; 48 | println!("Found person {}", person.name); 49 | } 50 | } 51 | ``` 52 | 53 | ## Requirements 54 | * **Rust** - Rust-Postgres is developed against the 1.9 release of Rust 55 | available on http://www.rust-lang.org. It should also compile against more 56 | recent releases. 57 | 58 | * **PostgreSQL 7.4 or later** - Rust-Postgres speaks version 3 of the 59 | PostgreSQL protocol, which corresponds to versions 7.4 and later. If your 60 | version of Postgres was compiled in the last decade, you should be okay. 61 | 62 | ## Usage 63 | 64 | ### Connecting 65 | Connect to a Postgres server using the standard URI format: 66 | ```rust 67 | let conn = try!(Connection::connect("postgres://user:pass@host:port/database?arg1=val1&arg2=val2", 68 | SslMode::None)); 69 | ``` 70 | `pass` may be omitted if not needed. `port` defaults to `5432` and `database` 71 | defaults to the value of `user` if not specified. The driver supports `trust`, 72 | `password`, and `md5` authentication. 73 | 74 | Unix domain sockets can be used as well by activating the `unix_socket` or 75 | `nightly` features. The `host` portion of the URI should be set to the absolute 76 | path to the directory containing the socket file. Since `/` is a reserved 77 | character in URLs, the path should be URL encoded. If Postgres stored its socket 78 | files in `/run/postgres`, the connection would then look like: 79 | ```rust 80 | let conn = try!(Connection::connect("postgres://postgres@%2Frun%2Fpostgres", SslMode::None)); 81 | ``` 82 | Paths which contain non-UTF8 characters can be handled in a different manner; 83 | see the documentation for details. 84 | 85 | ### Querying 86 | SQL statements can be executed with the `query` and `execute` methods. Both 87 | methods take a query string as well as a slice of parameters to bind to the 88 | query. The `i`th query parameter is specified in the query string by `$i`. Note 89 | that query parameters are 1-indexed rather than the more common 0-indexing. 90 | 91 | `execute` returns the number of rows affected by the query (or 0 if not 92 | applicable): 93 | ```rust 94 | let updates = try!(conn.execute("UPDATE foo SET bar = $1 WHERE baz = $2", &[&1i32, &"biz"])); 95 | println!("{} rows were updated", updates); 96 | ``` 97 | 98 | `query` returns an iterable object holding the rows returned from the database. 99 | The fields in a row can be accessed either by their indices or their column 100 | names, though access by index is more efficient. Unlike statement parameters, 101 | result columns are zero-indexed. 102 | ```rust 103 | for row in &try!(conn.query("SELECT bar, baz FROM foo WHERE buz = $1", &[&1i32])) { 104 | let bar: i32 = row.get(0); 105 | let baz: String = row.get("baz"); 106 | println!("bar: {}, baz: {}", bar, baz); 107 | } 108 | ``` 109 | 110 | ### Statement Preparation 111 | If the same statement will be executed repeatedly (possibly with different 112 | parameters), explicitly preparing it can improve performance: 113 | 114 | ```rust 115 | let stmt = try!(conn.prepare("UPDATE foo SET bar = $1 WHERE baz = $2")); 116 | for (bar, baz) in updates { 117 | try!(stmt.execute(&[bar, baz])); 118 | } 119 | ``` 120 | 121 | ### Transactions 122 | The `transaction` method will start a new transaction. It returns a 123 | `Transaction` object which has the functionality of a 124 | `Connection` as well as methods to control the result of the 125 | transaction: 126 | ```rust 127 | let trans = try!(conn.transaction()); 128 | 129 | try!(trans.execute(...)); 130 | let stmt = try!(trans.prepare(...)); 131 | // ... 132 | 133 | try!(trans.commit()); 134 | ``` 135 | The transaction will be active until the `Transaction` object falls out of 136 | scope. A transaction will roll back by default. Nested transactions are 137 | supported via savepoints. 138 | 139 | ### Type Correspondence 140 | Rust-Postgres enforces a strict correspondence between Rust types and Postgres 141 | types. The driver currently supports the following conversions: 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 198 | 199 | 200 | 201 | 207 | 208 | 209 | 210 | 218 | 219 | 220 | 221 | 225 | 226 | 227 | 228 | 232 | 233 | 234 | 235 | 239 | 240 | 241 | 242 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 257 | 258 | 259 | 260 |
Rust TypePostgres Type
boolBOOL
i8"char"
i16SMALLINT, SMALLSERIAL
i32INT, SERIAL
u32OID
i64BIGINT, BIGSERIAL
f32REAL
f64DOUBLE PRECISION
str/StringVARCHAR, CHAR(n), TEXT, CITEXT
[u8]/Vec<u8>BYTEA
193 | serialize::json::Json 194 | and 195 | serde_json::Value 196 | (optional) 197 | JSON, JSONB
202 | time::Timespec 203 | and 204 | chrono::NaiveDateTime 205 | (optional) 206 | TIMESTAMP
211 | time::Timespec, 212 | chrono::DateTime<UTC>, 213 | chrono::DateTime<Local>, 214 | and 215 | chrono::DateTime<FixedOffset> 216 | (optional) 217 | TIMESTAMP WITH TIME ZONE
222 | chrono::NaiveDate 223 | (optional) 224 | DATE
229 | chrono::NaiveTime 230 | (optional) 231 | TIME
236 | uuid::Uuid 237 | (optional) 238 | UUID
243 | bit_vec::BitVec 244 | (optional) 245 | BIT, VARBIT
HashMap<String, Option<String>>HSTORE
254 | eui48::MacAddress 255 | (optional) 256 | MACADDR
261 | 262 | `Option` implements `FromSql` where `T: FromSql` and `ToSql` where `T: 263 | ToSql`, and represents nullable Postgres values. 264 | 265 | `&[T]` and `Vec` implement `ToSql` where `T: ToSql`, and `Vec` 266 | additionally implements `FromSql` where `T: FromSql`, which represent 267 | one-dimensional Postgres arrays. 268 | 269 | More conversions can be defined by implementing the `ToSql` and `FromSql` 270 | traits. 271 | 272 | The [postgres-derive](https://github.com/sfackler/rust-postgres-derive) 273 | crate will synthesize `ToSql` and `FromSql` implementations for enum, domain, 274 | and composite Postgres types. 275 | 276 | Full support for array types is located in the 277 | [postgres-array](https://github.com/sfackler/rust-postgres-array) crate. 278 | 279 | Support for range types is located in the 280 | [postgres-range](https://github.com/sfackler/rust-postgres-range) crate. 281 | 282 | Support for the large object API is located in the 283 | [postgres-large-object](https://github.com/sfackler/rust-postgres-large-object) 284 | crate. 285 | 286 | ## Optional features 287 | 288 | ### Unix socket connections 289 | 290 | Support for connections through Unix domain sockets is provided optionally by 291 | either the `unix_socket` or `nightly` features. It is only available on "unixy" 292 | platforms such as OSX, BSD and Linux. 293 | 294 | ### UUID type 295 | 296 | [UUID](http://www.postgresql.org/docs/9.4/static/datatype-uuid.html) support is 297 | provided optionally by the `uuid` feature, which adds `ToSql` and `FromSql` 298 | implementations for `uuid`'s `Uuid` type. 299 | 300 | ### JSON/JSONB types 301 | 302 | [JSON and JSONB](http://www.postgresql.org/docs/9.4/static/datatype-json.html) 303 | support is provided optionally by the `rustc-serialize` feature, which adds 304 | `ToSql` and `FromSql` implementations for `rustc-serialize`'s `Json` type, and 305 | the `serde_json` feature, which adds implementations for `serde_json`'s `Value` 306 | type. 307 | 308 | ### TIMESTAMP/TIMESTAMPTZ/DATE/TIME types 309 | 310 | [Date and Time](http://www.postgresql.org/docs/9.1/static/datatype-datetime.html) 311 | support is provided optionally by the `time` feature, which adds `ToSql` and 312 | `FromSql` implementations for `time`'s `Timespec` type, or the `chrono` 313 | feature, which adds `ToSql` and `FromSql` implementations for `chrono`'s 314 | `DateTime`, `NaiveDateTime`, `NaiveDate` and `NaiveTime` types. 315 | 316 | ### BIT/VARBIT types 317 | 318 | [BIT and VARBIT](http://www.postgresql.org/docs/9.4/static/datatype-bit.html) 319 | support is provided optionally by the `bit-vec` feature, which adds `ToSql` and 320 | `FromSql` implementations for `bit-vec`'s `BitVec` type. 321 | 322 | ### MACADDR type 323 | 324 | [MACADDR](http://www.postgresql.org/docs/9.4/static/datatype-net-types.html#DATATYPE-MACADDR) 325 | support is provided optionally by the `eui48` feature, which adds `ToSql` and 326 | `FromSql` implementations for `eui48`'s `MacAddress` type. 327 | -------------------------------------------------------------------------------- /src/rows.rs: -------------------------------------------------------------------------------- 1 | //! Query result rows. 2 | 3 | use std::ascii::AsciiExt; 4 | use std::borrow::Cow; 5 | use std::collections::VecDeque; 6 | use std::fmt; 7 | use std::ops::Deref; 8 | use std::slice; 9 | 10 | use {Result, Transaction, SessionInfoNew, RowsNew, LazyRowsNew, StatementInternals, 11 | WrongTypeNew}; 12 | use types::{FromSql, SessionInfo, WrongType}; 13 | use stmt::{Statement, Column}; 14 | use error::Error; 15 | use message::Frontend; 16 | 17 | enum StatementContainer<'a> { 18 | Borrowed(&'a Statement<'a>), 19 | Owned(Statement<'a>), 20 | } 21 | 22 | impl<'a> Deref for StatementContainer<'a> { 23 | type Target = Statement<'a>; 24 | 25 | fn deref(&self) -> &Statement<'a> { 26 | match *self { 27 | StatementContainer::Borrowed(s) => s, 28 | StatementContainer::Owned(ref s) => s, 29 | } 30 | } 31 | } 32 | 33 | /// The resulting rows of a query. 34 | pub struct Rows<'stmt> { 35 | stmt: StatementContainer<'stmt>, 36 | data: Vec>>>, 37 | } 38 | 39 | impl<'a> RowsNew<'a> for Rows<'a> { 40 | fn new(stmt: &'a Statement<'a>, data: Vec>>>) -> Rows<'a> { 41 | Rows { 42 | stmt: StatementContainer::Borrowed(stmt), 43 | data: data, 44 | } 45 | } 46 | 47 | fn new_owned(stmt: Statement<'a>, data: Vec>>>) -> Rows<'a> { 48 | Rows { 49 | stmt: StatementContainer::Owned(stmt), 50 | data: data, 51 | } 52 | } 53 | } 54 | 55 | impl<'a> fmt::Debug for Rows<'a> { 56 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 57 | fmt.debug_struct("Rows") 58 | .field("columns", &self.columns()) 59 | .field("rows", &self.data.len()) 60 | .finish() 61 | } 62 | } 63 | 64 | impl<'stmt> Rows<'stmt> { 65 | /// Returns a slice describing the columns of the `Rows`. 66 | pub fn columns(&self) -> &[Column] { 67 | self.stmt.columns() 68 | } 69 | 70 | /// Returns the number of rows present. 71 | pub fn len(&self) -> usize { 72 | self.data.len() 73 | } 74 | 75 | /// Determines if there are any rows present. 76 | pub fn is_empty(&self) -> bool { 77 | self.len() == 0 78 | } 79 | 80 | /// Returns a specific `Row`. 81 | /// 82 | /// # Panics 83 | /// 84 | /// Panics if `idx` is out of bounds. 85 | pub fn get<'a>(&'a self, idx: usize) -> Row<'a> { 86 | Row { 87 | stmt: &*self.stmt, 88 | data: Cow::Borrowed(&self.data[idx]), 89 | } 90 | } 91 | 92 | /// Returns an iterator over the `Row`s. 93 | pub fn iter<'a>(&'a self) -> Iter<'a> { 94 | Iter { 95 | stmt: &*self.stmt, 96 | iter: self.data.iter(), 97 | } 98 | } 99 | } 100 | 101 | impl<'a> IntoIterator for &'a Rows<'a> { 102 | type Item = Row<'a>; 103 | type IntoIter = Iter<'a>; 104 | 105 | fn into_iter(self) -> Iter<'a> { 106 | self.iter() 107 | } 108 | } 109 | 110 | /// An iterator over `Row`s. 111 | pub struct Iter<'a> { 112 | stmt: &'a Statement<'a>, 113 | iter: slice::Iter<'a, Vec>>>, 114 | } 115 | 116 | impl<'a> Iterator for Iter<'a> { 117 | type Item = Row<'a>; 118 | 119 | fn next(&mut self) -> Option> { 120 | self.iter.next().map(|row| { 121 | Row { 122 | stmt: &*self.stmt, 123 | data: Cow::Borrowed(row), 124 | } 125 | }) 126 | } 127 | 128 | fn size_hint(&self) -> (usize, Option) { 129 | self.iter.size_hint() 130 | } 131 | } 132 | 133 | impl<'a> DoubleEndedIterator for Iter<'a> { 134 | fn next_back(&mut self) -> Option> { 135 | self.iter.next_back().map(|row| { 136 | Row { 137 | stmt: &*self.stmt, 138 | data: Cow::Borrowed(row), 139 | } 140 | }) 141 | } 142 | } 143 | 144 | impl<'a> ExactSizeIterator for Iter<'a> {} 145 | 146 | /// A single result row of a query. 147 | pub struct Row<'a> { 148 | stmt: &'a Statement<'a>, 149 | data: Cow<'a, [Option>]>, 150 | } 151 | 152 | impl<'a> fmt::Debug for Row<'a> { 153 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 154 | fmt.debug_struct("Row") 155 | .field("statement", self.stmt) 156 | .finish() 157 | } 158 | } 159 | 160 | impl<'a> Row<'a> { 161 | /// Returns the number of values in the row. 162 | pub fn len(&self) -> usize { 163 | self.data.len() 164 | } 165 | 166 | /// Determines if there are any values in the row. 167 | pub fn is_empty(&self) -> bool { 168 | self.len() == 0 169 | } 170 | 171 | /// Returns a slice describing the columns of the `Row`. 172 | pub fn columns(&self) -> &[Column] { 173 | self.stmt.columns() 174 | } 175 | 176 | /// Retrieves the contents of a field of the row. 177 | /// 178 | /// A field can be accessed by the name or index of its column, though 179 | /// access by index is more efficient. Rows are 0-indexed. 180 | /// 181 | /// # Panics 182 | /// 183 | /// Panics if the index does not reference a column or the return type is 184 | /// not compatible with the Postgres type. 185 | /// 186 | /// # Example 187 | /// 188 | /// ```rust,no_run 189 | /// # use postgres::{Connection, SslMode}; 190 | /// # let conn = Connection::connect("", SslMode::None).unwrap(); 191 | /// let stmt = conn.prepare("SELECT foo, bar from BAZ").unwrap(); 192 | /// for row in &stmt.query(&[]).unwrap() { 193 | /// let foo: i32 = row.get(0); 194 | /// let bar: String = row.get("bar"); 195 | /// println!("{}: {}", foo, bar); 196 | /// } 197 | /// ``` 198 | pub fn get(&self, idx: I) -> T 199 | where I: RowIndex + fmt::Debug, 200 | T: FromSql 201 | { 202 | match self.get_inner(&idx) { 203 | Some(Ok(ok)) => ok, 204 | Some(Err(err)) => panic!("error retrieving column {:?}: {:?}", idx, err), 205 | None => panic!("no such column {:?}", idx), 206 | } 207 | } 208 | 209 | /// Retrieves the contents of a field of the row. 210 | /// 211 | /// A field can be accessed by the name or index of its column, though 212 | /// access by index is more efficient. Rows are 0-indexed. 213 | /// 214 | /// Returns `None` if the index does not reference a column, `Some(Err(..))` 215 | /// if there was an error converting the result value, and `Some(Ok(..))` 216 | /// on success. 217 | pub fn get_opt(&self, idx: I) -> Option> 218 | where I: RowIndex, 219 | T: FromSql 220 | { 221 | self.get_inner(&idx) 222 | } 223 | 224 | fn get_inner(&self, idx: &I) -> Option> 225 | where I: RowIndex, 226 | T: FromSql 227 | { 228 | let idx = match idx.idx(self.stmt) { 229 | Some(idx) => idx, 230 | None => return None, 231 | }; 232 | 233 | let ty = self.stmt.columns()[idx].type_(); 234 | if !::accepts(ty) { 235 | return Some(Err(Error::Conversion(Box::new(WrongType::new(ty.clone()))))); 236 | } 237 | let conn = self.stmt.conn().conn.borrow(); 238 | let value = match self.data[idx] { 239 | Some(ref data) => FromSql::from_sql(ty, &mut &**data, &SessionInfo::new(&*conn)), 240 | None => FromSql::from_sql_null(ty, &SessionInfo::new(&*conn)), 241 | }; 242 | Some(value) 243 | } 244 | 245 | /// Retrieves the specified field as a raw buffer of Postgres data. 246 | /// 247 | /// # Panics 248 | /// 249 | /// Panics if the index does not reference a column. 250 | pub fn get_bytes(&self, idx: I) -> Option<&[u8]> 251 | where I: RowIndex + fmt::Debug 252 | { 253 | match idx.idx(self.stmt) { 254 | Some(idx) => self.data[idx].as_ref().map(|e| &**e), 255 | None => panic!("invalid index {:?}", idx), 256 | } 257 | } 258 | } 259 | 260 | /// A trait implemented by types that can index into columns of a row. 261 | pub trait RowIndex { 262 | /// Returns the index of the appropriate column, or `None` if no such 263 | /// column exists. 264 | fn idx(&self, stmt: &Statement) -> Option; 265 | } 266 | 267 | impl RowIndex for usize { 268 | #[inline] 269 | fn idx(&self, stmt: &Statement) -> Option { 270 | if *self >= stmt.columns().len() { 271 | None 272 | } else { 273 | Some(*self) 274 | } 275 | } 276 | } 277 | 278 | impl<'a> RowIndex for &'a str { 279 | #[inline] 280 | fn idx(&self, stmt: &Statement) -> Option { 281 | if let Some(idx) = stmt.columns().iter().position(|d| d.name() == *self) { 282 | return Some(idx); 283 | }; 284 | 285 | // FIXME ASCII-only case insensitivity isn't really the right thing to 286 | // do. Postgres itself uses a dubious wrapper around tolower and JDBC 287 | // uses the US locale. 288 | stmt.columns().iter().position(|d| d.name().eq_ignore_ascii_case(*self)) 289 | } 290 | } 291 | 292 | /// A lazily-loaded iterator over the resulting rows of a query. 293 | pub struct LazyRows<'trans, 'stmt> { 294 | stmt: &'stmt Statement<'stmt>, 295 | data: VecDeque>>>, 296 | name: String, 297 | row_limit: i32, 298 | more_rows: bool, 299 | finished: bool, 300 | _trans: &'trans Transaction<'trans>, 301 | } 302 | 303 | impl<'trans, 'stmt> LazyRowsNew<'trans, 'stmt> for LazyRows<'trans, 'stmt> { 304 | fn new(stmt: &'stmt Statement<'stmt>, 305 | data: VecDeque>>>, 306 | name: String, 307 | row_limit: i32, 308 | more_rows: bool, 309 | finished: bool, 310 | trans: &'trans Transaction<'trans>) 311 | -> LazyRows<'trans, 'stmt> { 312 | LazyRows { 313 | stmt: stmt, 314 | data: data, 315 | name: name, 316 | row_limit: row_limit, 317 | more_rows: more_rows, 318 | finished: finished, 319 | _trans: trans, 320 | } 321 | } 322 | } 323 | 324 | impl<'a, 'b> Drop for LazyRows<'a, 'b> { 325 | fn drop(&mut self) { 326 | if !self.finished { 327 | let _ = self.finish_inner(); 328 | } 329 | } 330 | } 331 | 332 | impl<'a, 'b> fmt::Debug for LazyRows<'a, 'b> { 333 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 334 | fmt.debug_struct("LazyRows") 335 | .field("name", &self.name) 336 | .field("row_limit", &self.row_limit) 337 | .field("remaining_rows", &self.data.len()) 338 | .field("more_rows", &self.more_rows) 339 | .finish() 340 | } 341 | } 342 | 343 | impl<'trans, 'stmt> LazyRows<'trans, 'stmt> { 344 | fn finish_inner(&mut self) -> Result<()> { 345 | let mut conn = self.stmt.conn().conn.borrow_mut(); 346 | check_desync!(conn); 347 | conn.close_statement(&self.name, b'P') 348 | } 349 | 350 | fn execute(&mut self) -> Result<()> { 351 | let mut conn = self.stmt.conn().conn.borrow_mut(); 352 | 353 | try!(conn.write_messages(&[Frontend::Execute { 354 | portal: &self.name, 355 | max_rows: self.row_limit, 356 | }, 357 | Frontend::Sync])); 358 | conn.read_rows(&mut self.data).map(|more_rows| self.more_rows = more_rows) 359 | } 360 | 361 | /// Returns a slice describing the columns of the `LazyRows`. 362 | pub fn columns(&self) -> &[Column] { 363 | self.stmt.columns() 364 | } 365 | 366 | /// Consumes the `LazyRows`, cleaning up associated state. 367 | /// 368 | /// Functionally identical to the `Drop` implementation on `LazyRows` 369 | /// except that it returns any error to the caller. 370 | pub fn finish(mut self) -> Result<()> { 371 | self.finish_inner() 372 | } 373 | } 374 | 375 | impl<'trans, 'stmt> Iterator for LazyRows<'trans, 'stmt> { 376 | type Item = Result>; 377 | 378 | fn next(&mut self) -> Option>> { 379 | if self.data.is_empty() && self.more_rows { 380 | if let Err(err) = self.execute() { 381 | return Some(Err(err)); 382 | } 383 | } 384 | 385 | self.data.pop_front().map(|r| { 386 | Ok(Row { 387 | stmt: self.stmt, 388 | data: Cow::Owned(r), 389 | }) 390 | }) 391 | } 392 | 393 | fn size_hint(&self) -> (usize, Option) { 394 | let lower = self.data.len(); 395 | let upper = if self.more_rows { 396 | None 397 | } else { 398 | Some(lower) 399 | }; 400 | (lower, upper) 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /tests/types/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::f32; 3 | use std::f64; 4 | use std::fmt; 5 | use std::io::{Read, Write}; 6 | 7 | use postgres::{Connection, SslMode, Result}; 8 | use postgres::error::Error; 9 | use postgres::types::{ToSql, FromSql, WrongType, Type, IsNull, Kind, SessionInfo}; 10 | 11 | #[cfg(feature = "bit-vec")] 12 | mod bit_vec; 13 | #[cfg(feature = "eui48")] 14 | mod eui48; 15 | #[cfg(feature = "uuid")] 16 | mod uuid; 17 | #[cfg(feature = "time")] 18 | mod time; 19 | #[cfg(feature = "rustc-serialize")] 20 | mod rustc_serialize; 21 | #[cfg(feature = "serde_json")] 22 | mod serde_json; 23 | #[cfg(feature = "chrono")] 24 | mod chrono; 25 | 26 | fn test_type(sql_type: &str, checks: &[(T, S)]) { 27 | let conn = or_panic!(Connection::connect("postgres://postgres@localhost", SslMode::None)); 28 | for &(ref val, ref repr) in checks.iter() { 29 | let stmt = or_panic!(conn.prepare(&*format!("SELECT {}::{}", *repr, sql_type))); 30 | let result = or_panic!(stmt.query(&[])).iter().next().unwrap().get(0); 31 | assert_eq!(val, &result); 32 | 33 | let stmt = or_panic!(conn.prepare(&*format!("SELECT $1::{}", sql_type))); 34 | let result = or_panic!(stmt.query(&[val])).iter().next().unwrap().get(0); 35 | assert_eq!(val, &result); 36 | } 37 | } 38 | 39 | #[test] 40 | fn test_ref_tosql() { 41 | let conn = or_panic!(Connection::connect("postgres://postgres@localhost", SslMode::None)); 42 | let stmt = conn.prepare("SELECT $1::Int").unwrap(); 43 | let num: &ToSql = &&7; 44 | stmt.query(&[num]).unwrap(); 45 | } 46 | 47 | #[test] 48 | fn test_bool_params() { 49 | test_type("BOOL", &[(Some(true), "'t'"), (Some(false), "'f'"), 50 | (None, "NULL")]); 51 | } 52 | 53 | #[test] 54 | fn test_i8_params() { 55 | test_type("\"char\"", &[(Some('a' as i8), "'a'"), (None, "NULL")]); 56 | } 57 | 58 | #[test] 59 | fn test_name_params() { 60 | test_type("NAME", &[(Some("hello world".to_owned()), "'hello world'"), 61 | (Some("イロハニホヘト チリヌルヲ".to_owned()), "'イロハニホヘト チリヌルヲ'"), 62 | (None, "NULL")]); 63 | } 64 | 65 | #[test] 66 | fn test_i16_params() { 67 | test_type("SMALLINT", &[(Some(15001i16), "15001"), 68 | (Some(-15001i16), "-15001"), (None, "NULL")]); 69 | } 70 | 71 | #[test] 72 | fn test_i32_params() { 73 | test_type("INT", &[(Some(2147483548i32), "2147483548"), 74 | (Some(-2147483548i32), "-2147483548"), (None, "NULL")]); 75 | } 76 | 77 | #[test] 78 | fn test_oid_params() { 79 | test_type("OID", &[(Some(2147483548u32), "2147483548"), 80 | (Some(4000000000), "4000000000"), (None, "NULL")]); 81 | } 82 | 83 | #[test] 84 | fn test_i64_params() { 85 | test_type("BIGINT", &[(Some(9223372036854775708i64), "9223372036854775708"), 86 | (Some(-9223372036854775708i64), "-9223372036854775708"), 87 | (None, "NULL")]); 88 | } 89 | 90 | #[test] 91 | fn test_f32_params() { 92 | test_type("REAL", &[(Some(f32::INFINITY), "'infinity'"), 93 | (Some(f32::NEG_INFINITY), "'-infinity'"), 94 | (Some(1000.55), "1000.55"), (None, "NULL")]); 95 | } 96 | 97 | #[test] 98 | fn test_f64_params() { 99 | test_type("DOUBLE PRECISION", &[(Some(f64::INFINITY), "'infinity'"), 100 | (Some(f64::NEG_INFINITY), "'-infinity'"), 101 | (Some(10000.55), "10000.55"), 102 | (None, "NULL")]); 103 | } 104 | 105 | #[test] 106 | fn test_varchar_params() { 107 | test_type("VARCHAR", &[(Some("hello world".to_owned()), "'hello world'"), 108 | (Some("イロハニホヘト チリヌルヲ".to_owned()), "'イロハニホヘト チリヌルヲ'"), 109 | (None, "NULL")]); 110 | } 111 | 112 | #[test] 113 | fn test_text_params() { 114 | test_type("TEXT", &[(Some("hello world".to_owned()), "'hello world'"), 115 | (Some("イロハニホヘト チリヌルヲ".to_owned()), "'イロハニホヘト チリヌルヲ'"), 116 | (None, "NULL")]); 117 | } 118 | 119 | #[test] 120 | fn test_bpchar_params() { 121 | let conn = or_panic!(Connection::connect("postgres://postgres@localhost", SslMode::None)); 122 | or_panic!(conn.execute("CREATE TEMPORARY TABLE foo ( 123 | id SERIAL PRIMARY KEY, 124 | b CHAR(5) 125 | )", &[])); 126 | or_panic!(conn.execute("INSERT INTO foo (b) VALUES ($1), ($2), ($3)", 127 | &[&Some("12345"), &Some("123"), &None::<&'static str>])); 128 | let stmt = or_panic!(conn.prepare("SELECT b FROM foo ORDER BY id")); 129 | let res = or_panic!(stmt.query(&[])); 130 | 131 | assert_eq!(vec!(Some("12345".to_owned()), Some("123 ".to_owned()), None), 132 | res.iter().map(|row| row.get(0)).collect::>()); 133 | } 134 | 135 | #[test] 136 | fn test_citext_params() { 137 | let conn = or_panic!(Connection::connect("postgres://postgres@localhost", SslMode::None)); 138 | or_panic!(conn.execute("CREATE TEMPORARY TABLE foo ( 139 | id SERIAL PRIMARY KEY, 140 | b CITEXT 141 | )", &[])); 142 | or_panic!(conn.execute("INSERT INTO foo (b) VALUES ($1), ($2), ($3)", 143 | &[&Some("foobar"), &Some("FooBar"), &None::<&'static str>])); 144 | let stmt = or_panic!(conn.prepare("SELECT id FROM foo WHERE b = 'FOOBAR' ORDER BY id")); 145 | let res = or_panic!(stmt.query(&[])); 146 | 147 | assert_eq!(vec!(Some(1i32), Some(2i32)), res.iter().map(|row| row.get(0)).collect::>()); 148 | } 149 | 150 | #[test] 151 | fn test_bytea_params() { 152 | test_type("BYTEA", &[(Some(vec!(0u8, 1, 2, 3, 254, 255)), "'\\x00010203feff'"), 153 | (None, "NULL")]); 154 | } 155 | 156 | #[test] 157 | fn test_hstore_params() { 158 | macro_rules! make_map { 159 | ($($k:expr => $v:expr),+) => ({ 160 | let mut map = HashMap::new(); 161 | $(map.insert($k, $v);)+ 162 | map 163 | }) 164 | } 165 | test_type("hstore", 166 | &[(Some(make_map!("a".to_owned() => Some("1".to_owned()))), "'a=>1'"), 167 | (Some(make_map!("hello".to_owned() => Some("world!".to_owned()), 168 | "hola".to_owned() => Some("mundo!".to_owned()), 169 | "what".to_owned() => None)), 170 | "'hello=>world!,hola=>mundo!,what=>NULL'"), 171 | (None, "NULL")]); 172 | } 173 | 174 | #[test] 175 | fn test_array_params() { 176 | test_type("integer[]", &[(Some(vec!(1i32, 2i32)), "ARRAY[1,2]"), 177 | (Some(vec!(1i32)), "ARRAY[1]"), 178 | (Some(vec!()), "ARRAY[]"), 179 | (None, "NULL")]); 180 | } 181 | 182 | fn test_nan_param(sql_type: &str) { 183 | let conn = or_panic!(Connection::connect("postgres://postgres@localhost", SslMode::None)); 184 | let stmt = or_panic!(conn.prepare(&*format!("SELECT 'NaN'::{}", sql_type))); 185 | let result = or_panic!(stmt.query(&[])); 186 | let val: T = result.iter().next().unwrap().get(0); 187 | assert!(val != val); 188 | } 189 | 190 | #[test] 191 | fn test_f32_nan_param() { 192 | test_nan_param::("REAL"); 193 | } 194 | 195 | #[test] 196 | fn test_f64_nan_param() { 197 | test_nan_param::("DOUBLE PRECISION"); 198 | } 199 | 200 | #[test] 201 | fn test_pg_database_datname() { 202 | let conn = or_panic!(Connection::connect("postgres://postgres@localhost", SslMode::None)); 203 | let stmt = or_panic!(conn.prepare("SELECT datname FROM pg_database")); 204 | let result = or_panic!(stmt.query(&[])); 205 | 206 | let next = result.iter().next().unwrap(); 207 | or_panic!(next.get_opt::<_, String>(0).unwrap()); 208 | or_panic!(next.get_opt::<_, String>("datname").unwrap()); 209 | } 210 | 211 | #[test] 212 | fn test_slice() { 213 | let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap(); 214 | conn.batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY, f VARCHAR); 215 | INSERT INTO foo (f) VALUES ('a'), ('b'), ('c'), ('d');").unwrap(); 216 | 217 | let stmt = conn.prepare("SELECT f FROM foo WHERE id = ANY($1)").unwrap(); 218 | let result = stmt.query(&[&&[1i32, 3, 4][..]]).unwrap(); 219 | assert_eq!(vec!["a".to_owned(), "c".to_owned(), "d".to_owned()], 220 | result.iter().map(|r| r.get::<_, String>(0)).collect::>()); 221 | } 222 | 223 | #[test] 224 | fn test_slice_wrong_type() { 225 | let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap(); 226 | conn.batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY)").unwrap(); 227 | 228 | let stmt = conn.prepare("SELECT * FROM foo WHERE id = ANY($1)").unwrap(); 229 | match stmt.query(&[&&["hi"][..]]) { 230 | Ok(_) => panic!("Unexpected success"), 231 | Err(Error::Conversion(ref e)) if e.is::() => {} 232 | Err(e) => panic!("Unexpected error {:?}", e), 233 | }; 234 | } 235 | 236 | #[test] 237 | fn test_slice_range() { 238 | let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap(); 239 | 240 | let stmt = conn.prepare("SELECT $1::INT8RANGE").unwrap(); 241 | match stmt.query(&[&&[1i64][..]]) { 242 | Ok(_) => panic!("Unexpected success"), 243 | Err(Error::Conversion(ref e)) if e.is::() => {} 244 | Err(e) => panic!("Unexpected error {:?}", e), 245 | }; 246 | } 247 | 248 | #[test] 249 | fn domain() { 250 | #[derive(Debug, PartialEq)] 251 | struct SessionId(Vec); 252 | 253 | impl ToSql for SessionId { 254 | fn to_sql(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result 255 | where W: Write 256 | { 257 | let inner = match *ty.kind() { 258 | Kind::Domain(ref inner) => inner, 259 | _ => unreachable!(), 260 | }; 261 | self.0.to_sql(inner, out, ctx) 262 | } 263 | 264 | fn accepts(ty: &Type) -> bool { 265 | ty.name() == "session_id" && match *ty.kind() { Kind::Domain(_) => true, _ => false } 266 | } 267 | 268 | to_sql_checked!(); 269 | } 270 | 271 | impl FromSql for SessionId { 272 | fn from_sql(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result { 273 | Vec::::from_sql(ty, raw, ctx).map(SessionId) 274 | } 275 | 276 | fn accepts(ty: &Type) -> bool { 277 | // This is super weird! 278 | as FromSql>::accepts(ty) 279 | } 280 | } 281 | 282 | let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap(); 283 | conn.batch_execute("CREATE DOMAIN pg_temp.session_id AS bytea CHECK(octet_length(VALUE) = 16); 284 | CREATE TABLE pg_temp.foo (id pg_temp.session_id);") 285 | .unwrap(); 286 | 287 | let id = SessionId(b"0123456789abcdef".to_vec()); 288 | conn.execute("INSERT INTO pg_temp.foo (id) VALUES ($1)", &[&id]).unwrap(); 289 | let rows = conn.query("SELECT id FROM pg_temp.foo", &[]).unwrap(); 290 | assert_eq!(id, rows.get(0).get(0)); 291 | } 292 | 293 | #[test] 294 | fn composite() { 295 | let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap(); 296 | conn.batch_execute("CREATE TYPE pg_temp.inventory_item AS ( 297 | name TEXT, 298 | supplier INTEGER, 299 | price NUMERIC 300 | )") 301 | .unwrap(); 302 | 303 | let stmt = conn.prepare("SELECT $1::inventory_item").unwrap(); 304 | let type_ = &stmt.param_types()[0]; 305 | assert_eq!(type_.name(), "inventory_item"); 306 | match type_.kind() { 307 | &Kind::Composite(ref fields) => { 308 | assert_eq!(fields[0].name(), "name"); 309 | assert_eq!(fields[0].type_(), &Type::Text); 310 | assert_eq!(fields[1].name(), "supplier"); 311 | assert_eq!(fields[1].type_(), &Type::Int4); 312 | assert_eq!(fields[2].name(), "price"); 313 | assert_eq!(fields[2].type_(), &Type::Numeric); 314 | } 315 | t => panic!("bad type {:?}", t), 316 | } 317 | } 318 | 319 | #[test] 320 | fn enum_() { 321 | let conn = Connection::connect("postgres://postgres@localhost", SslMode::None).unwrap(); 322 | conn.batch_execute("CREATE TYPE pg_temp.mood AS ENUM ('sad', 'ok', 'happy');").unwrap(); 323 | 324 | let stmt = conn.prepare("SELECT $1::mood").unwrap(); 325 | let type_ = &stmt.param_types()[0]; 326 | assert_eq!(type_.name(), "mood"); 327 | match type_.kind() { 328 | &Kind::Enum(ref variants) => { 329 | assert_eq!(variants, &["sad".to_owned(), "ok".to_owned(), "happy".to_owned()]); 330 | } 331 | _ => panic!("bad type"), 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /src/url.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | use std::str::FromStr; 11 | use hex::FromHex; 12 | 13 | pub struct Url { 14 | pub scheme: String, 15 | pub user: Option, 16 | pub host: String, 17 | pub port: Option, 18 | pub path: Path, 19 | } 20 | 21 | pub struct Path { 22 | pub path: String, 23 | pub query: Query, 24 | pub fragment: Option, 25 | } 26 | 27 | pub struct UserInfo { 28 | pub user: String, 29 | pub pass: Option, 30 | } 31 | 32 | pub type Query = Vec<(String, String)>; 33 | 34 | impl Url { 35 | pub fn new(scheme: String, 36 | user: Option, 37 | host: String, 38 | port: Option, 39 | path: String, 40 | query: Query, 41 | fragment: Option) 42 | -> Url { 43 | Url { 44 | scheme: scheme, 45 | user: user, 46 | host: host, 47 | port: port, 48 | path: Path::new(path, query, fragment), 49 | } 50 | } 51 | 52 | pub fn parse(rawurl: &str) -> DecodeResult { 53 | // scheme 54 | let (scheme, rest) = try!(get_scheme(rawurl)); 55 | 56 | // authority 57 | let (userinfo, host, port, rest) = try!(get_authority(rest)); 58 | 59 | // path 60 | let has_authority = !host.is_empty(); 61 | let (path, rest) = try!(get_path(rest, has_authority)); 62 | 63 | // query and fragment 64 | let (query, fragment) = try!(get_query_fragment(rest)); 65 | 66 | let url = Url::new(scheme.to_owned(), 67 | userinfo, 68 | host.to_owned(), 69 | port, 70 | path, 71 | query, 72 | fragment); 73 | Ok(url) 74 | } 75 | } 76 | 77 | impl Path { 78 | pub fn new(path: String, query: Query, fragment: Option) -> Path { 79 | Path { 80 | path: path, 81 | query: query, 82 | fragment: fragment, 83 | } 84 | } 85 | 86 | pub fn parse(rawpath: &str) -> DecodeResult { 87 | let (path, rest) = try!(get_path(rawpath, false)); 88 | 89 | // query and fragment 90 | let (query, fragment) = try!(get_query_fragment(&rest)); 91 | 92 | Ok(Path { 93 | path: path, 94 | query: query, 95 | fragment: fragment, 96 | }) 97 | } 98 | } 99 | 100 | impl UserInfo { 101 | #[inline] 102 | pub fn new(user: String, pass: Option) -> UserInfo { 103 | UserInfo { 104 | user: user, 105 | pass: pass, 106 | } 107 | } 108 | } 109 | 110 | pub type DecodeResult = Result; 111 | 112 | pub fn decode_component(container: &str) -> DecodeResult { 113 | decode_inner(container, false) 114 | } 115 | 116 | fn decode_inner(c: &str, full_url: bool) -> DecodeResult { 117 | let mut out = String::new(); 118 | let mut iter = c.as_bytes().iter().cloned(); 119 | 120 | loop { 121 | match iter.next() { 122 | Some(b) => { 123 | match b as char { 124 | '%' => { 125 | let bytes = match (iter.next(), iter.next()) { 126 | (Some(one), Some(two)) => [one, two], 127 | _ => { 128 | return Err("Malformed input: found '%' without two \ 129 | trailing bytes".to_owned()) 130 | } 131 | }; 132 | 133 | let bytes_from_hex = match Vec::::from_hex(&bytes) { 134 | Ok(b) => b, 135 | _ => { 136 | return Err("Malformed input: found '%' followed by \ 137 | invalid hex values. Character '%' must \ 138 | escaped.".to_owned()) 139 | } 140 | }; 141 | 142 | // Only decode some characters if full_url: 143 | match bytes_from_hex[0] as char { 144 | // gen-delims: 145 | ':' | 146 | '/' | 147 | '?' | 148 | '#' | 149 | '[' | 150 | ']' | 151 | '@' | 152 | '!' | 153 | '$' | 154 | '&' | 155 | '"' | 156 | '(' | 157 | ')' | 158 | '*' | 159 | '+' | 160 | ',' | 161 | ';' | 162 | '=' if full_url => { 163 | out.push('%'); 164 | out.push(bytes[0] as char); 165 | out.push(bytes[1] as char); 166 | } 167 | 168 | ch => out.push(ch), 169 | } 170 | } 171 | ch => out.push(ch), 172 | } 173 | } 174 | None => return Ok(out), 175 | } 176 | } 177 | } 178 | 179 | fn split_char_first(s: &str, c: char) -> (&str, &str) { 180 | let mut iter = s.splitn(2, c); 181 | 182 | match (iter.next(), iter.next()) { 183 | (Some(a), Some(b)) => (a, b), 184 | (Some(a), None) => (a, ""), 185 | (None, _) => unreachable!(), 186 | } 187 | } 188 | 189 | fn query_from_str(rawquery: &str) -> DecodeResult { 190 | let mut query: Query = vec![]; 191 | if !rawquery.is_empty() { 192 | for p in rawquery.split('&') { 193 | let (k, v) = split_char_first(p, '='); 194 | query.push((try!(decode_component(k)), try!(decode_component(v)))); 195 | } 196 | } 197 | 198 | Ok(query) 199 | } 200 | 201 | pub fn get_scheme(rawurl: &str) -> DecodeResult<(&str, &str)> { 202 | for (i, c) in rawurl.chars().enumerate() { 203 | let result = match c { 204 | 'A'...'Z' | 205 | 'a'...'z' => continue, 206 | '0'...'9' | '+' | '-' | '.' => { 207 | if i != 0 { 208 | continue; 209 | } 210 | 211 | Err("url: Scheme must begin with a letter.".to_owned()) 212 | } 213 | ':' => { 214 | if i == 0 { 215 | Err("url: Scheme cannot be empty.".to_owned()) 216 | } else { 217 | Ok((&rawurl[0..i], &rawurl[i + 1..rawurl.len()])) 218 | } 219 | } 220 | _ => Err("url: Invalid character in scheme.".to_owned()), 221 | }; 222 | 223 | return result; 224 | } 225 | 226 | Err("url: Scheme must be terminated with a colon.".to_owned()) 227 | } 228 | 229 | // returns userinfo, host, port, and unparsed part, or an error 230 | fn get_authority(rawurl: &str) -> DecodeResult<(Option, &str, Option, &str)> { 231 | enum State { 232 | Start, // starting state 233 | PassHostPort, // could be in user or port 234 | Ip6Port, // either in ipv6 host or port 235 | Ip6Host, // are in an ipv6 host 236 | InHost, // are in a host - may be ipv6, but don't know yet 237 | InPort, // are in port 238 | } 239 | 240 | #[derive(Clone, PartialEq)] 241 | enum Input { 242 | Digit, // all digits 243 | Hex, // digits and letters a-f 244 | Unreserved, // all other legal characters 245 | } 246 | 247 | if !rawurl.starts_with("//") { 248 | // there is no authority. 249 | return Ok((None, "", None, rawurl)); 250 | } 251 | 252 | let len = rawurl.len(); 253 | let mut st = State::Start; 254 | let mut input = Input::Digit; // most restricted, start here. 255 | 256 | let mut userinfo = None; 257 | let mut host = ""; 258 | let mut port = None; 259 | 260 | let mut colon_count = 0usize; 261 | let mut pos = 0; 262 | let mut begin = 2; 263 | let mut end = len; 264 | 265 | for (i, c) in rawurl.chars() 266 | .enumerate() 267 | .skip(2) { 268 | // deal with input class first 269 | match c { 270 | '0'...'9' => (), 271 | 'A'...'F' | 272 | 'a'...'f' => { 273 | if input == Input::Digit { 274 | input = Input::Hex; 275 | } 276 | } 277 | 'G'...'Z' | 278 | 'g'...'z' | 279 | '-' | 280 | '.' | 281 | '_' | 282 | '~' | 283 | '%' | 284 | '&' | 285 | '\'' | 286 | '(' | 287 | ')' | 288 | '+' | 289 | '!' | 290 | '*' | 291 | ',' | 292 | ';' | 293 | '=' => input = Input::Unreserved, 294 | ':' | '@' | '?' | '#' | '/' => { 295 | // separators, don't change anything 296 | } 297 | _ => return Err("Illegal character in authority".to_owned()), 298 | } 299 | 300 | // now process states 301 | match c { 302 | ':' => { 303 | colon_count += 1; 304 | match st { 305 | State::Start => { 306 | pos = i; 307 | st = State::PassHostPort; 308 | } 309 | State::PassHostPort => { 310 | // multiple colons means ipv6 address. 311 | if input == Input::Unreserved { 312 | return Err("Illegal characters in IPv6 address.".to_owned()); 313 | } 314 | st = State::Ip6Host; 315 | } 316 | State::InHost => { 317 | pos = i; 318 | if input == Input::Unreserved { 319 | // must be port 320 | host = &rawurl[begin..i]; 321 | st = State::InPort; 322 | } else { 323 | // can't be sure whether this is an ipv6 address or a port 324 | st = State::Ip6Port; 325 | } 326 | } 327 | State::Ip6Port => { 328 | if input == Input::Unreserved { 329 | return Err("Illegal characters in authority.".to_owned()); 330 | } 331 | st = State::Ip6Host; 332 | } 333 | State::Ip6Host => { 334 | if colon_count > 7 { 335 | host = &rawurl[begin..i]; 336 | pos = i; 337 | st = State::InPort; 338 | } 339 | } 340 | _ => return Err("Invalid ':' in authority.".to_owned()), 341 | } 342 | input = Input::Digit; // reset input class 343 | } 344 | 345 | '@' => { 346 | input = Input::Digit; // reset input class 347 | colon_count = 0; // reset count 348 | match st { 349 | State::Start => { 350 | let user = try!(decode_component(&rawurl[begin..i])); 351 | userinfo = Some(UserInfo::new(user, None)); 352 | st = State::InHost; 353 | } 354 | State::PassHostPort => { 355 | let user = try!(decode_component(&rawurl[begin..pos])); 356 | let pass = try!(decode_component(&rawurl[pos + 1..i])); 357 | userinfo = Some(UserInfo::new(user, Some(pass))); 358 | st = State::InHost; 359 | } 360 | _ => return Err("Invalid '@' in authority.".to_owned()), 361 | } 362 | begin = i + 1; 363 | } 364 | 365 | '?' | '#' | '/' => { 366 | end = i; 367 | break; 368 | } 369 | _ => (), 370 | } 371 | } 372 | 373 | // finish up 374 | match st { 375 | State::PassHostPort | 376 | State::Ip6Port => { 377 | if input != Input::Digit { 378 | return Err("Non-digit characters in port.".to_owned()); 379 | } 380 | host = &rawurl[begin..pos]; 381 | port = Some(&rawurl[pos + 1..end]); 382 | } 383 | State::Ip6Host | 384 | State::InHost | 385 | State::Start => host = &rawurl[begin..end], 386 | State::InPort => { 387 | if input != Input::Digit { 388 | return Err("Non-digit characters in port.".to_owned()); 389 | } 390 | port = Some(&rawurl[pos + 1..end]); 391 | } 392 | } 393 | 394 | let rest = &rawurl[end..len]; 395 | // If we have a port string, ensure it parses to u16. 396 | let port = match port { 397 | None => None, 398 | opt => { 399 | match opt.and_then(|p| FromStr::from_str(p).ok()) { 400 | None => return Err(format!("Failed to parse port: {:?}", port)), 401 | opt => opt, 402 | } 403 | } 404 | }; 405 | 406 | Ok((userinfo, host, port, rest)) 407 | } 408 | 409 | 410 | // returns the path and unparsed part of url, or an error 411 | fn get_path(rawurl: &str, is_authority: bool) -> DecodeResult<(String, &str)> { 412 | let len = rawurl.len(); 413 | let mut end = len; 414 | for (i, c) in rawurl.chars().enumerate() { 415 | match c { 416 | 'A'...'Z' | 417 | 'a'...'z' | 418 | '0'...'9' | 419 | '&' | 420 | '\'' | 421 | '(' | 422 | ')' | 423 | '.' | 424 | '@' | 425 | ':' | 426 | '%' | 427 | '/' | 428 | '+' | 429 | '!' | 430 | '*' | 431 | ',' | 432 | ';' | 433 | '=' | 434 | '_' | 435 | '-' | 436 | '~' => continue, 437 | '?' | '#' => { 438 | end = i; 439 | break; 440 | } 441 | _ => return Err("Invalid character in path.".to_owned()), 442 | } 443 | } 444 | 445 | if is_authority && end != 0 && !rawurl.starts_with('/') { 446 | Err("Non-empty path must begin with '/' in presence of authority.".to_owned()) 447 | } else { 448 | Ok((try!(decode_component(&rawurl[0..end])), &rawurl[end..len])) 449 | } 450 | } 451 | 452 | // returns the parsed query and the fragment, if present 453 | fn get_query_fragment(rawurl: &str) -> DecodeResult<(Query, Option)> { 454 | let (before_fragment, raw_fragment) = split_char_first(rawurl, '#'); 455 | 456 | // Parse the fragment if available 457 | let fragment = match raw_fragment { 458 | "" => None, 459 | raw => Some(try!(decode_component(raw))), 460 | }; 461 | 462 | match before_fragment.chars().next() { 463 | Some('?') => Ok((try!(query_from_str(&before_fragment[1..])), fragment)), 464 | None => Ok((vec![], fragment)), 465 | _ => Err(format!("Query didn't start with '?': '{}..'", before_fragment)), 466 | } 467 | } 468 | 469 | impl FromStr for Url { 470 | type Err = String; 471 | fn from_str(s: &str) -> Result { 472 | Url::parse(s) 473 | } 474 | } 475 | 476 | impl FromStr for Path { 477 | type Err = String; 478 | fn from_str(s: &str) -> Result { 479 | Path::parse(s) 480 | } 481 | } 482 | -------------------------------------------------------------------------------- /src/message.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::io::prelude::*; 3 | use std::mem; 4 | use std::time::Duration; 5 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 6 | 7 | use types::Oid; 8 | use priv_io::StreamOptions; 9 | 10 | pub const PROTOCOL_VERSION: u32 = 0x0003_0000; 11 | pub const CANCEL_CODE: u32 = 80877102; 12 | pub const SSL_CODE: u32 = 80877103; 13 | 14 | pub enum Backend { 15 | AuthenticationCleartextPassword, 16 | AuthenticationGSS, 17 | AuthenticationKerberosV5, 18 | AuthenticationMD5Password { 19 | salt: [u8; 4], 20 | }, 21 | AuthenticationOk, 22 | AuthenticationSCMCredential, 23 | AuthenticationSSPI, 24 | BackendKeyData { 25 | process_id: u32, 26 | secret_key: u32, 27 | }, 28 | BindComplete, 29 | CloseComplete, 30 | CommandComplete { 31 | tag: String, 32 | }, 33 | CopyData { 34 | data: Vec, 35 | }, 36 | CopyDone, 37 | CopyInResponse { 38 | format: u8, 39 | column_formats: Vec, 40 | }, 41 | CopyOutResponse { 42 | format: u8, 43 | column_formats: Vec, 44 | }, 45 | DataRow { 46 | row: Vec>>, 47 | }, 48 | EmptyQueryResponse, 49 | ErrorResponse { 50 | fields: Vec<(u8, String)>, 51 | }, 52 | NoData, 53 | NoticeResponse { 54 | fields: Vec<(u8, String)>, 55 | }, 56 | NotificationResponse { 57 | pid: u32, 58 | channel: String, 59 | payload: String, 60 | }, 61 | ParameterDescription { 62 | types: Vec, 63 | }, 64 | ParameterStatus { 65 | parameter: String, 66 | value: String, 67 | }, 68 | ParseComplete, 69 | PortalSuspended, 70 | ReadyForQuery { 71 | _state: u8, 72 | }, 73 | RowDescription { 74 | descriptions: Vec, 75 | }, 76 | } 77 | 78 | pub struct RowDescriptionEntry { 79 | pub name: String, 80 | pub table_oid: Oid, 81 | pub column_id: i16, 82 | pub type_oid: Oid, 83 | pub type_size: i16, 84 | pub type_modifier: i32, 85 | pub format: i16, 86 | } 87 | 88 | pub enum Frontend<'a> { 89 | Bind { 90 | portal: &'a str, 91 | statement: &'a str, 92 | formats: &'a [i16], 93 | values: &'a [Option>], 94 | result_formats: &'a [i16], 95 | }, 96 | CancelRequest { 97 | code: u32, 98 | process_id: u32, 99 | secret_key: u32, 100 | }, 101 | Close { 102 | variant: u8, 103 | name: &'a str, 104 | }, 105 | CopyData { 106 | data: &'a [u8], 107 | }, 108 | CopyDone, 109 | CopyFail { 110 | message: &'a str, 111 | }, 112 | Describe { 113 | variant: u8, 114 | name: &'a str, 115 | }, 116 | Execute { 117 | portal: &'a str, 118 | max_rows: i32, 119 | }, 120 | Parse { 121 | name: &'a str, 122 | query: &'a str, 123 | param_types: &'a [Oid], 124 | }, 125 | PasswordMessage { 126 | password: &'a str, 127 | }, 128 | Query { 129 | query: &'a str, 130 | }, 131 | SslRequest { 132 | code: u32, 133 | }, 134 | StartupMessage { 135 | version: u32, 136 | parameters: &'a [(String, String)], 137 | }, 138 | Sync, 139 | Terminate, 140 | } 141 | 142 | #[doc(hidden)] 143 | trait WriteCStr { 144 | fn write_cstr(&mut self, s: &str) -> io::Result<()>; 145 | } 146 | 147 | impl WriteCStr for W { 148 | fn write_cstr(&mut self, s: &str) -> io::Result<()> { 149 | try!(self.write_all(s.as_bytes())); 150 | Ok(try!(self.write_u8(0))) 151 | } 152 | } 153 | 154 | #[doc(hidden)] 155 | pub trait WriteMessage { 156 | fn write_message(&mut self, &Frontend) -> io::Result<()>; 157 | } 158 | 159 | impl WriteMessage for W { 160 | #[allow(cyclomatic_complexity)] 161 | fn write_message(&mut self, message: &Frontend) -> io::Result<()> { 162 | let mut buf = vec![]; 163 | let mut ident = None; 164 | 165 | match *message { 166 | Frontend::Bind { portal, statement, formats, values, result_formats } => { 167 | ident = Some(b'B'); 168 | try!(buf.write_cstr(portal)); 169 | try!(buf.write_cstr(statement)); 170 | 171 | try!(buf.write_u16::(try!(u16::from_usize(formats.len())))); 172 | for &format in formats { 173 | try!(buf.write_i16::(format)); 174 | } 175 | 176 | try!(buf.write_u16::(try!(u16::from_usize(values.len())))); 177 | for value in values { 178 | match *value { 179 | None => try!(buf.write_i32::(-1)), 180 | Some(ref value) => { 181 | try!(buf.write_i32::(try!(i32::from_usize(value.len())))); 182 | try!(buf.write_all(&**value)); 183 | } 184 | } 185 | } 186 | 187 | try!(buf.write_u16::(try!(u16::from_usize(result_formats.len())))); 188 | for &format in result_formats { 189 | try!(buf.write_i16::(format)); 190 | } 191 | } 192 | Frontend::CancelRequest { code, process_id, secret_key } => { 193 | try!(buf.write_u32::(code)); 194 | try!(buf.write_u32::(process_id)); 195 | try!(buf.write_u32::(secret_key)); 196 | } 197 | Frontend::Close { variant, name } => { 198 | ident = Some(b'C'); 199 | try!(buf.write_u8(variant)); 200 | try!(buf.write_cstr(name)); 201 | } 202 | Frontend::CopyData { data } => { 203 | ident = Some(b'd'); 204 | try!(buf.write_all(data)); 205 | } 206 | Frontend::CopyDone => ident = Some(b'c'), 207 | Frontend::CopyFail { message } => { 208 | ident = Some(b'f'); 209 | try!(buf.write_cstr(message)); 210 | } 211 | Frontend::Describe { variant, name } => { 212 | ident = Some(b'D'); 213 | try!(buf.write_u8(variant)); 214 | try!(buf.write_cstr(name)); 215 | } 216 | Frontend::Execute { portal, max_rows } => { 217 | ident = Some(b'E'); 218 | try!(buf.write_cstr(portal)); 219 | try!(buf.write_i32::(max_rows)); 220 | } 221 | Frontend::Parse { name, query, param_types } => { 222 | ident = Some(b'P'); 223 | try!(buf.write_cstr(name)); 224 | try!(buf.write_cstr(query)); 225 | try!(buf.write_u16::(try!(u16::from_usize(param_types.len())))); 226 | for &ty in param_types { 227 | try!(buf.write_u32::(ty)); 228 | } 229 | } 230 | Frontend::PasswordMessage { password } => { 231 | ident = Some(b'p'); 232 | try!(buf.write_cstr(password)); 233 | } 234 | Frontend::Query { query } => { 235 | ident = Some(b'Q'); 236 | try!(buf.write_cstr(query)); 237 | } 238 | Frontend::StartupMessage { version, parameters } => { 239 | try!(buf.write_u32::(version)); 240 | for &(ref k, ref v) in parameters { 241 | try!(buf.write_cstr(&**k)); 242 | try!(buf.write_cstr(&**v)); 243 | } 244 | try!(buf.write_u8(0)); 245 | } 246 | Frontend::SslRequest { code } => try!(buf.write_u32::(code)), 247 | Frontend::Sync => ident = Some(b'S'), 248 | Frontend::Terminate => ident = Some(b'X'), 249 | } 250 | 251 | if let Some(ident) = ident { 252 | try!(self.write_u8(ident)); 253 | } 254 | 255 | // add size of length value 256 | if buf.len() > u32::max_value() as usize - mem::size_of::() { 257 | return Err(io::Error::new(io::ErrorKind::InvalidInput, "value too large to transmit")); 258 | } 259 | try!(self.write_u32::((buf.len() + mem::size_of::()) as u32)); 260 | try!(self.write_all(&*buf)); 261 | 262 | Ok(()) 263 | } 264 | } 265 | 266 | #[doc(hidden)] 267 | trait ReadCStr { 268 | fn read_cstr(&mut self) -> io::Result; 269 | } 270 | 271 | impl ReadCStr for R { 272 | fn read_cstr(&mut self) -> io::Result { 273 | let mut buf = vec![]; 274 | try!(self.read_until(0, &mut buf)); 275 | buf.pop(); 276 | String::from_utf8(buf).map_err(|err| io::Error::new(io::ErrorKind::Other, err)) 277 | } 278 | } 279 | 280 | #[doc(hidden)] 281 | pub trait ReadMessage { 282 | fn read_message(&mut self) -> io::Result; 283 | 284 | fn read_message_timeout(&mut self, timeout: Duration) -> io::Result>; 285 | 286 | fn read_message_nonblocking(&mut self) -> io::Result>; 287 | 288 | fn finish_read_message(&mut self, ident: u8) -> io::Result; 289 | } 290 | 291 | impl ReadMessage for R { 292 | fn read_message(&mut self) -> io::Result { 293 | let ident = try!(self.read_u8()); 294 | self.finish_read_message(ident) 295 | } 296 | 297 | fn read_message_timeout(&mut self, timeout: Duration) -> io::Result> { 298 | try!(self.set_read_timeout(Some(timeout))); 299 | let ident = self.read_u8(); 300 | try!(self.set_read_timeout(None)); 301 | 302 | match ident { 303 | Ok(ident) => self.finish_read_message(ident).map(Some), 304 | Err(e) => { 305 | let e: io::Error = e.into(); 306 | if e.kind() == io::ErrorKind::WouldBlock || e.kind() == io::ErrorKind::TimedOut { 307 | Ok(None) 308 | } else { 309 | Err(e) 310 | } 311 | } 312 | } 313 | } 314 | 315 | fn read_message_nonblocking(&mut self) -> io::Result> { 316 | try!(self.set_nonblocking(true)); 317 | let ident = self.read_u8(); 318 | try!(self.set_nonblocking(false)); 319 | 320 | match ident { 321 | Ok(ident) => self.finish_read_message(ident).map(Some), 322 | Err(e) => { 323 | let e: io::Error = e.into(); 324 | if e.kind() == io::ErrorKind::WouldBlock { 325 | Ok(None) 326 | } else { 327 | Err(e) 328 | } 329 | } 330 | } 331 | } 332 | 333 | #[allow(cyclomatic_complexity)] 334 | fn finish_read_message(&mut self, ident: u8) -> io::Result { 335 | // subtract size of length value 336 | let len = try!(self.read_u32::()) - mem::size_of::() as u32; 337 | let mut rdr = self.by_ref().take(len as u64); 338 | 339 | let ret = match ident { 340 | b'1' => Backend::ParseComplete, 341 | b'2' => Backend::BindComplete, 342 | b'3' => Backend::CloseComplete, 343 | b'A' => { 344 | Backend::NotificationResponse { 345 | pid: try!(rdr.read_u32::()), 346 | channel: try!(rdr.read_cstr()), 347 | payload: try!(rdr.read_cstr()), 348 | } 349 | } 350 | b'c' => Backend::CopyDone, 351 | b'C' => Backend::CommandComplete { tag: try!(rdr.read_cstr()) }, 352 | b'd' => { 353 | let mut data = vec![]; 354 | try!(rdr.read_to_end(&mut data)); 355 | Backend::CopyData { data: data } 356 | } 357 | b'D' => try!(read_data_row(&mut rdr)), 358 | b'E' => Backend::ErrorResponse { fields: try!(read_fields(&mut rdr)) }, 359 | b'G' => { 360 | let format = try!(rdr.read_u8()); 361 | let mut column_formats = vec![]; 362 | for _ in 0..try!(rdr.read_u16::()) { 363 | column_formats.push(try!(rdr.read_u16::())); 364 | } 365 | Backend::CopyInResponse { 366 | format: format, 367 | column_formats: column_formats, 368 | } 369 | } 370 | b'H' => { 371 | let format = try!(rdr.read_u8()); 372 | let mut column_formats = vec![]; 373 | for _ in 0..try!(rdr.read_u16::()) { 374 | column_formats.push(try!(rdr.read_u16::())); 375 | } 376 | Backend::CopyOutResponse { 377 | format: format, 378 | column_formats: column_formats, 379 | } 380 | } 381 | b'I' => Backend::EmptyQueryResponse, 382 | b'K' => { 383 | Backend::BackendKeyData { 384 | process_id: try!(rdr.read_u32::()), 385 | secret_key: try!(rdr.read_u32::()), 386 | } 387 | } 388 | b'n' => Backend::NoData, 389 | b'N' => Backend::NoticeResponse { fields: try!(read_fields(&mut rdr)) }, 390 | b'R' => try!(read_auth_message(&mut rdr)), 391 | b's' => Backend::PortalSuspended, 392 | b'S' => { 393 | Backend::ParameterStatus { 394 | parameter: try!(rdr.read_cstr()), 395 | value: try!(rdr.read_cstr()), 396 | } 397 | } 398 | b't' => try!(read_parameter_description(&mut rdr)), 399 | b'T' => try!(read_row_description(&mut rdr)), 400 | b'Z' => Backend::ReadyForQuery { _state: try!(rdr.read_u8()) }, 401 | t => { 402 | return Err(io::Error::new(io::ErrorKind::Other, 403 | format!("unexpected message tag `{}`", t))) 404 | } 405 | }; 406 | if rdr.limit() != 0 { 407 | return Err(io::Error::new(io::ErrorKind::Other, "didn't read entire message")); 408 | } 409 | Ok(ret) 410 | } 411 | } 412 | 413 | fn read_fields(buf: &mut R) -> io::Result> { 414 | let mut fields = vec![]; 415 | loop { 416 | let ty = try!(buf.read_u8()); 417 | if ty == 0 { 418 | break; 419 | } 420 | 421 | fields.push((ty, try!(buf.read_cstr()))); 422 | } 423 | 424 | Ok(fields) 425 | } 426 | 427 | fn read_data_row(buf: &mut R) -> io::Result { 428 | let len = try!(buf.read_u16::()) as usize; 429 | let mut values = Vec::with_capacity(len); 430 | 431 | for _ in 0..len { 432 | let val = match try!(buf.read_i32::()) { 433 | -1 => None, 434 | len => { 435 | let mut data = vec![0; len as usize]; 436 | try!(buf.read_exact(&mut data)); 437 | Some(data) 438 | } 439 | }; 440 | values.push(val); 441 | } 442 | 443 | Ok(Backend::DataRow { row: values }) 444 | } 445 | 446 | fn read_auth_message(buf: &mut R) -> io::Result { 447 | Ok(match try!(buf.read_i32::()) { 448 | 0 => Backend::AuthenticationOk, 449 | 2 => Backend::AuthenticationKerberosV5, 450 | 3 => Backend::AuthenticationCleartextPassword, 451 | 5 => { 452 | let mut salt = [0; 4]; 453 | try!(buf.read_exact(&mut salt)); 454 | Backend::AuthenticationMD5Password { salt: salt } 455 | } 456 | 6 => Backend::AuthenticationSCMCredential, 457 | 7 => Backend::AuthenticationGSS, 458 | 9 => Backend::AuthenticationSSPI, 459 | t => { 460 | return Err(io::Error::new(io::ErrorKind::Other, 461 | format!("unexpected authentication tag `{}`", t))) 462 | } 463 | }) 464 | } 465 | 466 | fn read_parameter_description(buf: &mut R) -> io::Result { 467 | let len = try!(buf.read_u16::()) as usize; 468 | let mut types = Vec::with_capacity(len); 469 | 470 | for _ in 0..len { 471 | types.push(try!(buf.read_u32::())); 472 | } 473 | 474 | Ok(Backend::ParameterDescription { types: types }) 475 | } 476 | 477 | fn read_row_description(buf: &mut R) -> io::Result { 478 | let len = try!(buf.read_u16::()) as usize; 479 | let mut types = Vec::with_capacity(len); 480 | 481 | for _ in 0..len { 482 | types.push(RowDescriptionEntry { 483 | name: try!(buf.read_cstr()), 484 | table_oid: try!(buf.read_u32::()), 485 | column_id: try!(buf.read_i16::()), 486 | type_oid: try!(buf.read_u32::()), 487 | type_size: try!(buf.read_i16::()), 488 | type_modifier: try!(buf.read_i32::()), 489 | format: try!(buf.read_i16::()), 490 | }) 491 | } 492 | 493 | Ok(Backend::RowDescription { descriptions: types }) 494 | } 495 | 496 | trait FromUsize: Sized { 497 | fn from_usize(x: usize) -> io::Result; 498 | } 499 | 500 | macro_rules! from_usize { 501 | ($t:ty) => { 502 | impl FromUsize for $t { 503 | fn from_usize(x: usize) -> io::Result<$t> { 504 | if x > <$t>::max_value() as usize { 505 | Err(io::Error::new(io::ErrorKind::InvalidInput, "value too large to transmit")) 506 | } else { 507 | Ok(x as $t) 508 | } 509 | } 510 | } 511 | } 512 | } 513 | 514 | from_usize!(u16); 515 | from_usize!(i32); 516 | -------------------------------------------------------------------------------- /src/md5.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use std::ptr; 12 | use std::mem; 13 | use std::ops::{Add, Range}; 14 | use std::iter::repeat; 15 | 16 | #[derive(Clone)] 17 | struct StepUp { 18 | next: T, 19 | end: T, 20 | ammount: T, 21 | } 22 | 23 | impl Iterator for StepUp 24 | where T: Add + PartialOrd + Copy 25 | { 26 | type Item = T; 27 | 28 | #[inline] 29 | fn next(&mut self) -> Option { 30 | if self.next < self.end { 31 | let n = self.next; 32 | self.next = self.next + self.ammount; 33 | Some(n) 34 | } else { 35 | None 36 | } 37 | } 38 | } 39 | 40 | trait RangeExt { 41 | fn step_up(self, ammount: T) -> StepUp; 42 | } 43 | 44 | impl RangeExt for Range 45 | where T: Add + PartialOrd + Copy 46 | { 47 | fn step_up(self, ammount: T) -> StepUp { 48 | StepUp { 49 | next: self.start, 50 | end: self.end, 51 | ammount: ammount, 52 | } 53 | } 54 | } 55 | 56 | /// Copy bytes from src to dest 57 | #[inline] 58 | fn copy_memory(src: &[u8], dst: &mut [u8]) { 59 | assert!(dst.len() >= src.len()); 60 | unsafe { 61 | let srcp = src.as_ptr(); 62 | let dstp = dst.as_mut_ptr(); 63 | ptr::copy_nonoverlapping(srcp, dstp, src.len()); 64 | } 65 | } 66 | 67 | /// Zero all bytes in dst 68 | #[inline] 69 | fn zero(dst: &mut [u8]) { 70 | unsafe { 71 | ptr::write_bytes(dst.as_mut_ptr(), 0, dst.len()); 72 | } 73 | } 74 | 75 | /// Read a vector of bytes into a vector of u32s. The values are read in little-endian format. 76 | fn read_u32v_le(dst: &mut [u32], input: &[u8]) { 77 | assert!(dst.len() * 4 == input.len()); 78 | unsafe { 79 | let mut x: *mut u32 = dst.get_unchecked_mut(0); 80 | let mut y: *const u8 = input.get_unchecked(0); 81 | for _ in 0..dst.len() { 82 | let mut tmp: u32 = mem::uninitialized(); 83 | ptr::copy_nonoverlapping(y, &mut tmp as *mut _ as *mut u8, 4); 84 | *x = u32::from_le(tmp); 85 | x = x.offset(1); 86 | y = y.offset(4); 87 | } 88 | } 89 | } 90 | 91 | /// Write a u32 into a vector, which must be 4 bytes long. The value is written in little-endian 92 | /// format. 93 | fn write_u32_le(dst: &mut [u8], mut input: u32) { 94 | assert!(dst.len() == 4); 95 | input = input.to_le(); 96 | unsafe { 97 | let tmp = &input as *const _ as *const u8; 98 | ptr::copy_nonoverlapping(tmp, dst.get_unchecked_mut(0), 4); 99 | } 100 | } 101 | 102 | /// The `StandardPadding` trait adds a method useful for various hash algorithms to a `FixedBuffer` 103 | /// struct. 104 | trait StandardPadding { 105 | /// Add standard padding to the buffer. The buffer must not be full when this method is called 106 | /// and is guaranteed to have exactly rem remaining bytes when it returns. If there are not at 107 | /// least rem bytes available, the buffer will be zero padded, processed, cleared, and then 108 | /// filled with zeros again until only rem bytes are remaining. 109 | fn standard_padding(&mut self, rem: usize, func: F); 110 | } 111 | 112 | impl StandardPadding for T { 113 | fn standard_padding(&mut self, rem: usize, mut func: F) { 114 | let size = self.size(); 115 | 116 | self.next(1)[0] = 128; 117 | 118 | if self.remaining() < rem { 119 | self.zero_until(size); 120 | func(self.full_buffer()); 121 | } 122 | 123 | self.zero_until(size - rem); 124 | } 125 | } 126 | 127 | /// A `FixedBuffer`, likes its name implies, is a fixed size buffer. When the buffer becomes full, 128 | /// it must be processed. The input() method takes care of processing and then clearing the buffer 129 | /// automatically. However, other methods do not and require the caller to process the buffer. Any 130 | /// method that modifies the buffer directory or provides the caller with bytes that can be 131 | /// modifies results in those bytes being marked as used by the buffer. 132 | trait FixedBuffer { 133 | /// Input a vector of bytes. If the buffer becomes full, process it with the provided 134 | /// function and then clear the buffer. 135 | fn input(&mut self, input: &[u8], func: F); 136 | 137 | /// Reset the buffer. 138 | fn reset(&mut self); 139 | 140 | /// Zero the buffer up until the specified index. The buffer position currently must not be 141 | /// greater than that index. 142 | fn zero_until(&mut self, idx: usize); 143 | 144 | /// Get a slice of the buffer of the specified size. There must be at least that many bytes 145 | /// remaining in the buffer. 146 | fn next(&mut self, len: usize) -> &mut [u8]; 147 | 148 | /// Get the current buffer. The buffer must already be full. This clears the buffer as well. 149 | fn full_buffer(&mut self) -> &[u8]; 150 | 151 | /// Get the current buffer. 152 | fn current_buffer(&mut self) -> &[u8]; 153 | 154 | /// Get the current position of the buffer. 155 | fn position(&self) -> usize; 156 | 157 | /// Get the number of bytes remaining in the buffer until it is full. 158 | fn remaining(&self) -> usize; 159 | 160 | /// Get the size of the buffer 161 | fn size(&self) -> usize; 162 | } 163 | 164 | macro_rules! impl_fixed_buffer( ($name:ident, $size:expr) => ( 165 | impl FixedBuffer for $name { 166 | fn input(&mut self, input: &[u8], mut func: F) { 167 | let mut i = 0; 168 | 169 | // FIXME: #6304 - This local variable shouldn't be necessary. 170 | let size = $size; 171 | 172 | // If there is already data in the buffer, copy as much as we can into it and process 173 | // the data if the buffer becomes full. 174 | if self.buffer_idx != 0 { 175 | let buffer_remaining = size - self.buffer_idx; 176 | if input.len() >= buffer_remaining { 177 | copy_memory( 178 | &input[..buffer_remaining], 179 | &mut self.buffer[self.buffer_idx..size]); 180 | self.buffer_idx = 0; 181 | func(&self.buffer); 182 | i += buffer_remaining; 183 | } else { 184 | copy_memory( 185 | input, 186 | &mut self.buffer[self.buffer_idx..self.buffer_idx + input.len()]); 187 | self.buffer_idx += input.len(); 188 | return; 189 | } 190 | } 191 | 192 | // While we have at least a full buffer size chunks's worth of data, process that data 193 | // without copying it into the buffer 194 | while input.len() - i >= size { 195 | func(&input[i..i + size]); 196 | i += size; 197 | } 198 | 199 | // Copy any input data into the buffer. At this point in the method, the ammount of 200 | // data left in the input vector will be less than the buffer size and the buffer will 201 | // be empty. 202 | let input_remaining = input.len() - i; 203 | copy_memory( 204 | &input[i..], 205 | &mut self.buffer[0..input_remaining]); 206 | self.buffer_idx += input_remaining; 207 | } 208 | 209 | fn reset(&mut self) { 210 | self.buffer_idx = 0; 211 | } 212 | 213 | fn zero_until(&mut self, idx: usize) { 214 | assert!(idx >= self.buffer_idx); 215 | zero(&mut self.buffer[self.buffer_idx..idx]); 216 | self.buffer_idx = idx; 217 | } 218 | 219 | fn next(&mut self, len: usize) -> &mut [u8] { 220 | self.buffer_idx += len; 221 | &mut self.buffer[self.buffer_idx - len..self.buffer_idx] 222 | } 223 | 224 | fn full_buffer(&mut self) -> &[u8] { 225 | assert!(self.buffer_idx == $size); 226 | self.buffer_idx = 0; 227 | &self.buffer[..$size] 228 | } 229 | 230 | fn current_buffer(&mut self) -> &[u8] { 231 | let tmp = self.buffer_idx; 232 | self.buffer_idx = 0; 233 | &self.buffer[..tmp] 234 | } 235 | 236 | fn position(&self) -> usize { self.buffer_idx } 237 | 238 | fn remaining(&self) -> usize { $size - self.buffer_idx } 239 | 240 | fn size(&self) -> usize { $size } 241 | } 242 | )); 243 | 244 | /// A fixed size buffer of 64 bytes useful for cryptographic operations. 245 | #[derive(Copy)] 246 | struct FixedBuffer64 { 247 | buffer: [u8; 64], 248 | buffer_idx: usize, 249 | } 250 | 251 | impl Clone for FixedBuffer64 { 252 | fn clone(&self) -> FixedBuffer64 { 253 | *self 254 | } 255 | } 256 | 257 | impl FixedBuffer64 { 258 | /// Create a new buffer 259 | #[allow(new_without_default)] 260 | fn new() -> FixedBuffer64 { 261 | FixedBuffer64 { 262 | buffer: [0u8; 64], 263 | buffer_idx: 0, 264 | } 265 | } 266 | } 267 | 268 | impl_fixed_buffer!(FixedBuffer64, 64); 269 | 270 | // A structure that represents that state of a digest computation for the MD5 digest function 271 | struct Md5State { 272 | s0: u32, 273 | s1: u32, 274 | s2: u32, 275 | s3: u32, 276 | } 277 | 278 | impl Md5State { 279 | #[allow(new_without_default_derive)] 280 | fn new() -> Md5State { 281 | Md5State { 282 | s0: 0x67452301, 283 | s1: 0xefcdab89, 284 | s2: 0x98badcfe, 285 | s3: 0x10325476, 286 | } 287 | } 288 | 289 | fn reset(&mut self) { 290 | self.s0 = 0x67452301; 291 | self.s1 = 0xefcdab89; 292 | self.s2 = 0x98badcfe; 293 | self.s3 = 0x10325476; 294 | } 295 | 296 | #[allow(many_single_char_names)] 297 | fn process_block(&mut self, input: &[u8]) { 298 | fn f(u: u32, v: u32, w: u32) -> u32 { 299 | (u & v) | (!u & w) 300 | } 301 | 302 | fn g(u: u32, v: u32, w: u32) -> u32 { 303 | (u & w) | (v & !w) 304 | } 305 | 306 | fn h(u: u32, v: u32, w: u32) -> u32 { 307 | u ^ v ^ w 308 | } 309 | 310 | fn i(u: u32, v: u32, w: u32) -> u32 { 311 | v ^ (u | !w) 312 | } 313 | 314 | fn op_f(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 { 315 | w.wrapping_add(f(x, y, z)).wrapping_add(m).rotate_left(s).wrapping_add(x) 316 | } 317 | 318 | fn op_g(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 { 319 | w.wrapping_add(g(x, y, z)).wrapping_add(m).rotate_left(s).wrapping_add(x) 320 | } 321 | 322 | fn op_h(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 { 323 | w.wrapping_add(h(x, y, z)).wrapping_add(m).rotate_left(s).wrapping_add(x) 324 | } 325 | 326 | fn op_i(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 { 327 | w.wrapping_add(i(x, y, z)).wrapping_add(m).rotate_left(s).wrapping_add(x) 328 | } 329 | 330 | let mut a = self.s0; 331 | let mut b = self.s1; 332 | let mut c = self.s2; 333 | let mut d = self.s3; 334 | 335 | let mut data = [0u32; 16]; 336 | 337 | read_u32v_le(&mut data, input); 338 | 339 | // round 1 340 | for i in (0..16).step_up(4) { 341 | a = op_f(a, b, c, d, data[i].wrapping_add(C1[i]), 7); 342 | d = op_f(d, a, b, c, data[i + 1].wrapping_add(C1[i + 1]), 12); 343 | c = op_f(c, d, a, b, data[i + 2].wrapping_add(C1[i + 2]), 17); 344 | b = op_f(b, c, d, a, data[i + 3].wrapping_add(C1[i + 3]), 22); 345 | } 346 | 347 | // round 2 348 | let mut t = 1; 349 | for i in (0..16).step_up(4) { 350 | a = op_g(a, b, c, d, data[t & 0x0f].wrapping_add(C2[i]), 5); 351 | d = op_g(d, a, b, c, data[(t + 5) & 0x0f].wrapping_add(C2[i + 1]), 9); 352 | c = op_g(c, 353 | d, 354 | a, 355 | b, 356 | data[(t + 10) & 0x0f].wrapping_add(C2[i + 2]), 357 | 14); 358 | b = op_g(b, 359 | c, 360 | d, 361 | a, 362 | data[(t + 15) & 0x0f].wrapping_add(C2[i + 3]), 363 | 20); 364 | t += 20; 365 | } 366 | 367 | // round 3 368 | t = 5; 369 | for i in (0..16).step_up(4) { 370 | a = op_h(a, b, c, d, data[t & 0x0f].wrapping_add(C3[i]), 4); 371 | d = op_h(d, a, b, c, data[(t + 3) & 0x0f].wrapping_add(C3[i + 1]), 11); 372 | c = op_h(c, d, a, b, data[(t + 6) & 0x0f].wrapping_add(C3[i + 2]), 16); 373 | b = op_h(b, c, d, a, data[(t + 9) & 0x0f].wrapping_add(C3[i + 3]), 23); 374 | t += 12; 375 | } 376 | 377 | // round 4 378 | t = 0; 379 | for i in (0..16).step_up(4) { 380 | a = op_i(a, b, c, d, data[t & 0x0f].wrapping_add(C4[i]), 6); 381 | d = op_i(d, a, b, c, data[(t + 7) & 0x0f].wrapping_add(C4[i + 1]), 10); 382 | c = op_i(c, 383 | d, 384 | a, 385 | b, 386 | data[(t + 14) & 0x0f].wrapping_add(C4[i + 2]), 387 | 15); 388 | b = op_i(b, 389 | c, 390 | d, 391 | a, 392 | data[(t + 21) & 0x0f].wrapping_add(C4[i + 3]), 393 | 21); 394 | t += 28; 395 | } 396 | 397 | self.s0 = self.s0.wrapping_add(a); 398 | self.s1 = self.s1.wrapping_add(b); 399 | self.s2 = self.s2.wrapping_add(c); 400 | self.s3 = self.s3.wrapping_add(d); 401 | } 402 | } 403 | 404 | // Round 1 constants 405 | static C1: [u32; 16] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 406 | 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 407 | 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821]; 408 | 409 | // Round 2 constants 410 | static C2: [u32; 16] = [0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 411 | 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 412 | 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a]; 413 | 414 | // Round 3 constants 415 | static C3: [u32; 16] = [0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 416 | 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 417 | 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665]; 418 | 419 | // Round 4 constants 420 | static C4: [u32; 16] = [0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 421 | 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 422 | 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391]; 423 | 424 | /// The MD5 Digest algorithm 425 | pub struct Md5 { 426 | length_bytes: u64, 427 | buffer: FixedBuffer64, 428 | state: Md5State, 429 | finished: bool, 430 | } 431 | 432 | impl Md5 { 433 | /// Construct a new instance of the MD5 Digest. 434 | #[allow(new_without_default)] 435 | pub fn new() -> Md5 { 436 | Md5 { 437 | length_bytes: 0, 438 | buffer: FixedBuffer64::new(), 439 | state: Md5State::new(), 440 | finished: false, 441 | } 442 | } 443 | 444 | pub fn input(&mut self, input: &[u8]) { 445 | assert!(!self.finished); 446 | // Unlike Sha1 and Sha2, the length value in MD5 is defined as the length of the message mod 447 | // 2^64 - ie: integer overflow is OK. 448 | self.length_bytes += input.len() as u64; 449 | let self_state = &mut self.state; 450 | self.buffer.input(input, |d: &[u8]| { 451 | self_state.process_block(d); 452 | }); 453 | } 454 | 455 | pub fn reset(&mut self) { 456 | self.length_bytes = 0; 457 | self.buffer.reset(); 458 | self.state.reset(); 459 | self.finished = false; 460 | } 461 | 462 | pub fn result(&mut self, out: &mut [u8]) { 463 | if !self.finished { 464 | let self_state = &mut self.state; 465 | self.buffer.standard_padding(8, |d: &[u8]| { 466 | self_state.process_block(d); 467 | }); 468 | write_u32_le(self.buffer.next(4), (self.length_bytes << 3) as u32); 469 | write_u32_le(self.buffer.next(4), (self.length_bytes >> 29) as u32); 470 | self_state.process_block(self.buffer.full_buffer()); 471 | self.finished = true; 472 | } 473 | 474 | write_u32_le(&mut out[0..4], self.state.s0); 475 | write_u32_le(&mut out[4..8], self.state.s1); 476 | write_u32_le(&mut out[8..12], self.state.s2); 477 | write_u32_le(&mut out[12..16], self.state.s3); 478 | } 479 | 480 | fn output_bits(&self) -> usize { 481 | 128 482 | } 483 | 484 | pub fn result_str(&mut self) -> String { 485 | use hex::ToHex; 486 | 487 | let mut buf: Vec = repeat(0).take((self.output_bits() + 7) / 8).collect(); 488 | self.result(&mut buf); 489 | buf.to_hex() 490 | } 491 | } 492 | 493 | 494 | #[cfg(test)] 495 | mod tests { 496 | use md5::Md5; 497 | 498 | struct Test { 499 | input: &'static str, 500 | output_str: &'static str, 501 | } 502 | 503 | fn test_hash(sh: &mut D, tests: &[Test]) { 504 | // Test that it works when accepting the message all at once 505 | for t in tests.iter() { 506 | sh.input_str(t.input); 507 | 508 | let out_str = sh.result_str(); 509 | assert_eq!(out_str, t.output_str); 510 | 511 | sh.reset(); 512 | } 513 | 514 | // Test that it works when accepting the message in pieces 515 | for t in tests.iter() { 516 | let len = t.input.len(); 517 | let mut left = len; 518 | while left > 0 { 519 | let take = (left + 1) / 2; 520 | sh.input_str(&t.input[len - left..take + len - left]); 521 | left = left - take; 522 | } 523 | 524 | let out_str = sh.result_str(); 525 | assert_eq!(out_str, t.output_str); 526 | 527 | sh.reset(); 528 | } 529 | } 530 | 531 | #[test] 532 | fn test_md5() { 533 | // Examples from wikipedia 534 | let wikipedia_tests = vec![ 535 | Test { 536 | input: "", 537 | output_str: "d41d8cd98f00b204e9800998ecf8427e" 538 | }, 539 | Test { 540 | input: "The quick brown fox jumps over the lazy dog", 541 | output_str: "9e107d9d372bb6826bd81d3542a419d6" 542 | }, 543 | Test { 544 | input: "The quick brown fox jumps over the lazy dog.", 545 | output_str: "e4d909c290d0fb1ca068ffaddf22cbd0" 546 | }, 547 | ]; 548 | 549 | let tests = wikipedia_tests; 550 | 551 | let mut sh = Md5::new(); 552 | 553 | test_hash(&mut sh, &tests[..]); 554 | } 555 | } 556 | -------------------------------------------------------------------------------- /src/stmt.rs: -------------------------------------------------------------------------------- 1 | //! Prepared statements 2 | 3 | use std::cell::{Cell, RefMut}; 4 | use std::collections::VecDeque; 5 | use std::fmt; 6 | use std::io::{self, Read, Write}; 7 | use std::sync::Arc; 8 | 9 | use error::{Error, DbError}; 10 | use types::{SessionInfo, Type, ToSql}; 11 | use message::{WriteMessage, Backend, Frontend}; 12 | use rows::{Rows, LazyRows}; 13 | use {bad_response, Connection, Transaction, StatementInternals, Result, RowsNew, InnerConnection, 14 | SessionInfoNew, LazyRowsNew, DbErrorNew, ColumnNew, StatementInfo, TransactionInternals}; 15 | 16 | /// A prepared statement. 17 | pub struct Statement<'conn> { 18 | conn: &'conn Connection, 19 | info: Arc, 20 | next_portal_id: Cell, 21 | finished: bool, 22 | } 23 | 24 | impl<'a> fmt::Debug for Statement<'a> { 25 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 26 | fmt.debug_struct("Statement") 27 | .field("name", &self.info.name) 28 | .field("parameter_types", &self.info.param_types) 29 | .field("columns", &self.info.columns) 30 | .finish() 31 | } 32 | } 33 | 34 | impl<'conn> Drop for Statement<'conn> { 35 | fn drop(&mut self) { 36 | let _ = self.finish_inner(); 37 | } 38 | } 39 | 40 | impl<'conn> StatementInternals<'conn> for Statement<'conn> { 41 | fn new(conn: &'conn Connection, 42 | info: Arc, 43 | next_portal_id: Cell, 44 | finished: bool) 45 | -> Statement<'conn> { 46 | Statement { 47 | conn: conn, 48 | info: info, 49 | next_portal_id: next_portal_id, 50 | finished: finished, 51 | } 52 | } 53 | 54 | fn conn(&self) -> &'conn Connection { 55 | self.conn 56 | } 57 | 58 | fn into_query(self, params: &[&ToSql]) -> Result> { 59 | check_desync!(self.conn); 60 | self.inner_query("", 0, params) 61 | .map(|(buf, _)| Rows::new_owned(self, buf.into_iter().collect())) 62 | } 63 | } 64 | 65 | impl<'conn> Statement<'conn> { 66 | fn finish_inner(&mut self) -> Result<()> { 67 | if self.finished { 68 | Ok(()) 69 | } else { 70 | self.finished = true; 71 | let mut conn = self.conn.conn.borrow_mut(); 72 | check_desync!(conn); 73 | conn.close_statement(&self.info.name, b'S') 74 | } 75 | } 76 | 77 | #[allow(type_complexity)] 78 | fn inner_query<'a>(&'a self, 79 | portal_name: &str, 80 | row_limit: i32, 81 | params: &[&ToSql]) 82 | -> Result<(VecDeque>>>, bool)> { 83 | let mut conn = self.conn.conn.borrow_mut(); 84 | 85 | try!(conn.raw_execute(&self.info.name, 86 | portal_name, 87 | row_limit, 88 | self.param_types(), 89 | params)); 90 | 91 | let mut buf = VecDeque::new(); 92 | let more_rows = try!(conn.read_rows(&mut buf)); 93 | Ok((buf, more_rows)) 94 | } 95 | 96 | /// Returns a slice containing the expected parameter types. 97 | pub fn param_types(&self) -> &[Type] { 98 | &self.info.param_types 99 | } 100 | 101 | /// Returns a slice describing the columns of the result of the query. 102 | pub fn columns(&self) -> &[Column] { 103 | &self.info.columns 104 | } 105 | 106 | /// Executes the prepared statement, returning the number of rows modified. 107 | /// 108 | /// If the statement does not modify any rows (e.g. SELECT), 0 is returned. 109 | /// 110 | /// # Panics 111 | /// 112 | /// Panics if the number of parameters provided does not match the number 113 | /// expected. 114 | /// 115 | /// # Example 116 | /// 117 | /// ```rust,no_run 118 | /// # use postgres::{Connection, SslMode}; 119 | /// # let conn = Connection::connect("", SslMode::None).unwrap(); 120 | /// # let bar = 1i32; 121 | /// # let baz = true; 122 | /// let stmt = conn.prepare("UPDATE foo SET bar = $1 WHERE baz = $2").unwrap(); 123 | /// let rows_updated = stmt.execute(&[&bar, &baz]).unwrap(); 124 | /// println!("{} rows updated", rows_updated); 125 | /// ``` 126 | pub fn execute(&self, params: &[&ToSql]) -> Result { 127 | let mut conn = self.conn.conn.borrow_mut(); 128 | check_desync!(conn); 129 | try!(conn.raw_execute(&self.info.name, "", 0, self.param_types(), params)); 130 | 131 | let num; 132 | loop { 133 | match try!(conn.read_message()) { 134 | Backend::DataRow { .. } => {} 135 | Backend::ErrorResponse { fields } => { 136 | try!(conn.wait_for_ready()); 137 | return DbError::new(fields); 138 | } 139 | Backend::CommandComplete { tag } => { 140 | num = parse_update_count(tag); 141 | break; 142 | } 143 | Backend::EmptyQueryResponse => { 144 | num = 0; 145 | break; 146 | } 147 | Backend::CopyInResponse { .. } => { 148 | try!(conn.write_messages(&[Frontend::CopyFail { 149 | message: "COPY queries cannot be directly \ 150 | executed", 151 | }, 152 | Frontend::Sync])); 153 | } 154 | Backend::CopyOutResponse { .. } => { 155 | loop { 156 | match try!(conn.read_message()) { 157 | Backend::CopyDone => break, 158 | Backend::ErrorResponse { fields } => { 159 | try!(conn.wait_for_ready()); 160 | return DbError::new(fields); 161 | } 162 | _ => {} 163 | } 164 | } 165 | num = 0; 166 | break; 167 | } 168 | _ => { 169 | conn.desynchronized = true; 170 | return Err(Error::Io(bad_response())); 171 | } 172 | } 173 | } 174 | try!(conn.wait_for_ready()); 175 | 176 | Ok(num) 177 | } 178 | 179 | /// Executes the prepared statement, returning the resulting rows. 180 | /// 181 | /// # Panics 182 | /// 183 | /// Panics if the number of parameters provided does not match the number 184 | /// expected. 185 | /// 186 | /// # Example 187 | /// 188 | /// ```rust,no_run 189 | /// # use postgres::{Connection, SslMode}; 190 | /// # let conn = Connection::connect("", SslMode::None).unwrap(); 191 | /// let stmt = conn.prepare("SELECT foo FROM bar WHERE baz = $1").unwrap(); 192 | /// # let baz = true; 193 | /// for row in &stmt.query(&[&baz]).unwrap() { 194 | /// let foo: i32 = row.get("foo"); 195 | /// println!("foo: {}", foo); 196 | /// } 197 | /// ``` 198 | pub fn query<'a>(&'a self, params: &[&ToSql]) -> Result> { 199 | check_desync!(self.conn); 200 | self.inner_query("", 0, params).map(|(buf, _)| Rows::new(self, buf.into_iter().collect())) 201 | } 202 | 203 | /// Executes the prepared statement, returning a lazily loaded iterator 204 | /// over the resulting rows. 205 | /// 206 | /// No more than `row_limit` rows will be stored in memory at a time. Rows 207 | /// will be pulled from the database in batches of `row_limit` as needed. 208 | /// If `row_limit` is less than or equal to 0, `lazy_query` is equivalent 209 | /// to `query`. 210 | /// 211 | /// This can only be called inside of a transaction, and the `Transaction` 212 | /// object representing the active transaction must be passed to 213 | /// `lazy_query`. 214 | /// 215 | /// # Panics 216 | /// 217 | /// Panics if the provided `Transaction` is not associated with the same 218 | /// `Connection` as this `Statement`, if the `Transaction` is not 219 | /// active, or if the number of parameters provided does not match the 220 | /// number of parameters expected. 221 | pub fn lazy_query<'trans, 'stmt>(&'stmt self, 222 | trans: &'trans Transaction, 223 | params: &[&ToSql], 224 | row_limit: i32) 225 | -> Result> { 226 | assert!(self.conn as *const _ == trans.conn() as *const _, 227 | "the `Transaction` passed to `lazy_query` must be associated with the same \ 228 | `Connection` as the `Statement`"); 229 | let conn = self.conn.conn.borrow(); 230 | check_desync!(conn); 231 | assert!(conn.trans_depth == trans.depth(), 232 | "`lazy_query` must be passed the active transaction"); 233 | drop(conn); 234 | 235 | let id = self.next_portal_id.get(); 236 | self.next_portal_id.set(id + 1); 237 | let portal_name = format!("{}p{}", self.info.name, id); 238 | 239 | self.inner_query(&portal_name, row_limit, params).map(move |(data, more_rows)| { 240 | LazyRows::new(self, data, portal_name, row_limit, more_rows, false, trans) 241 | }) 242 | } 243 | 244 | /// Executes a `COPY FROM STDIN` statement, returning the number of rows 245 | /// added. 246 | /// 247 | /// The contents of the provided reader are passed to the Postgres server 248 | /// verbatim; it is the caller's responsibility to ensure it uses the 249 | /// proper format. See the 250 | /// [Postgres documentation](http://www.postgresql.org/docs/9.4/static/sql-copy.html) 251 | /// for details. 252 | /// 253 | /// If the statement is not a `COPY FROM STDIN` statement it will still be 254 | /// executed and this method will return an error. 255 | /// 256 | /// # Examples 257 | /// 258 | /// ```rust,no_run 259 | /// # use postgres::{Connection, SslMode}; 260 | /// # let conn = Connection::connect("", SslMode::None).unwrap(); 261 | /// conn.batch_execute("CREATE TABLE people (id INT PRIMARY KEY, name VARCHAR)").unwrap(); 262 | /// let stmt = conn.prepare("COPY people FROM STDIN").unwrap(); 263 | /// stmt.copy_in(&[], &mut "1\tjohn\n2\tjane\n".as_bytes()).unwrap(); 264 | /// ``` 265 | pub fn copy_in(&self, params: &[&ToSql], r: &mut R) -> Result { 266 | let mut conn = self.conn.conn.borrow_mut(); 267 | try!(conn.raw_execute(&self.info.name, "", 0, self.param_types(), params)); 268 | 269 | let (format, column_formats) = match try!(conn.read_message()) { 270 | Backend::CopyInResponse { format, column_formats } => (format, column_formats), 271 | Backend::ErrorResponse { fields } => { 272 | try!(conn.wait_for_ready()); 273 | return DbError::new(fields); 274 | } 275 | _ => { 276 | loop { 277 | if let Backend::ReadyForQuery { .. } = try!(conn.read_message()) { 278 | return Err(Error::Io(io::Error::new(io::ErrorKind::InvalidInput, 279 | "called `copy_in` on a \ 280 | non-`COPY FROM STDIN` \ 281 | statement"))); 282 | } 283 | } 284 | } 285 | }; 286 | 287 | let mut info = CopyInfo { 288 | conn: conn, 289 | format: Format::from_u16(format as u16), 290 | column_formats: column_formats.iter().map(|&f| Format::from_u16(f)).collect(), 291 | }; 292 | 293 | let mut buf = [0; 16 * 1024]; 294 | loop { 295 | match fill_copy_buf(&mut buf, r, &info) { 296 | Ok(0) => break, 297 | Ok(len) => { 298 | try_desync!(info.conn, 299 | info.conn.stream.write_message(&Frontend::CopyData { data: &buf[..len] })); 300 | } 301 | Err(err) => { 302 | try!(info.conn.write_messages(&[Frontend::CopyFail { message: "" }, 303 | Frontend::CopyDone, 304 | Frontend::Sync])); 305 | match try!(info.conn.read_message()) { 306 | Backend::ErrorResponse { .. } => { 307 | // expected from the CopyFail 308 | } 309 | _ => { 310 | info.conn.desynchronized = true; 311 | return Err(Error::Io(bad_response())); 312 | } 313 | } 314 | try!(info.conn.wait_for_ready()); 315 | return Err(Error::Io(err)); 316 | } 317 | } 318 | } 319 | 320 | try!(info.conn.write_messages(&[Frontend::CopyDone, Frontend::Sync])); 321 | 322 | let num = match try!(info.conn.read_message()) { 323 | Backend::CommandComplete { tag } => parse_update_count(tag), 324 | Backend::ErrorResponse { fields } => { 325 | try!(info.conn.wait_for_ready()); 326 | return DbError::new(fields); 327 | } 328 | _ => { 329 | info.conn.desynchronized = true; 330 | return Err(Error::Io(bad_response())); 331 | } 332 | }; 333 | 334 | try!(info.conn.wait_for_ready()); 335 | Ok(num) 336 | } 337 | 338 | /// Executes a `COPY TO STDOUT` statement, passing the resulting data to 339 | /// the provided writer and returning the number of rows received. 340 | /// 341 | /// See the [Postgres documentation](http://www.postgresql.org/docs/9.4/static/sql-copy.html) 342 | /// for details on the data format. 343 | /// 344 | /// If the statement is not a `COPY TO STDOUT` statement it will still be 345 | /// executed and this method will return an error. 346 | /// 347 | /// # Examples 348 | /// 349 | /// ```rust,no_run 350 | /// # use postgres::{Connection, SslMode}; 351 | /// # let conn = Connection::connect("", SslMode::None).unwrap(); 352 | /// conn.batch_execute(" 353 | /// CREATE TABLE people (id INT PRIMARY KEY, name VARCHAR); 354 | /// INSERT INTO people (id, name) VALUES (1, 'john'), (2, 'jane');").unwrap(); 355 | /// let stmt = conn.prepare("COPY people TO STDOUT").unwrap(); 356 | /// let mut buf = vec![]; 357 | /// stmt.copy_out(&[], &mut buf).unwrap(); 358 | /// assert_eq!(buf, b"1\tjohn\n2\tjane\n"); 359 | /// ``` 360 | pub fn copy_out<'a, W: WriteWithInfo>(&'a self, params: &[&ToSql], w: &mut W) -> Result { 361 | let mut conn = self.conn.conn.borrow_mut(); 362 | try!(conn.raw_execute(&self.info.name, "", 0, self.param_types(), params)); 363 | 364 | let (format, column_formats) = match try!(conn.read_message()) { 365 | Backend::CopyOutResponse { format, column_formats } => (format, column_formats), 366 | Backend::CopyInResponse { .. } => { 367 | try!(conn.write_messages(&[Frontend::CopyFail { message: "" }, 368 | Frontend::CopyDone, 369 | Frontend::Sync])); 370 | match try!(conn.read_message()) { 371 | Backend::ErrorResponse { .. } => { 372 | // expected from the CopyFail 373 | } 374 | _ => { 375 | conn.desynchronized = true; 376 | return Err(Error::Io(bad_response())); 377 | } 378 | } 379 | try!(conn.wait_for_ready()); 380 | return Err(Error::Io(io::Error::new(io::ErrorKind::InvalidInput, 381 | "called `copy_out` on a non-`COPY TO \ 382 | STDOUT` statement"))); 383 | } 384 | Backend::ErrorResponse { fields } => { 385 | try!(conn.wait_for_ready()); 386 | return DbError::new(fields); 387 | } 388 | _ => { 389 | loop { 390 | if let Backend::ReadyForQuery { .. } = try!(conn.read_message()) { 391 | return Err(Error::Io(io::Error::new(io::ErrorKind::InvalidInput, 392 | "called `copy_out` on a \ 393 | non-`COPY TO STDOUT` statement"))); 394 | } 395 | } 396 | } 397 | }; 398 | 399 | let mut info = CopyInfo { 400 | conn: conn, 401 | format: Format::from_u16(format as u16), 402 | column_formats: column_formats.iter().map(|&f| Format::from_u16(f)).collect(), 403 | }; 404 | 405 | let count; 406 | loop { 407 | match try!(info.conn.read_message()) { 408 | Backend::CopyData { data } => { 409 | let mut data = &data[..]; 410 | while !data.is_empty() { 411 | match w.write_with_info(data, &info) { 412 | Ok(n) => data = &data[n..], 413 | Err(e) => { 414 | loop { 415 | if let Backend::ReadyForQuery { .. } = try!(info.conn.read_message()) { 416 | return Err(Error::Io(e)); 417 | } 418 | } 419 | } 420 | } 421 | } 422 | } 423 | Backend::CopyDone => {} 424 | Backend::CommandComplete { tag } => { 425 | count = parse_update_count(tag); 426 | break; 427 | } 428 | Backend::ErrorResponse { fields } => { 429 | loop { 430 | if let Backend::ReadyForQuery { .. } = try!(info.conn.read_message()) { 431 | return DbError::new(fields); 432 | } 433 | } 434 | } 435 | _ => { 436 | loop { 437 | if let Backend::ReadyForQuery { .. } = try!(info.conn.read_message()) { 438 | return Err(Error::Io(bad_response())); 439 | } 440 | } 441 | } 442 | } 443 | } 444 | 445 | try!(info.conn.wait_for_ready()); 446 | Ok(count) 447 | } 448 | 449 | /// Consumes the statement, clearing it from the Postgres session. 450 | /// 451 | /// If this statement was created via the `prepare_cached` method, `finish` 452 | /// does nothing. 453 | /// 454 | /// Functionally identical to the `Drop` implementation of the 455 | /// `Statement` except that it returns any error to the caller. 456 | pub fn finish(mut self) -> Result<()> { 457 | self.finish_inner() 458 | } 459 | } 460 | 461 | fn fill_copy_buf(buf: &mut [u8], r: &mut R, info: &CopyInfo) -> io::Result { 462 | let mut nread = 0; 463 | while nread < buf.len() { 464 | match r.read_with_info(&mut buf[nread..], info) { 465 | Ok(0) => break, 466 | Ok(n) => nread += n, 467 | Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} 468 | Err(e) => return Err(e), 469 | } 470 | } 471 | Ok(nread) 472 | } 473 | 474 | /// Information about a column of the result of a query. 475 | #[derive(PartialEq, Eq, Clone, Debug)] 476 | pub struct Column { 477 | name: String, 478 | type_: Type, 479 | } 480 | 481 | impl ColumnNew for Column { 482 | fn new(name: String, type_: Type) -> Column { 483 | Column { 484 | name: name, 485 | type_: type_, 486 | } 487 | } 488 | } 489 | 490 | impl Column { 491 | /// The name of the column. 492 | pub fn name(&self) -> &str { 493 | &self.name 494 | } 495 | 496 | /// The type of the data in the column. 497 | pub fn type_(&self) -> &Type { 498 | &self.type_ 499 | } 500 | } 501 | 502 | /// A struct containing information relevant for a `COPY` operation. 503 | pub struct CopyInfo<'a> { 504 | conn: RefMut<'a, InnerConnection>, 505 | format: Format, 506 | column_formats: Vec, 507 | } 508 | 509 | impl<'a> CopyInfo<'a> { 510 | /// Returns the format of the overall data. 511 | pub fn format(&self) -> Format { 512 | self.format 513 | } 514 | 515 | /// Returns the format of the individual columns. 516 | pub fn column_formats(&self) -> &[Format] { 517 | &self.column_formats 518 | } 519 | 520 | /// Returns session info for the associated connection. 521 | pub fn session_info<'b>(&'b self) -> SessionInfo<'b> { 522 | SessionInfo::new(&*self.conn) 523 | } 524 | } 525 | 526 | /// Like `Read` except that a `CopyInfo` object is provided as well. 527 | /// 528 | /// All types that implement `Read` also implement this trait. 529 | pub trait ReadWithInfo { 530 | /// Like `Read::read`. 531 | fn read_with_info(&mut self, buf: &mut [u8], info: &CopyInfo) -> io::Result; 532 | } 533 | 534 | impl ReadWithInfo for R { 535 | fn read_with_info(&mut self, buf: &mut [u8], _: &CopyInfo) -> io::Result { 536 | self.read(buf) 537 | } 538 | } 539 | 540 | /// Like `Write` except that a `CopyInfo` object is provided as well. 541 | /// 542 | /// All types that implement `Write` also implement this trait. 543 | pub trait WriteWithInfo { 544 | /// Like `Write::write`. 545 | fn write_with_info(&mut self, buf: &[u8], info: &CopyInfo) -> io::Result; 546 | } 547 | 548 | impl WriteWithInfo for W { 549 | fn write_with_info(&mut self, buf: &[u8], _: &CopyInfo) -> io::Result { 550 | self.write(buf) 551 | } 552 | } 553 | 554 | /// The format of a portion of COPY query data. 555 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 556 | pub enum Format { 557 | /// A text based format. 558 | Text, 559 | /// A binary format. 560 | Binary, 561 | } 562 | 563 | impl Format { 564 | fn from_u16(value: u16) -> Format { 565 | match value { 566 | 0 => Format::Text, 567 | _ => Format::Binary, 568 | } 569 | } 570 | } 571 | 572 | fn parse_update_count(tag: String) -> u64 { 573 | tag.split(' ').last().unwrap().parse().unwrap_or(0) 574 | } 575 | --------------------------------------------------------------------------------