From a97208c0168301323aa48c02d94adc5680e41194 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Fri, 15 Apr 2022 00:07:45 +0200 Subject: [PATCH] postgres: use `Oid` type everywhere instead of `u32` (#1602) * postgres: use Oid type instead of u32 * Make serde happy * Expose the inner u32 * docs * Try to fix tests * Fix unit tests * Fix order * Not sure what happened here --- sqlx-core/src/postgres/arguments.rs | 2 +- sqlx-core/src/postgres/connection/describe.rs | 36 +-- .../src/postgres/connection/establish.rs | 3 +- sqlx-core/src/postgres/connection/executor.rs | 7 +- sqlx-core/src/postgres/connection/mod.rs | 9 +- sqlx-core/src/postgres/io/buf_mut.rs | 14 +- sqlx-core/src/postgres/message/bind.rs | 5 +- sqlx-core/src/postgres/message/close.rs | 5 +- sqlx-core/src/postgres/message/describe.rs | 9 +- sqlx-core/src/postgres/message/execute.rs | 5 +- .../postgres/message/parameter_description.rs | 9 +- sqlx-core/src/postgres/message/parse.rs | 11 +- .../src/postgres/message/row_description.rs | 5 +- sqlx-core/src/postgres/type_info.rs | 205 +++++++++--------- sqlx-core/src/postgres/types/array.rs | 5 +- sqlx-core/src/postgres/types/int.rs | 29 --- sqlx-core/src/postgres/types/mod.rs | 2 + sqlx-core/src/postgres/types/oid.rs | 76 +++++++ sqlx-core/src/postgres/types/record.rs | 7 +- sqlx-macros/src/database/postgres.rs | 5 +- tests/postgres/postgres.rs | 18 +- tests/postgres/types.rs | 4 +- 22 files changed, 270 insertions(+), 201 deletions(-) create mode 100644 sqlx-core/src/postgres/types/oid.rs diff --git a/sqlx-core/src/postgres/arguments.rs b/sqlx-core/src/postgres/arguments.rs index 8a210c4691..4ca51b5032 100644 --- a/sqlx-core/src/postgres/arguments.rs +++ b/sqlx-core/src/postgres/arguments.rs @@ -96,7 +96,7 @@ impl PgArguments { for (offset, name) in type_holes { let oid = conn.fetch_type_id_by_name(&*name).await?; - buffer[*offset..(*offset + 4)].copy_from_slice(&oid.to_be_bytes()); + buffer[*offset..(*offset + 4)].copy_from_slice(&oid.0.to_be_bytes()); } Ok(()) diff --git a/sqlx-core/src/postgres/connection/describe.rs b/sqlx-core/src/postgres/connection/describe.rs index 3b8d936fb3..a5e2a714db 100644 --- a/sqlx-core/src/postgres/connection/describe.rs +++ b/sqlx-core/src/postgres/connection/describe.rs @@ -3,6 +3,7 @@ use crate::ext::ustr::UStr; use crate::postgres::message::{ParameterDescription, RowDescription}; use crate::postgres::statement::PgStatementMetadata; use crate::postgres::type_info::{PgCustomType, PgType, PgTypeKind}; +use crate::postgres::types::Oid; use crate::postgres::{PgArguments, PgColumn, PgConnection, PgTypeInfo}; use crate::query_as::query_as; use crate::query_scalar::{query_scalar, query_scalar_with}; @@ -147,7 +148,7 @@ impl PgConnection { async fn maybe_fetch_type_info_by_oid( &mut self, - oid: u32, + oid: Oid, should_fetch: bool, ) -> Result { // first we check if this is a built-in type @@ -183,9 +184,9 @@ impl PgConnection { } } - fn fetch_type_by_oid(&mut self, oid: u32) -> BoxFuture<'_, Result> { + fn fetch_type_by_oid(&mut self, oid: Oid) -> BoxFuture<'_, Result> { Box::pin(async move { - let (name, typ_type, category, relation_id, element, base_type): (String, i8, i8, u32, u32, u32) = query_as( + let (name, typ_type, category, relation_id, element, base_type): (String, i8, i8, Oid, Oid, Oid) = query_as( "SELECT typname, typtype, typcategory, typrelid, typelem, typbasetype FROM pg_catalog.pg_type WHERE oid = $1", ) .bind(oid) @@ -237,7 +238,7 @@ impl PgConnection { }) } - async fn fetch_enum_by_oid(&mut self, oid: u32, name: String) -> Result { + async fn fetch_enum_by_oid(&mut self, oid: Oid, name: String) -> Result { let variants: Vec = query_scalar( r#" SELECT enumlabel @@ -259,12 +260,12 @@ ORDER BY enumsortorder fn fetch_composite_by_oid( &mut self, - oid: u32, - relation_id: u32, + oid: Oid, + relation_id: Oid, name: String, ) -> BoxFuture<'_, Result> { Box::pin(async move { - let raw_fields: Vec<(String, u32)> = query_as( + let raw_fields: Vec<(String, Oid)> = query_as( r#" SELECT attname, atttypid FROM pg_catalog.pg_attribute @@ -296,8 +297,8 @@ ORDER BY attnum fn fetch_domain_by_oid( &mut self, - oid: u32, - base_type: u32, + oid: Oid, + base_type: Oid, name: String, ) -> BoxFuture<'_, Result> { Box::pin(async move { @@ -313,11 +314,11 @@ ORDER BY attnum fn fetch_range_by_oid( &mut self, - oid: u32, + oid: Oid, name: String, ) -> BoxFuture<'_, Result> { Box::pin(async move { - let element_oid: u32 = query_scalar( + let element_oid: Oid = query_scalar( r#" SELECT rngsubtype FROM pg_catalog.pg_range @@ -338,13 +339,13 @@ WHERE rngtypid = $1 }) } - pub(crate) async fn fetch_type_id_by_name(&mut self, name: &str) -> Result { + pub(crate) async fn fetch_type_id_by_name(&mut self, name: &str) -> Result { if let Some(oid) = self.cache_type_oid.get(name) { return Ok(*oid); } // language=SQL - let (oid,): (u32,) = query_as( + let (oid,): (Oid,) = query_as( " SELECT oid FROM pg_catalog.pg_type WHERE typname ILIKE $1 ", @@ -362,7 +363,7 @@ SELECT oid FROM pg_catalog.pg_type WHERE typname ILIKE $1 pub(crate) async fn get_nullable_for_columns( &mut self, - stmt_id: u32, + stmt_id: Oid, meta: &PgStatementMetadata, ) -> Result>, Error> { if meta.columns.is_empty() { @@ -424,10 +425,13 @@ SELECT oid FROM pg_catalog.pg_type WHERE typname ILIKE $1 /// and returns `None` for all others. async fn nullables_from_explain( &mut self, - stmt_id: u32, + stmt_id: Oid, params_len: usize, ) -> Result>, Error> { - let mut explain = format!("EXPLAIN (VERBOSE, FORMAT JSON) EXECUTE sqlx_s_{}", stmt_id); + let mut explain = format!( + "EXPLAIN (VERBOSE, FORMAT JSON) EXECUTE sqlx_s_{}", + stmt_id.0 + ); let mut comma = false; if params_len > 0 { diff --git a/sqlx-core/src/postgres/connection/establish.rs b/sqlx-core/src/postgres/connection/establish.rs index 74fd364447..cd163c5039 100644 --- a/sqlx-core/src/postgres/connection/establish.rs +++ b/sqlx-core/src/postgres/connection/establish.rs @@ -7,6 +7,7 @@ use crate::postgres::connection::{sasl, stream::PgStream, tls}; use crate::postgres::message::{ Authentication, BackendKeyData, MessageFormat, Password, ReadyForQuery, Startup, }; +use crate::postgres::types::Oid; use crate::postgres::{PgConnectOptions, PgConnection}; // https://www.postgresql.org/docs/current/protocol-flow.html#id-1.10.5.7.3 @@ -143,7 +144,7 @@ impl PgConnection { transaction_status, transaction_depth: 0, pending_ready_for_query_count: 0, - next_statement_id: 1, + next_statement_id: Oid(1), cache_statement: StatementCache::new(options.statement_cache_capacity), cache_type_oid: HashMap::new(), cache_type_info: HashMap::new(), diff --git a/sqlx-core/src/postgres/connection/executor.rs b/sqlx-core/src/postgres/connection/executor.rs index e3f5b1517e..80a7e9e121 100644 --- a/sqlx-core/src/postgres/connection/executor.rs +++ b/sqlx-core/src/postgres/connection/executor.rs @@ -8,6 +8,7 @@ use crate::postgres::message::{ }; use crate::postgres::statement::PgStatementMetadata; use crate::postgres::type_info::PgType; +use crate::postgres::types::Oid; use crate::postgres::{ statement::PgStatement, PgArguments, PgConnection, PgQueryResult, PgRow, PgTypeInfo, PgValueFormat, Postgres, @@ -24,9 +25,9 @@ async fn prepare( sql: &str, parameters: &[PgTypeInfo], metadata: Option>, -) -> Result<(u32, Arc), Error> { +) -> Result<(Oid, Arc), Error> { let id = conn.next_statement_id; - conn.next_statement_id = conn.next_statement_id.wrapping_add(1); + conn.next_statement_id.incr_one(); // build a list of type OIDs to send to the database in the PARSE command // we have not yet started the query sequence, so we are *safe* to cleanly make @@ -169,7 +170,7 @@ impl PgConnection { // optional metadata that was provided by the user, this means they are reusing // a statement object metadata: Option>, - ) -> Result<(u32, Arc), Error> { + ) -> Result<(Oid, Arc), Error> { if let Some(statement) = self.cache_statement.get_mut(sql) { return Ok((*statement).clone()); } diff --git a/sqlx-core/src/postgres/connection/mod.rs b/sqlx-core/src/postgres/connection/mod.rs index f9c28c9d52..965342bfc4 100644 --- a/sqlx-core/src/postgres/connection/mod.rs +++ b/sqlx-core/src/postgres/connection/mod.rs @@ -15,6 +15,7 @@ use crate::postgres::message::{ Close, Message, MessageFormat, Query, ReadyForQuery, Terminate, TransactionStatus, }; use crate::postgres::statement::PgStatementMetadata; +use crate::postgres::types::Oid; use crate::postgres::{PgConnectOptions, PgTypeInfo, Postgres}; use crate::transaction::Transaction; @@ -46,14 +47,14 @@ pub struct PgConnection { // sequence of statement IDs for use in preparing statements // in PostgreSQL, the statement is prepared to a user-supplied identifier - next_statement_id: u32, + next_statement_id: Oid, // cache statement by query string to the id and columns - cache_statement: StatementCache<(u32, Arc)>, + cache_statement: StatementCache<(Oid, Arc)>, // cache user-defined types by id <-> info - cache_type_info: HashMap, - cache_type_oid: HashMap, + cache_type_info: HashMap, + cache_type_oid: HashMap, // number of ReadyForQuery messages that we are currently expecting pub(crate) pending_ready_for_query_count: usize, diff --git a/sqlx-core/src/postgres/io/buf_mut.rs b/sqlx-core/src/postgres/io/buf_mut.rs index e7159f522e..d0b710293d 100644 --- a/sqlx-core/src/postgres/io/buf_mut.rs +++ b/sqlx-core/src/postgres/io/buf_mut.rs @@ -1,11 +1,13 @@ +use crate::postgres::types::Oid; + pub trait PgBufMutExt { fn put_length_prefixed(&mut self, f: F) where F: FnOnce(&mut Vec); - fn put_statement_name(&mut self, id: u32); + fn put_statement_name(&mut self, id: Oid); - fn put_portal_name(&mut self, id: Option); + fn put_portal_name(&mut self, id: Option); } impl PgBufMutExt for Vec { @@ -29,22 +31,22 @@ impl PgBufMutExt for Vec { // writes a statement name by ID #[inline] - fn put_statement_name(&mut self, id: u32) { + fn put_statement_name(&mut self, id: Oid) { // N.B. if you change this don't forget to update it in ../describe.rs self.extend(b"sqlx_s_"); - self.extend(itoa::Buffer::new().format(id).as_bytes()); + self.extend(itoa::Buffer::new().format(id.0).as_bytes()); self.push(0); } // writes a portal name by ID #[inline] - fn put_portal_name(&mut self, id: Option) { + fn put_portal_name(&mut self, id: Option) { if let Some(id) = id { self.extend(b"sqlx_p_"); - self.extend(itoa::Buffer::new().format(id).as_bytes()); + self.extend(itoa::Buffer::new().format(id.0).as_bytes()); } self.push(0); diff --git a/sqlx-core/src/postgres/message/bind.rs b/sqlx-core/src/postgres/message/bind.rs index 84d665e907..6c5dcf78c8 100644 --- a/sqlx-core/src/postgres/message/bind.rs +++ b/sqlx-core/src/postgres/message/bind.rs @@ -1,14 +1,15 @@ use crate::io::Encode; use crate::postgres::io::PgBufMutExt; +use crate::postgres::types::Oid; use crate::postgres::PgValueFormat; #[derive(Debug)] pub struct Bind<'a> { /// The ID of the destination portal (`None` selects the unnamed portal). - pub portal: Option, + pub portal: Option, /// The id of the source prepared statement. - pub statement: u32, + pub statement: Oid, /// The parameter format codes. Each must presently be zero (text) or one (binary). /// diff --git a/sqlx-core/src/postgres/message/close.rs b/sqlx-core/src/postgres/message/close.rs index 07e7795008..a073979063 100644 --- a/sqlx-core/src/postgres/message/close.rs +++ b/sqlx-core/src/postgres/message/close.rs @@ -1,5 +1,6 @@ use crate::io::Encode; use crate::postgres::io::PgBufMutExt; +use crate::postgres::types::Oid; const CLOSE_PORTAL: u8 = b'P'; const CLOSE_STATEMENT: u8 = b'S'; @@ -7,8 +8,8 @@ const CLOSE_STATEMENT: u8 = b'S'; #[derive(Debug)] #[allow(dead_code)] pub enum Close { - Statement(u32), - Portal(u32), + Statement(Oid), + Portal(Oid), } impl Encode<'_> for Close { diff --git a/sqlx-core/src/postgres/message/describe.rs b/sqlx-core/src/postgres/message/describe.rs index 08b4c408eb..ea919d5021 100644 --- a/sqlx-core/src/postgres/message/describe.rs +++ b/sqlx-core/src/postgres/message/describe.rs @@ -1,5 +1,6 @@ use crate::io::Encode; use crate::postgres::io::PgBufMutExt; +use crate::postgres::types::Oid; const DESCRIBE_PORTAL: u8 = b'P'; const DESCRIBE_STATEMENT: u8 = b'S'; @@ -10,10 +11,10 @@ const DESCRIBE_STATEMENT: u8 = b'S'; #[allow(dead_code)] pub enum Describe { UnnamedStatement, - Statement(u32), + Statement(Oid), UnnamedPortal, - Portal(u32), + Portal(Oid), } impl Encode<'_> for Describe { @@ -54,7 +55,7 @@ fn test_encode_describe_portal() { const EXPECTED: &[u8] = b"D\0\0\0\x0EPsqlx_p_5\0"; let mut buf = Vec::new(); - let m = Describe::Portal(5); + let m = Describe::Portal(Oid(5)); m.encode(&mut buf); @@ -78,7 +79,7 @@ fn test_encode_describe_statement() { const EXPECTED: &[u8] = b"D\0\0\0\x0ESsqlx_s_5\0"; let mut buf = Vec::new(); - let m = Describe::Statement(5); + let m = Describe::Statement(Oid(5)); m.encode(&mut buf); diff --git a/sqlx-core/src/postgres/message/execute.rs b/sqlx-core/src/postgres/message/execute.rs index 9465750368..dc70ced914 100644 --- a/sqlx-core/src/postgres/message/execute.rs +++ b/sqlx-core/src/postgres/message/execute.rs @@ -1,9 +1,10 @@ use crate::io::Encode; use crate::postgres::io::PgBufMutExt; +use crate::postgres::types::Oid; pub struct Execute { /// The id of the portal to execute (`None` selects the unnamed portal). - pub portal: Option, + pub portal: Option, /// Maximum number of rows to return, if portal contains a query /// that returns rows (ignored otherwise). Zero denotes “no limit”. @@ -28,7 +29,7 @@ fn test_encode_execute() { let mut buf = Vec::new(); let m = Execute { - portal: Some(5), + portal: Some(Oid(5)), limit: 2, }; diff --git a/sqlx-core/src/postgres/message/parameter_description.rs b/sqlx-core/src/postgres/message/parameter_description.rs index c0cbf2fc6d..e6640a73bd 100644 --- a/sqlx-core/src/postgres/message/parameter_description.rs +++ b/sqlx-core/src/postgres/message/parameter_description.rs @@ -3,10 +3,11 @@ use smallvec::SmallVec; use crate::error::Error; use crate::io::Decode; +use crate::postgres::types::Oid; #[derive(Debug)] pub struct ParameterDescription { - pub types: SmallVec<[u32; 6]>, + pub types: SmallVec<[Oid; 6]>, } impl Decode<'_> for ParameterDescription { @@ -15,7 +16,7 @@ impl Decode<'_> for ParameterDescription { let mut types = SmallVec::with_capacity(cnt as usize); for _ in 0..cnt { - types.push(buf.get_u32()); + types.push(Oid(buf.get_u32())); } Ok(Self { types }) @@ -29,8 +30,8 @@ fn test_decode_parameter_description() { let m = ParameterDescription::decode(DATA.into()).unwrap(); assert_eq!(m.types.len(), 2); - assert_eq!(m.types[0], 0x0000_0000); - assert_eq!(m.types[1], 0x0000_0500); + assert_eq!(m.types[0], Oid(0x0000_0000)); + assert_eq!(m.types[1], Oid(0x0000_0500)); } #[test] diff --git a/sqlx-core/src/postgres/message/parse.rs b/sqlx-core/src/postgres/message/parse.rs index 2e3576de91..5734ae4120 100644 --- a/sqlx-core/src/postgres/message/parse.rs +++ b/sqlx-core/src/postgres/message/parse.rs @@ -2,11 +2,12 @@ use std::i16; use crate::io::{BufMutExt, Encode}; use crate::postgres::io::PgBufMutExt; +use crate::postgres::types::Oid; #[derive(Debug)] pub struct Parse<'a> { /// The ID of the destination prepared statement. - pub statement: u32, + pub statement: Oid, /// The query string to be parsed. pub query: &'a str, @@ -14,7 +15,7 @@ pub struct Parse<'a> { /// The parameter data types specified (could be zero). Note that this is not an /// indication of the number of parameters that might appear in the query string, /// only the number that the frontend wants to pre-specify types for. - pub param_types: &'a [u32], + pub param_types: &'a [Oid], } impl Encode<'_> for Parse<'_> { @@ -32,7 +33,7 @@ impl Encode<'_> for Parse<'_> { buf.extend(&(self.param_types.len() as i16).to_be_bytes()); for &oid in self.param_types { - buf.extend(&oid.to_be_bytes()); + buf.extend(&oid.0.to_be_bytes()); } }) } @@ -44,9 +45,9 @@ fn test_encode_parse() { let mut buf = Vec::new(); let m = Parse { - statement: 1, + statement: Oid(1), query: "SELECT $1", - param_types: &[25], + param_types: &[Oid(25)], }; m.encode(&mut buf); diff --git a/sqlx-core/src/postgres/message/row_description.rs b/sqlx-core/src/postgres/message/row_description.rs index d6fa0ac22f..ff25ea9373 100644 --- a/sqlx-core/src/postgres/message/row_description.rs +++ b/sqlx-core/src/postgres/message/row_description.rs @@ -2,6 +2,7 @@ use bytes::{Buf, Bytes}; use crate::error::Error; use crate::io::{BufExt, Decode}; +use crate::postgres::types::Oid; #[derive(Debug)] pub struct RowDescription { @@ -22,7 +23,7 @@ pub struct Field { pub relation_attribute_no: Option, /// The object ID of the field's data type. - pub data_type_id: u32, + pub data_type_id: Oid, /// The data type size (see pg_type.typlen). Note that negative values denote /// variable-width types. @@ -45,7 +46,7 @@ impl Decode<'_> for RowDescription { let name = buf.get_str_nul()?.to_owned(); let relation_id = buf.get_i32(); let relation_attribute_no = buf.get_i16(); - let data_type_id = buf.get_u32(); + let data_type_id = Oid(buf.get_u32()); let data_type_size = buf.get_i16(); let type_modifier = buf.get_i32(); let format = buf.get_i16(); diff --git a/sqlx-core/src/postgres/type_info.rs b/sqlx-core/src/postgres/type_info.rs index 97d5efa0cc..dbc5a4b7db 100644 --- a/sqlx-core/src/postgres/type_info.rs +++ b/sqlx-core/src/postgres/type_info.rs @@ -6,6 +6,7 @@ use std::ops::Deref; use std::sync::Arc; use crate::ext::ustr::UStr; +use crate::postgres::types::Oid; use crate::type_info::TypeInfo; /// Type information for a PostgreSQL type. @@ -129,14 +130,14 @@ pub enum PgType { // NOTE: Do we want to bring back type declaration by ID? It's notoriously fragile but // someone may have a user for it - DeclareWithOid(u32), + DeclareWithOid(Oid), } #[derive(Debug, Clone)] #[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))] pub struct PgCustomType { #[cfg_attr(feature = "offline", serde(skip))] - pub(crate) oid: u32, + pub(crate) oid: Oid, pub(crate) name: UStr, pub(crate) kind: PgTypeKind, } @@ -155,7 +156,7 @@ pub enum PgTypeKind { impl PgTypeInfo { /// Returns the corresponding `PgTypeInfo` if the OID is a built-in type and recognized by SQLx. - pub(crate) fn try_from_oid(oid: u32) -> Option { + pub(crate) fn try_from_oid(oid: Oid) -> Option { PgType::try_from_oid(oid).map(Self) } @@ -221,7 +222,7 @@ impl PgTypeInfo { /// Note that the OID for a type is very dependent on the environment. If you only ever use /// one database or if this is an unhandled build-in type, you should be fine. Otherwise, /// you will be better served using [`with_name`](Self::with_name). - pub const fn with_oid(oid: u32) -> Self { + pub const fn with_oid(oid: Oid) -> Self { Self(PgType::DeclareWithOid(oid)) } } @@ -234,8 +235,8 @@ impl PgTypeInfo { impl PgType { /// Returns the corresponding `PgType` if the OID is a built-in type and recognized by SQLx. - pub(crate) fn try_from_oid(oid: u32) -> Option { - Some(match oid { + pub(crate) fn try_from_oid(oid: Oid) -> Option { + Some(match oid.0 { 16 => PgType::Bool, 17 => PgType::Bytea, 18 => PgType::Char, @@ -335,107 +336,107 @@ impl PgType { }) } - pub(crate) fn oid(&self) -> u32 { + pub(crate) fn oid(&self) -> Oid { match self.try_oid() { Some(oid) => oid, None => unreachable!("(bug) use of unresolved type declaration [oid]"), } } - pub(crate) fn try_oid(&self) -> Option { + pub(crate) fn try_oid(&self) -> Option { Some(match self { - PgType::Bool => 16, - PgType::Bytea => 17, - PgType::Char => 18, - PgType::Name => 19, - PgType::Int8 => 20, - PgType::Int2 => 21, - PgType::Int4 => 23, - PgType::Text => 25, - PgType::Oid => 26, - PgType::Json => 114, - PgType::JsonArray => 199, - PgType::Point => 600, - PgType::Lseg => 601, - PgType::Path => 602, - PgType::Box => 603, - PgType::Polygon => 604, - PgType::Line => 628, - PgType::LineArray => 629, - PgType::Cidr => 650, - PgType::CidrArray => 651, - PgType::Float4 => 700, - PgType::Float8 => 701, - PgType::Unknown => 705, - PgType::Circle => 718, - PgType::CircleArray => 719, - PgType::Macaddr8 => 774, - PgType::Macaddr8Array => 775, - PgType::Money => 790, - PgType::MoneyArray => 791, - PgType::Macaddr => 829, - PgType::Inet => 869, - PgType::BoolArray => 1000, - PgType::ByteaArray => 1001, - PgType::CharArray => 1002, - PgType::NameArray => 1003, - PgType::Int2Array => 1005, - PgType::Int4Array => 1007, - PgType::TextArray => 1009, - PgType::BpcharArray => 1014, - PgType::VarcharArray => 1015, - PgType::Int8Array => 1016, - PgType::PointArray => 1017, - PgType::LsegArray => 1018, - PgType::PathArray => 1019, - PgType::BoxArray => 1020, - PgType::Float4Array => 1021, - PgType::Float8Array => 1022, - PgType::PolygonArray => 1027, - PgType::OidArray => 1028, - PgType::MacaddrArray => 1040, - PgType::InetArray => 1041, - PgType::Bpchar => 1042, - PgType::Varchar => 1043, - PgType::Date => 1082, - PgType::Time => 1083, - PgType::Timestamp => 1114, - PgType::TimestampArray => 1115, - PgType::DateArray => 1182, - PgType::TimeArray => 1183, - PgType::Timestamptz => 1184, - PgType::TimestamptzArray => 1185, - PgType::Interval => 1186, - PgType::IntervalArray => 1187, - PgType::NumericArray => 1231, - PgType::Timetz => 1266, - PgType::TimetzArray => 1270, - PgType::Bit => 1560, - PgType::BitArray => 1561, - PgType::Varbit => 1562, - PgType::VarbitArray => 1563, - PgType::Numeric => 1700, - PgType::Void => 2278, - PgType::Record => 2249, - PgType::RecordArray => 2287, - PgType::Uuid => 2950, - PgType::UuidArray => 2951, - PgType::Jsonb => 3802, - PgType::JsonbArray => 3807, - PgType::Int4Range => 3904, - PgType::Int4RangeArray => 3905, - PgType::NumRange => 3906, - PgType::NumRangeArray => 3907, - PgType::TsRange => 3908, - PgType::TsRangeArray => 3909, - PgType::TstzRange => 3910, - PgType::TstzRangeArray => 3911, - PgType::DateRange => 3912, - PgType::DateRangeArray => 3913, - PgType::Int8Range => 3926, - PgType::Int8RangeArray => 3927, - PgType::Jsonpath => 4072, - PgType::JsonpathArray => 4073, + PgType::Bool => Oid(16), + PgType::Bytea => Oid(17), + PgType::Char => Oid(18), + PgType::Name => Oid(19), + PgType::Int8 => Oid(20), + PgType::Int2 => Oid(21), + PgType::Int4 => Oid(23), + PgType::Text => Oid(25), + PgType::Oid => Oid(26), + PgType::Json => Oid(114), + PgType::JsonArray => Oid(199), + PgType::Point => Oid(600), + PgType::Lseg => Oid(601), + PgType::Path => Oid(602), + PgType::Box => Oid(603), + PgType::Polygon => Oid(604), + PgType::Line => Oid(628), + PgType::LineArray => Oid(629), + PgType::Cidr => Oid(650), + PgType::CidrArray => Oid(651), + PgType::Float4 => Oid(700), + PgType::Float8 => Oid(701), + PgType::Unknown => Oid(705), + PgType::Circle => Oid(718), + PgType::CircleArray => Oid(719), + PgType::Macaddr8 => Oid(774), + PgType::Macaddr8Array => Oid(775), + PgType::Money => Oid(790), + PgType::MoneyArray => Oid(791), + PgType::Macaddr => Oid(829), + PgType::Inet => Oid(869), + PgType::BoolArray => Oid(1000), + PgType::ByteaArray => Oid(1001), + PgType::CharArray => Oid(1002), + PgType::NameArray => Oid(1003), + PgType::Int2Array => Oid(1005), + PgType::Int4Array => Oid(1007), + PgType::TextArray => Oid(1009), + PgType::BpcharArray => Oid(1014), + PgType::VarcharArray => Oid(1015), + PgType::Int8Array => Oid(1016), + PgType::PointArray => Oid(1017), + PgType::LsegArray => Oid(1018), + PgType::PathArray => Oid(1019), + PgType::BoxArray => Oid(1020), + PgType::Float4Array => Oid(1021), + PgType::Float8Array => Oid(1022), + PgType::PolygonArray => Oid(1027), + PgType::OidArray => Oid(1028), + PgType::MacaddrArray => Oid(1040), + PgType::InetArray => Oid(1041), + PgType::Bpchar => Oid(1042), + PgType::Varchar => Oid(1043), + PgType::Date => Oid(1082), + PgType::Time => Oid(1083), + PgType::Timestamp => Oid(1114), + PgType::TimestampArray => Oid(1115), + PgType::DateArray => Oid(1182), + PgType::TimeArray => Oid(1183), + PgType::Timestamptz => Oid(1184), + PgType::TimestamptzArray => Oid(1185), + PgType::Interval => Oid(1186), + PgType::IntervalArray => Oid(1187), + PgType::NumericArray => Oid(1231), + PgType::Timetz => Oid(1266), + PgType::TimetzArray => Oid(1270), + PgType::Bit => Oid(1560), + PgType::BitArray => Oid(1561), + PgType::Varbit => Oid(1562), + PgType::VarbitArray => Oid(1563), + PgType::Numeric => Oid(1700), + PgType::Void => Oid(2278), + PgType::Record => Oid(2249), + PgType::RecordArray => Oid(2287), + PgType::Uuid => Oid(2950), + PgType::UuidArray => Oid(2951), + PgType::Jsonb => Oid(3802), + PgType::JsonbArray => Oid(3807), + PgType::Int4Range => Oid(3904), + PgType::Int4RangeArray => Oid(3905), + PgType::NumRange => Oid(3906), + PgType::NumRangeArray => Oid(3907), + PgType::TsRange => Oid(3908), + PgType::TsRangeArray => Oid(3909), + PgType::TstzRange => Oid(3910), + PgType::TstzRangeArray => Oid(3911), + PgType::DateRange => Oid(3912), + PgType::DateRangeArray => Oid(3913), + PgType::Int8Range => Oid(3926), + PgType::Int8RangeArray => Oid(3927), + PgType::Jsonpath => Oid(4072), + PgType::JsonpathArray => Oid(4073), PgType::Custom(ty) => ty.oid, PgType::DeclareWithOid(oid) => *oid, @@ -744,7 +745,7 @@ impl PgType { PgType::Custom(ty) => &ty.kind, PgType::DeclareWithOid(oid) => { - unreachable!("(bug) use of unresolved type declaration [oid={}]", oid); + unreachable!("(bug) use of unresolved type declaration [oid={}]", oid.0); } PgType::DeclareWithName(name) => { unreachable!("(bug) use of unresolved type declaration [name={}]", name); @@ -863,7 +864,7 @@ impl PgType { PgTypeKind::Range(_) => None, }, PgType::DeclareWithOid(oid) => { - unreachable!("(bug) use of unresolved type declaration [oid={}]", oid); + unreachable!("(bug) use of unresolved type declaration [oid={}]", oid.0); } PgType::DeclareWithName(name) => { unreachable!("(bug) use of unresolved type declaration [name={}]", name); diff --git a/sqlx-core/src/postgres/types/array.rs b/sqlx-core/src/postgres/types/array.rs index 68a0ac385e..b7514e8418 100644 --- a/sqlx-core/src/postgres/types/array.rs +++ b/sqlx-core/src/postgres/types/array.rs @@ -5,6 +5,7 @@ use crate::decode::Decode; use crate::encode::{Encode, IsNull}; use crate::error::BoxDynError; use crate::postgres::type_info::PgType; +use crate::postgres::types::Oid; use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres}; use crate::types::Type; @@ -84,7 +85,7 @@ where PgType::DeclareWithName(name) => buf.patch_type_by_name(&name), ty => { - buf.extend(&ty.oid().to_be_bytes()); + buf.extend(&ty.oid().0.to_be_bytes()); } } @@ -130,7 +131,7 @@ where let _flags = buf.get_i32(); // the OID of the element - let element_type_oid = buf.get_u32(); + let element_type_oid = Oid(buf.get_u32()); let element_type_info: PgTypeInfo = PgTypeInfo::try_from_oid(element_type_oid) .or_else(|| value.type_info.try_array_element().map(Cow::into_owned)) .unwrap_or_else(|| PgTypeInfo(PgType::DeclareWithOid(element_type_oid))); diff --git a/sqlx-core/src/postgres/types/int.rs b/sqlx-core/src/postgres/types/int.rs index 63b7d3dfee..e721bab143 100644 --- a/sqlx-core/src/postgres/types/int.rs +++ b/sqlx-core/src/postgres/types/int.rs @@ -64,35 +64,6 @@ impl Decode<'_, Postgres> for i16 { } } -impl Type for u32 { - fn type_info() -> PgTypeInfo { - PgTypeInfo::OID - } -} - -impl PgHasArrayType for u32 { - fn array_type_info() -> PgTypeInfo { - PgTypeInfo::OID_ARRAY - } -} - -impl Encode<'_, Postgres> for u32 { - fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull { - buf.extend(&self.to_be_bytes()); - - IsNull::No - } -} - -impl Decode<'_, Postgres> for u32 { - fn decode(value: PgValueRef<'_>) -> Result { - Ok(match value.format() { - PgValueFormat::Binary => BigEndian::read_u32(value.as_bytes()?), - PgValueFormat::Text => value.as_str()?.parse()?, - }) - } -} - impl Type for i32 { fn type_info() -> PgTypeInfo { PgTypeInfo::INT4 diff --git a/sqlx-core/src/postgres/types/mod.rs b/sqlx-core/src/postgres/types/mod.rs index 68a20cc31a..359fe86e14 100644 --- a/sqlx-core/src/postgres/types/mod.rs +++ b/sqlx-core/src/postgres/types/mod.rs @@ -171,6 +171,7 @@ mod interval; mod lquery; mod ltree; mod money; +mod oid; mod range; mod record; mod str; @@ -220,6 +221,7 @@ pub use ltree::PgLTree; pub use ltree::PgLTreeLabel; pub use ltree::PgLTreeParseError; pub use money::PgMoney; +pub use oid::Oid; pub use range::PgRange; #[cfg(any(feature = "chrono", feature = "time"))] diff --git a/sqlx-core/src/postgres/types/oid.rs b/sqlx-core/src/postgres/types/oid.rs new file mode 100644 index 0000000000..74d8e3b68b --- /dev/null +++ b/sqlx-core/src/postgres/types/oid.rs @@ -0,0 +1,76 @@ +use byteorder::{BigEndian, ByteOrder}; +#[cfg(feature = "serde")] +use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; + +use crate::decode::Decode; +use crate::encode::{Encode, IsNull}; +use crate::error::BoxDynError; +use crate::postgres::{ + PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres, +}; +use crate::types::Type; + +/// The PostgreSQL [`OID`] type stores an object identifier, +/// used internally by PostgreSQL as primary keys for various system tables. +/// +/// [`OID`]: https://www.postgresql.org/docs/current/datatype-oid.html +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)] +pub struct Oid( + /// The raw unsigned integer value sent over the wire + pub u32, +); + +impl Oid { + pub(crate) fn incr_one(&mut self) { + self.0 = self.0.wrapping_add(1); + } +} + +impl Type for Oid { + fn type_info() -> PgTypeInfo { + PgTypeInfo::OID + } +} + +impl PgHasArrayType for Oid { + fn array_type_info() -> PgTypeInfo { + PgTypeInfo::OID_ARRAY + } +} + +impl Encode<'_, Postgres> for Oid { + fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull { + buf.extend(&self.0.to_be_bytes()); + + IsNull::No + } +} + +impl Decode<'_, Postgres> for Oid { + fn decode(value: PgValueRef<'_>) -> Result { + Ok(Self(match value.format() { + PgValueFormat::Binary => BigEndian::read_u32(value.as_bytes()?), + PgValueFormat::Text => value.as_str()?.parse()?, + })) + } +} + +#[cfg(feature = "serde")] +impl Serialize for Oid { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.0.serialize(serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Oid { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + u32::deserialize(deserializer).map(Self) + } +} diff --git a/sqlx-core/src/postgres/types/record.rs b/sqlx-core/src/postgres/types/record.rs index 1d9acab9b3..92ee1fa471 100644 --- a/sqlx-core/src/postgres/types/record.rs +++ b/sqlx-core/src/postgres/types/record.rs @@ -4,6 +4,7 @@ use crate::decode::Decode; use crate::encode::Encode; use crate::error::{mismatched_types, BoxDynError}; use crate::postgres::type_info::{PgType, PgTypeKind}; +use crate::postgres::types::Oid; use crate::postgres::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres}; use crate::type_info::TypeInfo; use crate::types::Type; @@ -46,7 +47,7 @@ impl<'a> PgRecordEncoder<'a> { self.buf.patch_type_by_name(&name); } else { // write type id - self.buf.extend(&ty.0.oid().to_be_bytes()); + self.buf.extend(&ty.0.oid().0.to_be_bytes()); } self.buf.encode(value); @@ -101,7 +102,7 @@ impl<'r> PgRecordDecoder<'r> { match self.fmt { PgValueFormat::Binary => { - let element_type_oid = self.buf.get_u32(); + let element_type_oid = Oid(self.buf.get_u32()); let element_type_opt = match self.typ.0.kind() { PgTypeKind::Simple if self.typ.0 == PgType::Record => { PgTypeInfo::try_from_oid(element_type_oid) @@ -192,7 +193,7 @@ impl<'r> PgRecordDecoder<'r> { T::decode(PgValueRef { // NOTE: We pass `0` as the type ID because we don't have a reasonable value // we could use. - type_info: PgTypeInfo::with_oid(0), + type_info: PgTypeInfo::with_oid(Oid(0)), format: self.fmt, value: buf, row: None, diff --git a/sqlx-macros/src/database/postgres.rs b/sqlx-macros/src/database/postgres.rs index 2ffde06a9b..83a84334f4 100644 --- a/sqlx-macros/src/database/postgres.rs +++ b/sqlx-macros/src/database/postgres.rs @@ -8,12 +8,13 @@ impl_database_ext! { i8, i16, i32, - u32, i64, f32, f64, Vec | &[u8], + sqlx::postgres::types::Oid, + sqlx::postgres::types::PgInterval, sqlx::postgres::types::PgMoney, @@ -81,10 +82,10 @@ impl_database_ext! { Vec | &[i8], Vec | &[i16], Vec | &[i32], - Vec | &[u32], Vec | &[i64], Vec | &[f32], Vec | &[f64], + Vec | &[sqlx::postgres::types::Oid], Vec | &[sqlx::postgres::types::PgMoney], #[cfg(feature = "uuid")] diff --git a/tests/postgres/postgres.rs b/tests/postgres/postgres.rs index b516c2229d..f4078ac1bc 100644 --- a/tests/postgres/postgres.rs +++ b/tests/postgres/postgres.rs @@ -1,9 +1,9 @@ use futures::{StreamExt, TryStreamExt}; +use sqlx::postgres::types::Oid; use sqlx::postgres::{ - PgAdvisoryLock, PgConnectOptions, PgConnection, PgDatabaseError, PgErrorPosition, PgListener, - PgSeverity, + PgAdvisoryLock, PgConnectOptions, PgConnection, PgConnectionInfo, PgDatabaseError, + PgErrorPosition, PgListener, PgPoolOptions, PgRow, PgSeverity, Postgres, }; -use sqlx::postgres::{PgConnectionInfo, PgPoolOptions, PgRow, Postgres}; use sqlx::{Column, Connection, Executor, Row, Statement, TypeInfo}; use sqlx_test::{new, pool, setup_if_needed}; use std::env; @@ -662,14 +662,14 @@ async fn it_caches_statements() -> anyhow::Result<()> { for i in 0..2 { let row = sqlx::query("SELECT $1 AS val") - .bind(i) + .bind(Oid(i)) .persistent(true) .fetch_one(&mut conn) .await?; - let val: u32 = row.get("val"); + let val: Oid = row.get("val"); - assert_eq!(i, val); + assert_eq!(Oid(i), val); } assert_eq!(1, conn.cached_statements_size()); @@ -678,14 +678,14 @@ async fn it_caches_statements() -> anyhow::Result<()> { for i in 0..2 { let row = sqlx::query("SELECT $1 AS val") - .bind(i) + .bind(Oid(i)) .persistent(false) .fetch_one(&mut conn) .await?; - let val: u32 = row.get("val"); + let val: Oid = row.get("val"); - assert_eq!(i, val); + assert_eq!(Oid(i), val); } assert_eq!(0, conn.cached_statements_size()); diff --git a/tests/postgres/types.rs b/tests/postgres/types.rs index 21ebbe6120..d8e695e96d 100644 --- a/tests/postgres/types.rs +++ b/tests/postgres/types.rs @@ -2,7 +2,7 @@ extern crate time_ as time; use std::ops::Bound; -use sqlx::postgres::types::{PgInterval, PgMoney, PgRange}; +use sqlx::postgres::types::{Oid, PgInterval, PgMoney, PgRange}; use sqlx::postgres::Postgres; use sqlx_test::{test_decode_type, test_prepared_type, test_type}; use std::str::FromStr; @@ -70,7 +70,7 @@ test_type!(i8( "120::\"char\"" == 120_i8, )); -test_type!(u32(Postgres, "325235::oid" == 325235_u32,)); +test_type!(Oid(Postgres, "325235::oid" == Oid(325235),)); test_type!(i16( Postgres,