feat(sqlx): add sqlx-postgres to sqlx

This commit is contained in:
Ryan Leckey 2021-03-20 00:16:35 -07:00
parent 1eb1cd3ea9
commit dbf13844d9
No known key found for this signature in database
GPG Key ID: F8AA68C235AB08C9
5 changed files with 398 additions and 9 deletions

View File

@ -33,13 +33,13 @@ mysql = ["sqlx-mysql"]
mysql-async = ["async", "mysql", "sqlx-mysql/async"]
mysql-blocking = ["blocking", "mysql", "sqlx-mysql/blocking"]
## Postgres
#postgres = ["sqlx-postgres"]
#postgres-async = ["async", "postgres", "sqlx-postgres/async"]
#postgres-blocking = ["blocking", "postgres", "sqlx-postgres/blocking"]
# Postgres
postgres = ["sqlx-postgres"]
postgres-async = ["async", "postgres", "sqlx-postgres/async"]
postgres-blocking = ["blocking", "postgres", "sqlx-postgres/blocking"]
[dependencies]
sqlx-core = { version = "0.6.0-pre", path = "../sqlx-core" }
sqlx-mysql = { version = "0.6.0-pre", path = "../sqlx-mysql", optional = true }
#sqlx-postgres = { version = "0.6.0-pre", path = "../sqlx-postgres", optional = true }
sqlx-postgres = { version = "0.6.0-pre", path = "../sqlx-postgres", optional = true }
futures-util = { version = "0.3", optional = true, features = ["io"] }

View File

@ -53,8 +53,8 @@ mod runtime;
#[cfg(feature = "mysql")]
pub mod mysql;
// #[cfg(feature = "postgres")]
// pub mod postgres;
#[cfg(feature = "postgres")]
pub mod postgres;
#[cfg(feature = "blocking")]
pub use blocking::Blocking;
@ -70,6 +70,6 @@ pub use sqlx_core::AsyncStd;
#[cfg(feature = "tokio")]
pub use sqlx_core::Tokio;
pub use sqlx_core::{
Acquire, Arguments, Close, Connect, ConnectOptions, Connection, Database, Decode, Encode,
Error, Execute, Executor, FromRow, Result, Row, Runtime, Type, Describe,
Acquire, Arguments, Close, Connect, ConnectOptions, Connection, Database, Decode, Describe,
Encode, Error, Execute, Executor, FromRow, Result, Row, Runtime, Type,
};

20
sqlx/src/postgres.rs Normal file
View File

@ -0,0 +1,20 @@
//! [PostgreSQL] database driver.
//!
mod connection;
mod options;
// #[cfg(feature = "blocking")]
// mod blocking;
//
// these types are wrapped instead of re-exported
// this is to provide runtime-specialized inherent methods by taking advantage
// of through crate-local negative reasoning
pub use connection::PgConnection;
pub use options::PgConnectOptions;
//
// re-export the remaining types from the driver
pub use sqlx_postgres::{
types, PgColumn, PgQueryResult, PgRawValue, PgRawValueFormat, PgRow, PgTypeId, Postgres,
};

View File

