diff --git a/Cargo.lock b/Cargo.lock index ab65628b..70ca8f73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,6 +261,16 @@ dependencies = [ "instant", ] +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -372,6 +382,17 @@ dependencies = [ "libc", ] +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "instant" version = "0.1.9" @@ -448,6 +469,12 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "memchr" version = "2.3.4" @@ -596,6 +623,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + [[package]] name = "pin-project" version = "1.0.2" @@ -737,6 +770,7 @@ dependencies = [ "futures-util", "tokio 0.2.24", "tokio 1.0.1", + "url", ] [[package]] @@ -759,6 +793,21 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "tinyvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + [[package]] name = "tokio" version = "0.2.24" @@ -790,12 +839,42 @@ dependencies = [ "pin-project-lite 0.2.0", ] +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "url" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + [[package]] name = "vec-arena" version = "1.0.0" diff --git a/sqlx-core/Cargo.toml b/sqlx-core/Cargo.toml index 8bad07d2..ca83fbc6 100644 --- a/sqlx-core/Cargo.toml +++ b/sqlx-core/Cargo.toml @@ -39,3 +39,4 @@ _async-std = { version = "1.8.0", optional = true, package = "async-std" } futures-util = { version = "0.3.8", optional = true } _tokio = { version = "1.0.1", optional = true, package = "tokio", features = ["net"] } tokio_02 = { version = "0.2.24", optional = true, package = "tokio", features = ["net"] } +url = "2.2.0" diff --git a/sqlx-core/src/blocking.rs b/sqlx-core/src/blocking.rs index 8e91d9cf..e8459653 100644 --- a/sqlx-core/src/blocking.rs +++ b/sqlx-core/src/blocking.rs @@ -3,4 +3,9 @@ pub(crate) mod runtime; +mod connection; +mod options; + +pub use connection::Connection; +pub use options::ConnectOptions; pub use runtime::Runtime; diff --git a/sqlx-core/src/blocking/connection.rs b/sqlx-core/src/blocking/connection.rs new file mode 100644 index 00000000..15b48938 --- /dev/null +++ b/sqlx-core/src/blocking/connection.rs @@ -0,0 +1,39 @@ +use super::{ConnectOptions, Runtime}; + +/// A unique connection (session) with a specific database. +/// +/// For detailed information, refer to the asynchronous version of +/// this: [`Connection`][crate::Connection]. +/// +pub trait Connection: 'static + Send +where + Rt: Runtime, +{ + type Options: ConnectOptions; + + /// Establish a new database connection. + /// + /// For detailed information, refer to the asynchronous version of + /// this: [`connect()`][crate::Connection::connect]. + /// + fn connect(url: &str) -> crate::Result + where + Self: Sized, + { + url.parse::()?.connect() + } + + /// Explicitly close this database connection. + /// + /// For detailed information, refer to the asynchronous version of + /// this: [`close()`][crate::Connection::close]. + /// + fn close(self) -> crate::Result<()>; + + /// Checks if a connection to the database is still valid. + /// + /// For detailed information, refer to the asynchronous version of + /// this: [`ping()`][crate::Connection::ping]. + /// + fn ping(&mut self) -> crate::Result<()>; +} diff --git a/sqlx-core/src/blocking/options.rs b/sqlx-core/src/blocking/options.rs new file mode 100644 index 00000000..b1749808 --- /dev/null +++ b/sqlx-core/src/blocking/options.rs @@ -0,0 +1,26 @@ +use std::fmt::Debug; +use std::str::FromStr; + +use super::{Connection, Runtime}; + +/// Options which can be used to configure how a SQL connection is opened. +/// +/// For detailed information, refer to the asynchronous version of +/// this: [`ConnectOptions`][crate::ConnectOptions]. +/// +pub trait ConnectOptions: + 'static + Send + Sync + Default + Debug + Clone + FromStr +where + Rt: Runtime, +{ + type Connection: Connection + ?Sized; + + /// Establish a connection to the database. + /// + /// For detailed information, refer to the asynchronous version of + /// this: [`connect()`][crate::ConnectOptions::connect]. + /// + fn connect(&self) -> crate::Result + where + Self::Connection: Sized; +} diff --git a/sqlx-core/src/connection.rs b/sqlx-core/src/connection.rs new file mode 100644 index 00000000..0eaa863d --- /dev/null +++ b/sqlx-core/src/connection.rs @@ -0,0 +1,70 @@ +use futures_util::future::BoxFuture; + +use crate::{ConnectOptions, Runtime}; + +/// A unique connection (session) with a specific database. +/// +/// With a client/server model, this is equivalent to a network connection +/// to the server. +/// +/// SQL statements will be executed and results returned within the context +/// of this single SQL connection. +/// +pub trait Connection: 'static + Send +where + Rt: Runtime, +{ + type Options: ConnectOptions; + + /// Establish a new database connection. + /// + /// A value of [`Options`](#associatedtype.Options) is parsed from the provided connection string. This parsing + /// is database-specific. + /// + /// ```rust,ignore + /// use sqlx::postgres::PgConnection; + /// use sqlx::ConnectOptions; + /// + /// let mut conn = PgConnection::connect( + /// "postgres://postgres:password@localhost/database", + /// ).await?; + /// ``` + /// + /// You may alternatively build the connection options imperatively. + /// + /// ```rust,ignore + /// use sqlx::mysql::{MySqlConnection, MySqlConnectOptions}; + /// use sqlx::ConnectOptions; + /// + /// let mut conn: MySqlConnection = MySqlConnectOptions::builder() + /// .host("localhost") + /// .username("root") + /// .password("password") + /// .connect().await?; + /// ``` + /// + fn connect(url: &str) -> BoxFuture<'_, crate::Result> + where + Self: Sized, + { + let options = url.parse::(); + Box::pin(async move { options?.connect().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 the database backend + /// will be faster at cleaning up resources. + /// + fn close(self) -> BoxFuture<'static, crate::Result<()>>; + + /// Checks if a connection to the database is still valid. + /// + /// The method of operation greatly depends on the database driver. In MySQL, there is an + /// explicit [`COM_PING`](https://dev.mysql.com/doc/internals/en/com-ping.html) command. In + /// PostgreSQL, `ping` will issue a query consisting of a comment `/* SQLx ping */` which, + /// in effect, does nothing apart from getting a response from the server. + /// + fn ping(&mut self) -> BoxFuture<'_, crate::Result<()>>; +} diff --git a/sqlx-core/src/error.rs b/sqlx-core/src/error.rs new file mode 100644 index 00000000..b23c91b4 --- /dev/null +++ b/sqlx-core/src/error.rs @@ -0,0 +1,26 @@ +use std::error::Error as StdError; +use std::fmt::{self, Display, Formatter}; + +pub type Result = std::result::Result; + +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + InvalidConnectionUrl(url::ParseError), +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidConnectionUrl(source) => write!(f, "invalid connection url: {}", source), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Self::InvalidConnectionUrl(source) => Some(source), + } + } +} diff --git a/sqlx-core/src/lib.rs b/sqlx-core/src/lib.rs index 68681d26..631ef2df 100644 --- a/sqlx-core/src/lib.rs +++ b/sqlx-core/src/lib.rs @@ -27,9 +27,19 @@ extern crate _async_std as async_std; #[cfg(feature = "tokio")] extern crate _tokio as tokio; +mod error; + +pub use error::{Error, Result}; + #[cfg(feature = "async")] mod runtime; +#[cfg(feature = "async")] +mod connection; + +#[cfg(feature = "async")] +mod options; + #[cfg(feature = "blocking")] #[cfg_attr(doc_cfg, doc(cfg(feature = "blocking")))] pub mod blocking; @@ -38,7 +48,7 @@ pub mod blocking; pub use blocking::runtime::Blocking; #[cfg(feature = "async")] -pub use runtime::Runtime; +pub use {connection::Connection, options::ConnectOptions, runtime::Runtime}; #[cfg(all(feature = "async", feature = "async-std"))] pub use runtime::async_std::AsyncStd; diff --git a/sqlx-core/src/options.rs b/sqlx-core/src/options.rs new file mode 100644 index 00000000..58469ef7 --- /dev/null +++ b/sqlx-core/src/options.rs @@ -0,0 +1,20 @@ +use std::fmt::Debug; +use std::str::FromStr; + +use futures_util::future::BoxFuture; + +use crate::{Connection, Runtime}; + +/// Options which can be used to configure how a SQL connection is opened. +pub trait ConnectOptions: + 'static + Send + Sync + Default + Debug + Clone + FromStr +where + Rt: Runtime, +{ + type Connection: Connection + ?Sized; + + /// Establish a connection to the database. + fn connect(&self) -> BoxFuture<'_, crate::Result> + where + Self::Connection: Sized; +}