mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-10-02 15:25:32 +00:00
fix some remaining usage of PoolOptions
This commit is contained in:
parent
05cf469945
commit
54c857b448
124
README.md
124
README.md
@ -55,20 +55,20 @@
|
|||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
SQLx is an async, pure Rust<sub>†</sub> SQL crate featuring compile-time checked queries without a DSL.
|
SQLx is an async, pure Rust<sub>†</sub> SQL crate featuring compile-time checked queries without a DSL.
|
||||||
|
|
||||||
* **Truly Asynchronous**. Built from the ground-up using async/await for maximum concurrency.
|
* **Truly Asynchronous**. Built from the ground-up using async/await for maximum concurrency.
|
||||||
|
|
||||||
* **Type-safe SQL** (if you want it) without DSLs. Use the `query!()` macro to check your SQL and bind parameters at
|
* **Type-safe SQL** (if you want it) without DSLs. Use the `query!()` macro to check your SQL and bind parameters at
|
||||||
compile time. (You can still use dynamic SQL queries if you like.)
|
compile time. (You can still use dynamic SQL queries if you like.)
|
||||||
|
|
||||||
* **Database Agnostic**. Support for [PostgreSQL], [MySQL], and [SQLite].
|
* **Database Agnostic**. Support for [PostgreSQL], [MySQL], and [SQLite].
|
||||||
|
|
||||||
* **Pure Rust**. The Postgres and MySQL/MariaDB drivers are written in pure Rust using **zero** unsafe<sub>††</sub> code.
|
* **Pure Rust**. The Postgres and MySQL/MariaDB drivers are written in pure Rust using **zero** unsafe<sub>††</sub> code.
|
||||||
|
|
||||||
* **Runtime Agnostic**. Works on [async-std](https://crates.io/crates/async-std) or [tokio](https://crates.io/crates/tokio) with the `runtime-async-std` or `runtime-tokio` cargo feature flag.
|
* **Runtime Agnostic**. Works on [async-std](https://crates.io/crates/async-std) or [tokio](https://crates.io/crates/tokio) with the `runtime-async-std` or `runtime-tokio` cargo feature flag.
|
||||||
|
|
||||||
<sub><sup>† The SQLite driver uses the libsqlite3 C library as SQLite is an embedded database (the only way
|
<sub><sup>† The SQLite driver uses the libsqlite3 C library as SQLite is an embedded database (the only way
|
||||||
we could be pure Rust for SQLite is by porting _all_ of SQLite to Rust).</sup></sub>
|
we could be pure Rust for SQLite is by porting _all_ of SQLite to Rust).</sup></sub>
|
||||||
|
|
||||||
<sub><sup>†† SQLx uses `#![forbid(unsafe_code)]` unless the `sqlite` feature is enabled. As the SQLite driver interacts
|
<sub><sup>†† SQLx uses `#![forbid(unsafe_code)]` unless the `sqlite` feature is enabled. As the SQLite driver interacts
|
||||||
@ -83,19 +83,19 @@ with C, those interactions are `unsafe`.</sup></sub>
|
|||||||
* Cross-platform. Being native Rust, SQLx will compile anywhere Rust is supported.
|
* Cross-platform. Being native Rust, SQLx will compile anywhere Rust is supported.
|
||||||
|
|
||||||
* Built-in connection pooling with `sqlx::Pool`.
|
* Built-in connection pooling with `sqlx::Pool`.
|
||||||
|
|
||||||
* Row streaming. Data is read asynchronously from the database and decoded on-demand.
|
* Row streaming. Data is read asynchronously from the database and decoded on-demand.
|
||||||
|
|
||||||
* Automatic statement preparation and caching. When using the high-level query API (`sqlx::query`), statements are
|
* Automatic statement preparation and caching. When using the high-level query API (`sqlx::query`), statements are
|
||||||
prepared and cached per-connection.
|
prepared and cached per-connection.
|
||||||
|
|
||||||
* Simple (unprepared) query execution including fetching results into the same `Row` types used by
|
* Simple (unprepared) query execution including fetching results into the same `Row` types used by
|
||||||
the high-level API. Supports batch execution and returning results from all statements.
|
the high-level API. Supports batch execution and returning results from all statements.
|
||||||
|
|
||||||
* Transport Layer Security (TLS) where supported ([MySQL] and [PostgreSQL]).
|
* Transport Layer Security (TLS) where supported ([MySQL] and [PostgreSQL]).
|
||||||
|
|
||||||
* Asynchronous notifications using `LISTEN` and `NOTIFY` for [PostgreSQL].
|
* Asynchronous notifications using `LISTEN` and `NOTIFY` for [PostgreSQL].
|
||||||
|
|
||||||
* Nested transactions with support for save points.
|
* Nested transactions with support for save points.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
@ -124,29 +124,29 @@ sqlx = { version = "0.3", default-features = false, features = [ "runtime-tokio"
|
|||||||
#### Cargo Feature Flags
|
#### Cargo Feature Flags
|
||||||
|
|
||||||
* `runtime-async-std` (on by default): Use the `async-std` runtime.
|
* `runtime-async-std` (on by default): Use the `async-std` runtime.
|
||||||
|
|
||||||
* `runtime-tokio`: Use the `tokio` runtime. Mutually exclusive with the `runtime-async-std` feature.
|
* `runtime-tokio`: Use the `tokio` runtime. Mutually exclusive with the `runtime-async-std` feature.
|
||||||
|
|
||||||
* `postgres`: Add support for the Postgres database server.
|
* `postgres`: Add support for the Postgres database server.
|
||||||
|
|
||||||
* `mysql`: Add support for the MySQL (and MariaDB) database server.
|
* `mysql`: Add support for the MySQL (and MariaDB) database server.
|
||||||
|
|
||||||
* `sqlite`: Add support for the self-contained [SQLite](https://sqlite.org/) database engine.
|
* `sqlite`: Add support for the self-contained [SQLite](https://sqlite.org/) database engine.
|
||||||
|
|
||||||
* `uuid`: Add support for UUID (in Postgres).
|
* `uuid`: Add support for UUID (in Postgres).
|
||||||
|
|
||||||
* `chrono`: Add support for date and time types from `chrono`.
|
* `chrono`: Add support for date and time types from `chrono`.
|
||||||
|
|
||||||
* `time`: Add support for date and time types from `time` crate (alternative to `chrono`, prefered by `query!` macro, if both enabled)
|
* `time`: Add support for date and time types from `time` crate (alternative to `chrono`, prefered by `query!` macro, if both enabled)
|
||||||
|
|
||||||
* `bigdecimal`: Add support for `NUMERIC` using the `bigdecimal` crate.
|
* `bigdecimal`: Add support for `NUMERIC` using the `bigdecimal` crate.
|
||||||
|
|
||||||
* `ipnetwork`: Add support for `INET` and `CIDR` (in postgres) using the `ipnetwork` crate.
|
* `ipnetwork`: Add support for `INET` and `CIDR` (in postgres) using the `ipnetwork` crate.
|
||||||
|
|
||||||
* `json`: Add support for `JSON` and `JSONB` (in postgres) using the `serde_json` crate.
|
* `json`: Add support for `JSON` and `JSONB` (in postgres) using the `serde_json` crate.
|
||||||
|
|
||||||
* `tls`: Add support for TLS connections.
|
* `tls`: Add support for TLS connections.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Quickstart
|
### Quickstart
|
||||||
@ -161,15 +161,15 @@ use sqlx::postgres::PgPool;
|
|||||||
#[async_std::main] // or #[tokio::main]
|
#[async_std::main] // or #[tokio::main]
|
||||||
async fn main() -> Result<(), sqlx::Error> {
|
async fn main() -> Result<(), sqlx::Error> {
|
||||||
// Create a connection pool
|
// Create a connection pool
|
||||||
let pool = PgPoolOptions::new(&env::var("DATABASE_URL")?)?
|
let pool = PgPoolOptions::new()
|
||||||
.max_connections(5)
|
.max_connections(5)
|
||||||
.connect().await?;
|
.connect(&env::var("DATABASE_URL")?).await?;
|
||||||
|
|
||||||
// Make a simple query to return the given parameter
|
// Make a simple query to return the given parameter
|
||||||
let row: (i64,) = sqlx::query_as("SELECT $1")
|
let row: (i64,) = sqlx::query_as("SELECT $1")
|
||||||
.bind(150_i64)
|
.bind(150_i64)
|
||||||
.fetch_one(&pool).await?;
|
.fetch_one(&pool).await?;
|
||||||
|
|
||||||
assert_eq!(row.0, 150);
|
assert_eq!(row.0, 150);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -178,7 +178,7 @@ async fn main() -> Result<(), sqlx::Error> {
|
|||||||
|
|
||||||
### Connecting
|
### Connecting
|
||||||
|
|
||||||
A single connection can be established using any of the database connection types and calling `connect()`.
|
A single connection can be established using any of the database connection types and calling `connect()`.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use sqlx::Connect;
|
use sqlx::Connect;
|
||||||
@ -186,21 +186,21 @@ use sqlx::Connect;
|
|||||||
let conn = SqliteConnection::connect("sqlite::memory:").await?;
|
let conn = SqliteConnection::connect("sqlite::memory:").await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
Generally, you will want to instead create a connection pool (`sqlx::Pool`) in order for your application to
|
Generally, you will want to instead create a connection pool (`sqlx::Pool`) in order for your application to
|
||||||
regulate how many server-side connections it's using.
|
regulate how many server-side connections it's using.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let pool = MySqlPool::new("mysql://user:pass@host/database").await?;
|
let pool = MySqlPool::new("mysql://user:pass@host/database").await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Querying
|
### Querying
|
||||||
|
|
||||||
In SQL, queries can be separated into prepared (parameterized) or unprepared (simple). Prepared queries have their
|
In SQL, queries can be separated into prepared (parameterized) or unprepared (simple). Prepared queries have their
|
||||||
query plan _cached_, use a binary mode of communication (lower bandwidth and faster decoding), and utilize parameters
|
query plan _cached_, use a binary mode of communication (lower bandwidth and faster decoding), and utilize parameters
|
||||||
to avoid SQL injection. Unprepared queries are simple and intended only for use case where a prepared statement
|
to avoid SQL injection. Unprepared queries are simple and intended only for use case where a prepared statement
|
||||||
will not work, such as various database commands (e.g., `PRAGMA` or `SET` or `BEGIN`).
|
will not work, such as various database commands (e.g., `PRAGMA` or `SET` or `BEGIN`).
|
||||||
|
|
||||||
SQLx supports all operations with both types of queries. In SQLx, a `&str` is treated as an unprepared query
|
SQLx supports all operations with both types of queries. In SQLx, a `&str` is treated as an unprepared query
|
||||||
and a `Query` or `QueryAs` struct is treated as a prepared query.
|
and a `Query` or `QueryAs` struct is treated as a prepared query.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@ -217,11 +217,11 @@ sqlx::query("DELETE FROM table").execute(&mut conn).await?;
|
|||||||
sqlx::query("DELETE FROM table").execute(&pool).await?;
|
sqlx::query("DELETE FROM table").execute(&pool).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
The `execute` query finalizer returns the number of affected rows, if any, and drops all received results.
|
The `execute` query finalizer returns the number of affected rows, if any, and drops all received results.
|
||||||
In addition, there are `fetch`, `fetch_one`, `fetch_optional`, `fetch_all`, and `fetch_scalar` to receive results.
|
In addition, there are `fetch`, `fetch_one`, `fetch_optional`, `fetch_all`, and `fetch_scalar` to receive results.
|
||||||
|
|
||||||
The `Query` type returned from `sqlx::query` will return `Row<'conn>` from the database. Column values can be accessed
|
The `Query` type returned from `sqlx::query` will return `Row<'conn>` from the database. Column values can be accessed
|
||||||
by ordinal or by name with `row.get()`. As the `Row` retains an immutable borrow on the connection, only one
|
by ordinal or by name with `row.get()`. As the `Row` retains an immutable borrow on the connection, only one
|
||||||
`Row` may exist at a time.
|
`Row` may exist at a time.
|
||||||
|
|
||||||
The `fetch` query finalizer returns a stream-like type that iterates through the rows in the result sets.
|
The `fetch` query finalizer returns a stream-like type that iterates through the rows in the result sets.
|
||||||
@ -232,7 +232,7 @@ let mut cursor = sqlx::query("SELECT * FROM users WHERE email = ?")
|
|||||||
.fetch(&mut conn);
|
.fetch(&mut conn);
|
||||||
|
|
||||||
while let Some(row) = cursor.next().await? {
|
while let Some(row) = cursor.next().await? {
|
||||||
// map the row into a user-defined domain type
|
// map the row into a user-defined domain type
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -256,22 +256,22 @@ let mut stream = sqlx::query_as::<_, User>("SELECT * FROM users WHERE email = ?
|
|||||||
.fetch(&mut conn);
|
.fetch(&mut conn);
|
||||||
```
|
```
|
||||||
|
|
||||||
Instead of a stream of results, we can use `fetch_one` or `fetch_optional` to request one required or optional result
|
Instead of a stream of results, we can use `fetch_one` or `fetch_optional` to request one required or optional result
|
||||||
from the database.
|
from the database.
|
||||||
|
|
||||||
### Compile-time verification
|
### Compile-time verification
|
||||||
|
|
||||||
We can use the macro, `sqlx::query!` to achieve compile-time syntactic and semantic verification of the SQL, with
|
We can use the macro, `sqlx::query!` to achieve compile-time syntactic and semantic verification of the SQL, with
|
||||||
an output to an anonymous record type where each SQL column is a Rust field (using raw identifiers where needed).
|
an output to an anonymous record type where each SQL column is a Rust field (using raw identifiers where needed).
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let countries = sqlx::query!(
|
let countries = sqlx::query!(
|
||||||
"
|
"
|
||||||
SELECT country, COUNT(*) as count
|
SELECT country, COUNT(*) as count
|
||||||
FROM users
|
FROM users
|
||||||
GROUP BY country
|
GROUP BY country
|
||||||
WHERE organization = ?
|
WHERE organization = ?
|
||||||
",
|
",
|
||||||
organization
|
organization
|
||||||
)
|
)
|
||||||
.fetch_all(&pool) // -> Vec<{ country: String, count: i64 }>
|
.fetch_all(&pool) // -> Vec<{ country: String, count: i64 }>
|
||||||
@ -282,28 +282,28 @@ WHERE organization = ?
|
|||||||
```
|
```
|
||||||
|
|
||||||
Differences from `query()`:
|
Differences from `query()`:
|
||||||
|
|
||||||
* The input (or bind) parameters must be given all at once (and they are compile-time validated to be
|
* The input (or bind) parameters must be given all at once (and they are compile-time validated to be
|
||||||
the right number and the right type).
|
the right number and the right type).
|
||||||
|
|
||||||
* The output type is an anonymous record. In the above example the type would be similar to:
|
* The output type is an anonymous record. In the above example the type would be similar to:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
{ country: String, count: i64 }
|
{ country: String, count: i64 }
|
||||||
```
|
```
|
||||||
|
|
||||||
* The `DATABASE_URL` environment variable must be set at build time to a database which it can prepare
|
* The `DATABASE_URL` environment variable must be set at build time to a database which it can prepare
|
||||||
queries against; the database does not have to contain any data but must be the same
|
queries against; the database does not have to contain any data but must be the same
|
||||||
kind (MySQL, Postgres, etc.) and have the same schema as the database you will be connecting to at runtime.
|
kind (MySQL, Postgres, etc.) and have the same schema as the database you will be connecting to at runtime.
|
||||||
|
|
||||||
For convenience, you can use a .env file to set DATABASE_URL so that you don't have to pass it every time:
|
For convenience, you can use a .env file to set DATABASE_URL so that you don't have to pass it every time:
|
||||||
|
|
||||||
```
|
```
|
||||||
DATABASE_URL=mysql://localhost/my_database
|
DATABASE_URL=mysql://localhost/my_database
|
||||||
```
|
```
|
||||||
|
|
||||||
The biggest downside to `query!()` is that the output type cannot be named (due to Rust not
|
The biggest downside to `query!()` is that the output type cannot be named (due to Rust not
|
||||||
officially supporting anonymous records). To address that, there is a `query_as!()` macro that is identical
|
officially supporting anonymous records). To address that, there is a `query_as!()` macro that is identical
|
||||||
except that you can name the output type.
|
except that you can name the output type.
|
||||||
|
|
||||||
|
|
||||||
@ -313,11 +313,11 @@ struct Country { country: String, count: i64 }
|
|||||||
|
|
||||||
let countries = sqlx::query_as!(Country,
|
let countries = sqlx::query_as!(Country,
|
||||||
"
|
"
|
||||||
SELECT country, COUNT(*) as count
|
SELECT country, COUNT(*) as count
|
||||||
FROM users
|
FROM users
|
||||||
GROUP BY country
|
GROUP BY country
|
||||||
WHERE organization = ?
|
WHERE organization = ?
|
||||||
",
|
",
|
||||||
organization
|
organization
|
||||||
)
|
)
|
||||||
.fetch_all() // -> Vec<Country>
|
.fetch_all() // -> Vec<Country>
|
||||||
@ -329,9 +329,9 @@ WHERE organization = ?
|
|||||||
|
|
||||||
## Safety
|
## Safety
|
||||||
|
|
||||||
This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% Safe Rust.
|
This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% Safe Rust.
|
||||||
|
|
||||||
If the `sqlite` feature is enabled, this is downgraded to `#![deny(unsafe_code)]` with `#![allow(unsafe_code)]` on the
|
If the `sqlite` feature is enabled, this is downgraded to `#![deny(unsafe_code)]` with `#![allow(unsafe_code)]` on the
|
||||||
`sqlx::sqlite` module. There are several places where we interact with the C SQLite API. We try to document each call for the invariants we're assuming. We absolutely welcome auditing of, and feedback on, our unsafe code usage.
|
`sqlx::sqlite` module. There are several places where we interact with the C SQLite API. We try to document each call for the invariants we're assuming. We absolutely welcome auditing of, and feedback on, our unsafe code usage.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
@ -24,18 +24,18 @@ fn bench_pgpool_acquire(c: &mut Criterion) {
|
|||||||
|
|
||||||
fn do_bench_acquire(b: &mut Bencher, concurrent: u32, fair: bool) {
|
fn do_bench_acquire(b: &mut Bencher, concurrent: u32, fair: bool) {
|
||||||
let pool = sqlx_rt::block_on(
|
let pool = sqlx_rt::block_on(
|
||||||
PgPoolOptions::new(
|
PgPoolOptions::new()
|
||||||
&dotenv::var("DATABASE_URL").expect("DATABASE_URL must be set to run benchmarks"),
|
// we don't want timeouts because we want to see how the pool degrades
|
||||||
)
|
.connect_timeout(Duration::from_secs(3600))
|
||||||
// we don't want timeouts because we want to see how the pool degrades
|
// force the pool to start full
|
||||||
.connect_timeout(Duration::from_secs(3600))
|
.min_connections(50)
|
||||||
// force the pool to start full
|
.max_connections(50)
|
||||||
.min_connections(50)
|
// we're not benchmarking `ping()`
|
||||||
.max_connections(50)
|
.test_before_acquire(false)
|
||||||
// we're not benchmarking `ping()`
|
.__fair(fair)
|
||||||
.test_before_acquire(false)
|
.connect(
|
||||||
.__fair(fair)
|
&dotenv::var("DATABASE_URL").expect("DATABASE_URL must be set to run benchmarks"),
|
||||||
.connect(),
|
),
|
||||||
)
|
)
|
||||||
.expect("failed to open PgPool");
|
.expect("failed to open PgPool");
|
||||||
|
|
||||||
|
@ -76,11 +76,11 @@ CREATE TEMPORARY TABLE users (id INTEGER PRIMARY KEY);
|
|||||||
|
|
||||||
#[sqlx_macros::test]
|
#[sqlx_macros::test]
|
||||||
async fn it_executes_with_pool() -> anyhow::Result<()> {
|
async fn it_executes_with_pool() -> anyhow::Result<()> {
|
||||||
let pool: MySqlPool = MySqlPoolOptions::new(&dotenv::var("DATABASE_URL")?)?
|
let pool: MySqlPool = MySqlPoolOptions::new()
|
||||||
.min_connections(2)
|
.min_connections(2)
|
||||||
.max_connections(2)
|
.max_connections(2)
|
||||||
.test_before_acquire(false)
|
.test_before_acquire(false)
|
||||||
.connect()
|
.connect(&dotenv::var("DATABASE_URL")?)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let rows = pool.fetch_all("SELECT 1; SELECT 2").await?;
|
let rows = pool.fetch_all("SELECT 1; SELECT 2").await?;
|
||||||
|
@ -370,11 +370,11 @@ async fn pool_smoke_test() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
eprintln!("starting pool");
|
eprintln!("starting pool");
|
||||||
|
|
||||||
let pool = PgPoolOptions::new(&dotenv::var("DATABASE_URL")?)?
|
let pool = PgPoolOptions::new()
|
||||||
.connect_timeout(Duration::from_secs(30))
|
.connect_timeout(Duration::from_secs(30))
|
||||||
.min_connections(5)
|
.min_connections(5)
|
||||||
.max_connections(10)
|
.max_connections(10)
|
||||||
.connect()
|
.connect(&dotenv::var("DATABASE_URL")?)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// spin up more tasks than connections available, and ensure we don't deadlock
|
// spin up more tasks than connections available, and ensure we don't deadlock
|
||||||
|
@ -127,11 +127,11 @@ async fn it_fetches_in_loop() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
#[sqlx_macros::test]
|
#[sqlx_macros::test]
|
||||||
async fn it_executes_with_pool() -> anyhow::Result<()> {
|
async fn it_executes_with_pool() -> anyhow::Result<()> {
|
||||||
let pool: SqlitePool = SqlitePoolOptions::new(&dotenv::var("DATABASE_URL")?)?
|
let pool: SqlitePool = SqlitePoolOptions::new()?
|
||||||
.min_connections(2)
|
.min_connections(2)
|
||||||
.max_connections(2)
|
.max_connections(2)
|
||||||
.test_before_acquire(false)
|
.test_before_acquire(false)
|
||||||
.connect()
|
.connect(&dotenv::var("DATABASE_URL")?)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let rows = pool.fetch_all("SELECT 1; SElECT 2").await?;
|
let rows = pool.fetch_all("SELECT 1; SElECT 2").await?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user