@ -0,0 +1,207 @@
use std::fmt::{self, Debug, Formatter};
use std::ops::{Deref, DerefMut};
#[cfg(feature = "async")]
use futures_util::future::{BoxFuture, FutureExt};
use sqlx_core::{Execute, Executor};
use super::{PgConnectOptions, PgQueryResult, PgRow, Postgres};
#[cfg(feature = "blocking")]
use crate::blocking;
use crate::{Arguments, Close, Connect, Connection, DefaultRuntime, Describe, Runtime};
#[cfg(feature = "async")]
use crate::{Async, Result};
/// A single connection (also known as a session) to a MySQL database server.
#[allow(clippy::module_name_repetitions)]
pub struct PgConnection<Rt: Runtime = DefaultRuntime>(pub(super) sqlx_postgres::PgConnection<Rt>);
#[cfg(feature = "async")]
impl<Rt: Async> PgConnection<Rt> {
/// Open a new database connection.
///
/// A value of [`PgConnectOptions`] is parsed from the provided
/// connection `url`.
///
/// ```text
/// mysql://[[user[:password]@]host][/database][?properties]
/// ```
///
/// Implemented with [`Connect::connect`][crate::Connect::connect].
pub async fn connect(url: &str) -> Result<Self> {
sqlx_postgres::PgConnection::<Rt>::connect(url).await.map(Self)
}
/// Open a new database connection with the configured options.
///
/// Implemented with [`Connect::connect_with`][crate::Connect::connect_with].
pub async fn connect_with(options: &PgConnectOptions<Rt>) -> Result<Self> {
sqlx_postgres::PgConnection::<Rt>::connect_with(&**options).await.map(Self)
}
/// Checks if a connection to the database is still valid.
///
/// Implemented with [`Connection::ping`][crate::Connection::ping].
pub async fn ping(&mut self) -> Result<()> {
self.0.ping().await
}
// pub async fn execute<'q, 'a, E>(&mut self, query: E) -> Result<PgQueryResult>
// where
// E: Execute<'q, 'a, Postgres>,
// {
// self.0.execute(query).await
// }
//
// pub async fn fetch_all<'q, 'a, E>(&mut self, query: E) -> Result<Vec<PgRow>>
// where
// E: Execute<'q, 'a, Postgres>,
// {
// self.0.fetch_all(query).await
// }
//
// pub async fn fetch_one<'q, 'a, E>(&mut self, query: E) -> Result<PgRow>
// where
// E: Execute<'q, 'a, Postgres>,
// {
// self.0.fetch_one(query).await
// }
//
// pub async fn fetch_optional<'q, 'a, E>(&mut self, query: E) -> Result<Option<PgRow>>
// where
// E: Execute<'q, 'a, Postgres>,
// {
// self.0.fetch_optional(query).await
// }
/// Explicitly close this database connection.
///
/// This method is **not required** for safe and consistent operation. However, it is
/// recommended to call it instead of letting a connection `drop` as MySQL
/// will be faster at cleaning up resources.
///
/// Implemented with [`Close::close`][crate::Close::close].
pub async fn close(self) -> Result<()> {
self.0.close().await
}
}
impl<Rt: Runtime> Debug for PgConnection<Rt> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}
impl<Rt: Runtime> Close<Rt> for PgConnection<Rt> {
#[cfg(feature = "async")]
#[inline]
fn close(self) -> BoxFuture<'static, Result<()>>
where
Rt: Async,
{
self.close().boxed()
}
}
impl<Rt: Runtime> Connect<Rt> for PgConnection<Rt> {
type Options = PgConnectOptions<Rt>;
#[cfg(feature = "async")]
#[inline]
fn connect_with(options: &Self::Options) -> BoxFuture<'_, Result<Self>>
where
Rt: Async,
{
Self::connect_with(options).boxed()
}
}
impl<Rt: Runtime> Connection<Rt> for PgConnection<Rt> {
type Database = Postgres;
#[cfg(feature = "async")]
#[inline]
fn ping(&mut self) -> BoxFuture<'_, Result<()>>
where
Rt: Async,
{
self.0.ping()
}
#[cfg(feature = "async")]
#[inline]
fn describe<'x, 'e, 'q>(
&'e mut self,
query: &'q str,
) -> BoxFuture<'x, Result<Describe<Postgres>>>
where
Rt: Async,
'e: 'x,
'q: 'x,
{
self.0.describe(query)
}
}
// impl<Rt: Runtime> Executor<Rt> for PgConnection<Rt> {
// type Database = Postgres;
//
// #[cfg(feature = "async")]
// fn execute<'x, 'e, 'q, 'a, E>(&'e mut self, query: E) -> BoxFuture<'x, Result<PgQueryResult>>
// where
// Rt: Async,
// E: 'x + Execute<'q, 'a, Postgres>,
// 'e: 'x,
// 'q: 'x,
// 'a: 'x,
// {
// self.0.execute(query)
// }
//
// #[cfg(feature = "async")]
// fn fetch_all<'x, 'e, 'q, 'a, E>(&'e mut self, query: E) -> BoxFuture<'x, Result<Vec<PgRow>>>
// where
// Rt: Async,
// E: 'x + Execute<'q, 'a, Postgres>,
// 'e: 'x,
// 'q: 'x,
// 'a: 'x,
// {
// self.0.fetch_all(query)
// }
//
// #[cfg(feature = "async")]
// fn fetch_optional<'x, 'e, 'q, 'a, E>(
// &'e mut self,
// query: E,
// ) -> BoxFuture<'x, Result<Option<PgRow>>>
// where
// Rt: Async,
// E: 'x + Execute<'q, 'a, Postgres>,
// 'e: 'x,
// 'q: 'x,
// 'a: 'x,
// {
// self.0.fetch_optional(query)
// }
// }
impl<Rt: Runtime> From<sqlx_postgres::PgConnection<Rt>> for PgConnection<Rt> {
fn from(connection: sqlx_postgres::PgConnection<Rt>) -> Self {
Self(connection)
}
}
impl<Rt: Runtime> Deref for PgConnection<Rt> {
type Target = sqlx_postgres::PgConnection<Rt>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<Rt: Runtime> DerefMut for PgConnection<Rt> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View File

@ -0,0 +1,162 @@
use std::fmt::{self, Debug, Formatter};
use std::marker::PhantomData;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use std::str::FromStr;
#[cfg(feature = "async")]
use futures_util::future::{BoxFuture, FutureExt};
use super::PgConnection;
#[cfg(feature = "async")]
use crate::Async;
use crate::{ConnectOptions, DefaultRuntime, Error, Result, Runtime};
/// Options which can be used to configure how a MySQL connection is opened.
#[allow(clippy::module_name_repetitions)]
pub struct PgConnectOptions<Rt: Runtime = DefaultRuntime> {
runtime: PhantomData<Rt>,
options: sqlx_postgres::PgConnectOptions,
}
impl<Rt: Runtime> PgConnectOptions<Rt> {
/// Creates a default set of connection options.
///
/// Implemented with [`Default`](#impl-Default).
#[inline]
pub fn new() -> Self {
Self::default()
}
/// Parses connection options from a connection URL.
///
/// ```text
/// mysql://[[user[:password]@]host][/database][?properties]
/// ```
///
/// Implemented with [`FromStr`](#impl-FromStr).
///
#[inline]
pub fn parse(url: &str) -> Result<Self> {
Ok(url.parse::<sqlx_postgres::PgConnectOptions>()?.into())
}
}
#[cfg(feature = "async")]
impl<Rt: Async> PgConnectOptions<Rt> {
/// Open a new database connection with the configured connection options.
///
/// Implemented with [`ConnectOptions::connect`].
#[inline]
pub async fn connect(&self) -> Result<PgConnection<Rt>> {
<Self as ConnectOptions>::connect::<PgConnection<Rt>, Rt>(self).await
}
}
// explicitly forwards builder methods
// in order to return Self as sqlx::mysql::PgConnectOptions instead of
// sqlx_postgres::PgConnectOptions
impl<Rt: Runtime> PgConnectOptions<Rt> {
/// Sets the hostname of the database server.
///
/// If the hostname begins with a slash (`/`), it is interpreted as the absolute path
/// to a Unix domain socket file instead of a hostname of a server.
///
/// Defaults to `localhost`.
///
pub fn host(&mut self, host: impl AsRef<str>) -> &mut Self {
self.options.host(host);
self
}
/// Sets the path of the Unix domain socket to connect to.
///
/// Overrides [`host()`](#method.host) and [`port()`](#method.port).
///
pub fn socket(&mut self, socket: impl AsRef<Path>) -> &mut Self {
self.options.socket(socket);
self
}
/// Sets the TCP port number of the database server.
///
/// Defaults to `3306`.
///
pub fn port(&mut self, port: u16) -> &mut Self {
self.options.port(port);
self
}
/// Sets the username to be used for authentication.
// FIXME: Specify what happens when you do NOT set this
pub fn username(&mut self, username: impl AsRef<str>) -> &mut Self {
self.options.username(username);
self
}
/// Sets the password to be used for authentication.
pub fn password(&mut self, password: impl AsRef<str>) -> &mut Self {
self.options.password(password);
self
}
/// Sets the default database for the connection.
pub fn database(&mut self, database: impl AsRef<str>) -> &mut Self {
self.options.database(database);
self
}
}
// allow trivial conversion from [sqlx_postgres::PgConnectOptions] to
// our runtime-wrapped [sqlx::mysql::PgConnectOptions]
impl<Rt: Runtime> From<sqlx_postgres::PgConnectOptions> for PgConnectOptions<Rt> {
#[inline]
fn from(options: sqlx_postgres::PgConnectOptions) -> Self {
Self { runtime: PhantomData, options }
}
}
// default implement [ConnectOptions]
// ensures that the required traits for [PgConnectOptions<Rt>] are implemented
impl<Rt: Runtime> ConnectOptions for PgConnectOptions<Rt> {}
// forward Debug to [sqlx_postgres::PgConnectOptions]
impl<Rt: Runtime> Debug for PgConnectOptions<Rt> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.options)
}
}
// forward Default to [sqlx_postgres::PgConnectOptions]
impl<Rt: Runtime> Default for PgConnectOptions<Rt> {
fn default() -> Self {
sqlx_postgres::PgConnectOptions::default().into()
}
}
// forward Clone to [sqlx_postgres::PgConnectOptions]
impl<Rt: Runtime> Clone for PgConnectOptions<Rt> {
fn clone(&self) -> Self {
Self { runtime: PhantomData, options: self.options.clone() }
}
}
// forward FromStr to [sqlx_postgres::PgConnectOptions]
impl<Rt: Runtime> FromStr for PgConnectOptions<Rt> {
type Err = Error;
fn from_str(url: &str) -> Result<Self> {
Self::parse(url)
}
}
// allow dereferencing into [sqlx_postgres::PgConnectOptions]
// note that we do not allow mutable dereferencing as those methods have the wrong return type
impl<Rt: Runtime> Deref for PgConnectOptions<Rt> {
type Target = sqlx_postgres::PgConnectOptions;
fn deref(&self) -> &Self::Target {
&self.options
}
}