diff --git a/postgres-shared/src/types/mod.rs b/postgres-shared/src/types/mod.rs index 76ba41fa..c18f6b81 100644 --- a/postgres-shared/src/types/mod.rs +++ b/postgres-shared/src/types/mod.rs @@ -156,11 +156,11 @@ impl Other { #[doc(hidden)] pub fn new(name: String, oid: Oid, kind: Kind, schema: String) -> Other { Other(Arc::new(OtherInner { - name: name, - oid: oid, - kind: kind, - schema: schema, - })) + name: name, + oid: oid, + kind: kind, + schema: schema, + })) } } @@ -236,19 +236,19 @@ impl WrongType { /// The following implementations are provided by this crate, along with the /// corresponding Postgres types: /// -/// | Rust type | Postgres type(s) | -/// |-----------------------------------|--------------------------------------| -/// | `bool` | BOOL | -/// | `i8` | "char" | -/// | `i16` | SMALLINT, SMALLSERIAL | -/// | `i32` | INT, SERIAL | -/// | `u32` | OID | -/// | `i64` | BIGINT, BIGSERIAL | -/// | `f32` | REAL | -/// | `f64` | DOUBLE PRECISION | -/// | `String` | VARCHAR, CHAR(n), TEXT, CITEXT, NAME | -/// | `Vec` | BYTEA | -/// | `HashMap>` | HSTORE | +/// | Rust type | Postgres type(s) | +/// |-----------------------------------|-----------------------------------------------| +/// | `bool` | BOOL | +/// | `i8` | "char" | +/// | `i16` | SMALLINT, SMALLSERIAL | +/// | `i32` | INT, SERIAL | +/// | `u32` | OID | +/// | `i64` | BIGINT, BIGSERIAL | +/// | `f32` | REAL | +/// | `f64` | DOUBLE PRECISION | +/// | `String` | VARCHAR, CHAR(n), TEXT, CITEXT, NAME, UNKNOWN | +/// | `Vec` | BYTEA | +/// | `HashMap>` | HSTORE | /// /// In addition, some implementations are provided for types in third party /// crates. These are disabled by default; to opt into one of these @@ -288,9 +288,7 @@ pub trait FromSql: Sized { /// /// The caller of this method is responsible for ensuring that this type /// is compatible with the Postgres `Type`. - fn from_sql(ty: &Type, - raw: &[u8]) - -> Result>; + fn from_sql(ty: &Type, raw: &[u8]) -> Result>; /// Creates a new value of this type from a `NULL` SQL value. /// @@ -306,9 +304,7 @@ pub trait FromSql: Sized { /// A convenience function that delegates to `from_sql` and `from_sql_null` depending on the /// value of `raw`. - fn from_sql_nullable(ty: &Type, - raw: Option<&[u8]>) - -> Result> { + fn from_sql_nullable(ty: &Type, raw: Option<&[u8]>) -> Result> { match raw { Some(raw) => Self::from_sql(ty, raw), None => Self::from_sql_null(ty), @@ -321,9 +317,7 @@ pub trait FromSql: Sized { } impl FromSql for Option { - fn from_sql(ty: &Type, - raw: &[u8]) - -> Result, Box> { + fn from_sql(ty: &Type, raw: &[u8]) -> Result, Box> { ::from_sql(ty, raw).map(Some) } @@ -337,9 +331,7 @@ impl FromSql for Option { } impl FromSql for Vec { - fn from_sql(ty: &Type, - raw: &[u8]) - -> Result, Box> { + fn from_sql(ty: &Type, raw: &[u8]) -> Result, Box> { let member_type = match *ty.kind() { Kind::Array(ref member) => member, _ => panic!("expected array type"), @@ -350,7 +342,8 @@ impl FromSql for Vec { return Err("array contains too many dimensions".into()); } - array.values() + array + .values() .and_then(|v| T::from_sql_nullable(member_type, v)) .collect() } @@ -364,9 +357,7 @@ impl FromSql for Vec { } impl FromSql for Vec { - fn from_sql(_: &Type, - raw: &[u8]) - -> Result, Box> { + fn from_sql(_: &Type, raw: &[u8]) -> Result, Box> { Ok(types::bytea_from_sql(raw).to_owned()) } @@ -380,7 +371,7 @@ impl FromSql for String { fn accepts(ty: &Type) -> bool { match *ty { - Type::Varchar | Type::Text | Type::Bpchar | Type::Name => true, + Type::Varchar | Type::Text | Type::Bpchar | Type::Name | Type::Unknown => true, Type::Other(ref u) if u.name() == "citext" => true, _ => false, } @@ -500,10 +491,7 @@ pub trait ToSql: fmt::Debug { /// The return value indicates if this value should be represented as /// `NULL`. If this is the case, implementations **must not** write /// anything to `out`. - fn to_sql(&self, - ty: &Type, - out: &mut Vec) - -> Result> + fn to_sql(&self, ty: &Type, out: &mut Vec) -> Result> where Self: Sized; /// Determines if a value of this type can be converted to the specified @@ -523,10 +511,7 @@ pub trait ToSql: fmt::Debug { impl<'a, T> ToSql for &'a T where T: ToSql { - fn to_sql(&self, - ty: &Type, - out: &mut Vec) - -> Result> { + fn to_sql(&self, ty: &Type, out: &mut Vec) -> Result> { (*self).to_sql(ty, out) } @@ -538,10 +523,7 @@ impl<'a, T> ToSql for &'a T } impl ToSql for Option { - fn to_sql(&self, - ty: &Type, - out: &mut Vec) - -> Result> { + fn to_sql(&self, ty: &Type, out: &mut Vec) -> Result> { match *self { Some(ref val) => val.to_sql(ty, out), None => Ok(IsNull::Yes), @@ -556,10 +538,7 @@ impl ToSql for Option { } impl<'a, T: ToSql> ToSql for &'a [T] { - fn to_sql(&self, - ty: &Type, - w: &mut Vec) - -> Result> { + fn to_sql(&self, ty: &Type, w: &mut Vec) -> Result> { let member_type = match *ty.kind() { Kind::Array(ref member) => member, _ => panic!("expected array type"), @@ -571,16 +550,14 @@ impl<'a, T: ToSql> ToSql for &'a [T] { }; types::array_to_sql(Some(dimension), - true, - member_type.oid(), - self.iter(), - |e, w| { - match e.to_sql(member_type, w)? { - IsNull::No => Ok(postgres_protocol::IsNull::No), - IsNull::Yes => Ok(postgres_protocol::IsNull::Yes), - } - }, - w)?; + true, + member_type.oid(), + self.iter(), + |e, w| match e.to_sql(member_type, w)? { + IsNull::No => Ok(postgres_protocol::IsNull::No), + IsNull::Yes => Ok(postgres_protocol::IsNull::Yes), + }, + w)?; Ok(IsNull::No) } @@ -595,10 +572,7 @@ impl<'a, T: ToSql> ToSql for &'a [T] { } impl<'a> ToSql for &'a [u8] { - fn to_sql(&self, - _: &Type, - w: &mut Vec) - -> Result> { + fn to_sql(&self, _: &Type, w: &mut Vec) -> Result> { types::bytea_to_sql(*self, w); Ok(IsNull::No) } @@ -609,10 +583,7 @@ impl<'a> ToSql for &'a [u8] { } impl ToSql for Vec { - fn to_sql(&self, - ty: &Type, - w: &mut Vec) - -> Result> { + fn to_sql(&self, ty: &Type, w: &mut Vec) -> Result> { <&[T] as ToSql>::to_sql(&&**self, ty, w) } @@ -624,10 +595,7 @@ impl ToSql for Vec { } impl ToSql for Vec { - fn to_sql(&self, - ty: &Type, - w: &mut Vec) - -> Result> { + fn to_sql(&self, ty: &Type, w: &mut Vec) -> Result> { <&[u8] as ToSql>::to_sql(&&**self, ty, w) } @@ -639,10 +607,7 @@ impl ToSql for Vec { } impl<'a> ToSql for &'a str { - fn to_sql(&self, - _: &Type, - w: &mut Vec) - -> Result> { + fn to_sql(&self, _: &Type, w: &mut Vec) -> Result> { types::text_to_sql(*self, w); Ok(IsNull::No) } @@ -659,10 +624,7 @@ impl<'a> ToSql for &'a str { } impl ToSql for String { - fn to_sql(&self, - ty: &Type, - w: &mut Vec) - -> Result> { + fn to_sql(&self, ty: &Type, w: &mut Vec) -> Result> { <&str as ToSql>::to_sql(&&**self, ty, w) } @@ -701,11 +663,9 @@ simple_to!(f32, float4_to_sql, Type::Float4); simple_to!(f64, float8_to_sql, Type::Float8); impl ToSql for HashMap> { - fn to_sql(&self, - _: &Type, - w: &mut Vec) - -> Result> { - types::hstore_to_sql(self.iter().map(|(k, v)| (&**k, v.as_ref().map(|v| &**v))), w)?; + fn to_sql(&self, _: &Type, w: &mut Vec) -> Result> { + types::hstore_to_sql(self.iter().map(|(k, v)| (&**k, v.as_ref().map(|v| &**v))), + w)?; Ok(IsNull::No) } diff --git a/postgres/tests/types/mod.rs b/postgres/tests/types/mod.rs index 7634452a..82f6bba6 100644 --- a/postgres/tests/types/mod.rs +++ b/postgres/tests/types/mod.rs @@ -26,7 +26,7 @@ mod chrono; #[cfg(feature = "with-geo")] mod geo; -fn test_type(sql_type: &str, checks: &[(T, S)]) { +fn test_type(sql_type: &str, checks: &[(T, S)]) { let conn = or_panic!(Connection::connect("postgres://postgres@localhost", TlsMode::None)); for &(ref val, ref repr) in checks.iter() { let stmt = or_panic!(conn.prepare(&*format!("SELECT {}::{}", *repr, sql_type))); @@ -49,8 +49,8 @@ fn test_ref_tosql() { #[test] fn test_bool_params() { - test_type("BOOL", &[(Some(true), "'t'"), (Some(false), "'f'"), - (None, "NULL")]); + test_type("BOOL", + &[(Some(true), "'t'"), (Some(false), "'f'"), (None, "NULL")]); } #[test] @@ -60,63 +60,79 @@ fn test_i8_params() { #[test] fn test_name_params() { - test_type("NAME", &[(Some("hello world".to_owned()), "'hello world'"), - (Some("イロハニホヘト チリヌルヲ".to_owned()), "'イロハニホヘト チリヌルヲ'"), - (None, "NULL")]); + test_type("NAME", + &[(Some("hello world".to_owned()), "'hello world'"), + (Some("イロハニホヘト チリヌルヲ".to_owned()), + "'イロハニホヘト チリヌルヲ'"), + (None, "NULL")]); } #[test] fn test_i16_params() { - test_type("SMALLINT", &[(Some(15001i16), "15001"), - (Some(-15001i16), "-15001"), (None, "NULL")]); + test_type("SMALLINT", + &[(Some(15001i16), "15001"), + (Some(-15001i16), "-15001"), + (None, "NULL")]); } #[test] fn test_i32_params() { - test_type("INT", &[(Some(2147483548i32), "2147483548"), - (Some(-2147483548i32), "-2147483548"), (None, "NULL")]); + test_type("INT", + &[(Some(2147483548i32), "2147483548"), + (Some(-2147483548i32), "-2147483548"), + (None, "NULL")]); } #[test] fn test_oid_params() { - test_type("OID", &[(Some(2147483548u32), "2147483548"), - (Some(4000000000), "4000000000"), (None, "NULL")]); + test_type("OID", + &[(Some(2147483548u32), "2147483548"), + (Some(4000000000), "4000000000"), + (None, "NULL")]); } #[test] fn test_i64_params() { - test_type("BIGINT", &[(Some(9223372036854775708i64), "9223372036854775708"), - (Some(-9223372036854775708i64), "-9223372036854775708"), - (None, "NULL")]); + test_type("BIGINT", + &[(Some(9223372036854775708i64), "9223372036854775708"), + (Some(-9223372036854775708i64), "-9223372036854775708"), + (None, "NULL")]); } #[test] fn test_f32_params() { - test_type("REAL", &[(Some(f32::INFINITY), "'infinity'"), - (Some(f32::NEG_INFINITY), "'-infinity'"), - (Some(1000.55), "1000.55"), (None, "NULL")]); + test_type("REAL", + &[(Some(f32::INFINITY), "'infinity'"), + (Some(f32::NEG_INFINITY), "'-infinity'"), + (Some(1000.55), "1000.55"), + (None, "NULL")]); } #[test] fn test_f64_params() { - test_type("DOUBLE PRECISION", &[(Some(f64::INFINITY), "'infinity'"), - (Some(f64::NEG_INFINITY), "'-infinity'"), - (Some(10000.55), "10000.55"), - (None, "NULL")]); + test_type("DOUBLE PRECISION", + &[(Some(f64::INFINITY), "'infinity'"), + (Some(f64::NEG_INFINITY), "'-infinity'"), + (Some(10000.55), "10000.55"), + (None, "NULL")]); } #[test] fn test_varchar_params() { - test_type("VARCHAR", &[(Some("hello world".to_owned()), "'hello world'"), - (Some("イロハニホヘト チリヌルヲ".to_owned()), "'イロハニホヘト チリヌルヲ'"), - (None, "NULL")]); + test_type("VARCHAR", + &[(Some("hello world".to_owned()), "'hello world'"), + (Some("イロハニホヘト チリヌルヲ".to_owned()), + "'イロハニホヘト チリヌルヲ'"), + (None, "NULL")]); } #[test] fn test_text_params() { - test_type("TEXT", &[(Some("hello world".to_owned()), "'hello world'"), - (Some("イロハニホヘト チリヌルヲ".to_owned()), "'イロハニホヘト チリヌルヲ'"), - (None, "NULL")]); + test_type("TEXT", + &[(Some("hello world".to_owned()), "'hello world'"), + (Some("イロハニホヘト チリヌルヲ".to_owned()), + "'イロハニホヘト チリヌルヲ'"), + (None, "NULL")]); } #[test] @@ -125,13 +141,14 @@ fn test_bpchar_params() { or_panic!(conn.execute("CREATE TEMPORARY TABLE foo ( id SERIAL PRIMARY KEY, b CHAR(5) - )", &[])); + )", + &[])); or_panic!(conn.execute("INSERT INTO foo (b) VALUES ($1), ($2), ($3)", &[&Some("12345"), &Some("123"), &None::<&'static str>])); let stmt = or_panic!(conn.prepare("SELECT b FROM foo ORDER BY id")); let res = or_panic!(stmt.query(&[])); - assert_eq!(vec!(Some("12345".to_owned()), Some("123 ".to_owned()), None), + assert_eq!(vec![Some("12345".to_owned()), Some("123 ".to_owned()), None], res.iter().map(|row| row.get(0)).collect::>()); } @@ -141,19 +158,22 @@ fn test_citext_params() { or_panic!(conn.execute("CREATE TEMPORARY TABLE foo ( id SERIAL PRIMARY KEY, b CITEXT - )", &[])); + )", + &[])); or_panic!(conn.execute("INSERT INTO foo (b) VALUES ($1), ($2), ($3)", &[&Some("foobar"), &Some("FooBar"), &None::<&'static str>])); let stmt = or_panic!(conn.prepare("SELECT id FROM foo WHERE b = 'FOOBAR' ORDER BY id")); let res = or_panic!(stmt.query(&[])); - assert_eq!(vec!(Some(1i32), Some(2i32)), res.iter().map(|row| row.get(0)).collect::>()); + assert_eq!(vec![Some(1i32), Some(2i32)], + res.iter().map(|row| row.get(0)).collect::>()); } #[test] fn test_bytea_params() { - test_type("BYTEA", &[(Some(vec!(0u8, 1, 2, 3, 254, 255)), "'\\x00010203feff'"), - (None, "NULL")]); + test_type("BYTEA", + &[(Some(vec![0u8, 1, 2, 3, 254, 255]), "'\\x00010203feff'"), + (None, "NULL")]); } #[test] @@ -167,22 +187,23 @@ fn test_hstore_params() { } test_type("hstore", &[(Some(make_map!("a".to_owned() => Some("1".to_owned()))), "'a=>1'"), - (Some(make_map!("hello".to_owned() => Some("world!".to_owned()), + (Some(make_map!("hello".to_owned() => Some("world!".to_owned()), "hola".to_owned() => Some("mundo!".to_owned()), "what".to_owned() => None)), - "'hello=>world!,hola=>mundo!,what=>NULL'"), + "'hello=>world!,hola=>mundo!,what=>NULL'"), (None, "NULL")]); } #[test] fn test_array_params() { - test_type("integer[]", &[(Some(vec!(1i32, 2i32)), "ARRAY[1,2]"), - (Some(vec!(1i32)), "ARRAY[1]"), - (Some(vec!()), "ARRAY[]"), - (None, "NULL")]); + test_type("integer[]", + &[(Some(vec![1i32, 2i32]), "ARRAY[1,2]"), + (Some(vec![1i32]), "ARRAY[1]"), + (Some(vec![]), "ARRAY[]"), + (None, "NULL")]); } -fn test_nan_param(sql_type: &str) { +fn test_nan_param(sql_type: &str) { let conn = or_panic!(Connection::connect("postgres://postgres@localhost", TlsMode::None)); let stmt = or_panic!(conn.prepare(&*format!("SELECT 'NaN'::{}", sql_type))); let result = or_panic!(stmt.query(&[])); @@ -215,20 +236,27 @@ fn test_pg_database_datname() { fn test_slice() { let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap(); conn.batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY, f VARCHAR); - INSERT INTO foo (f) VALUES ('a'), ('b'), ('c'), ('d');").unwrap(); + INSERT INTO foo (f) VALUES ('a'), ('b'), ('c'), ('d');") + .unwrap(); - let stmt = conn.prepare("SELECT f FROM foo WHERE id = ANY($1)").unwrap(); + let stmt = conn.prepare("SELECT f FROM foo WHERE id = ANY($1)") + .unwrap(); let result = stmt.query(&[&&[1i32, 3, 4][..]]).unwrap(); assert_eq!(vec!["a".to_owned(), "c".to_owned(), "d".to_owned()], - result.iter().map(|r| r.get::<_, String>(0)).collect::>()); + result + .iter() + .map(|r| r.get::<_, String>(0)) + .collect::>()); } #[test] fn test_slice_wrong_type() { let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap(); - conn.batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY)").unwrap(); + conn.batch_execute("CREATE TEMPORARY TABLE foo (id SERIAL PRIMARY KEY)") + .unwrap(); - let stmt = conn.prepare("SELECT * FROM foo WHERE id = ANY($1)").unwrap(); + let stmt = conn.prepare("SELECT * FROM foo WHERE id = ANY($1)") + .unwrap(); match stmt.query(&[&&["hi"][..]]) { Ok(_) => panic!("Unexpected success"), Err(Error::Conversion(ref e)) if e.is::() => {} @@ -254,7 +282,10 @@ fn domain() { struct SessionId(Vec); impl ToSql for SessionId { - fn to_sql(&self, ty: &Type, out: &mut Vec) -> result::Result> { + fn to_sql(&self, + ty: &Type, + out: &mut Vec) + -> result::Result> { let inner = match *ty.kind() { Kind::Domain(ref inner) => inner, _ => unreachable!(), @@ -263,14 +294,20 @@ fn domain() { } fn accepts(ty: &Type) -> bool { - ty.name() == "session_id" && match *ty.kind() { Kind::Domain(_) => true, _ => false } + ty.name() == "session_id" && + match *ty.kind() { + Kind::Domain(_) => true, + _ => false, + } } to_sql_checked!(); } impl FromSql for SessionId { - fn from_sql(ty: &Type, raw: &[u8]) -> result::Result> { + fn from_sql(ty: &Type, + raw: &[u8]) + -> result::Result> { Vec::::from_sql(ty, raw).map(SessionId) } @@ -286,7 +323,8 @@ fn domain() { .unwrap(); let id = SessionId(b"0123456789abcdef".to_vec()); - conn.execute("INSERT INTO pg_temp.foo (id) VALUES ($1)", &[&id]).unwrap(); + conn.execute("INSERT INTO pg_temp.foo (id) VALUES ($1)", &[&id]) + .unwrap(); let rows = conn.query("SELECT id FROM pg_temp.foo", &[]).unwrap(); assert_eq!(id, rows.get(0).get(0)); } @@ -320,15 +358,28 @@ fn composite() { #[test] fn enum_() { let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap(); - conn.batch_execute("CREATE TYPE pg_temp.mood AS ENUM ('sad', 'ok', 'happy');").unwrap(); + conn.batch_execute("CREATE TYPE pg_temp.mood AS ENUM ('sad', 'ok', 'happy');") + .unwrap(); let stmt = conn.prepare("SELECT $1::mood").unwrap(); let type_ = &stmt.param_types()[0]; assert_eq!(type_.name(), "mood"); match *type_.kind() { Kind::Enum(ref variants) => { - assert_eq!(variants, &["sad".to_owned(), "ok".to_owned(), "happy".to_owned()]); + assert_eq!(variants, + &["sad".to_owned(), "ok".to_owned(), "happy".to_owned()]); } _ => panic!("bad type"), } } + +#[test] +fn unknown() { + let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap(); + let rows = conn.query("SELECT 'hello world'", &[]).unwrap(); + match *rows.columns()[0].type_() { + Type::Unknown => {} + _ => panic!("bad type"), + } + assert_eq!("hello world", rows.get(0).get::<_, String>(0)); +}