Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: launchbadge/sqlx
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: Aandreba/sqlx
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.

Commits on Feb 19, 2023

  1. implemented neg for pgmoney

    Alex Andreba committed Feb 19, 2023
    Copy the full SHA
    ffa0d72 View commit details

Commits on Mar 31, 2023

  1. PgHasArrayType for transparent types fix. (#2086)

    Problem: PgHasArrayType was checking the application's postgres feature
    Solution: only check the library's postgres feature
    
    Co-authored-by: Daniel Tashjian <daniel@ecomedes.com>
    2 people authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    c162d24 View commit details
  2. Update libsqlite3-sys to 0.25.1 (#2094)

    penberg authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    09c0ad0 View commit details
  3. Fix sqlite update return and order by type inference (#1960)

    * add failing test cases for update/delete return into
    
    * fix regression in null tracking by improving tracking of cursor empty/full state
    
    * add failing test case for order by column types
    
    * Add support for SorterOpen,SorterInsert,SorterData
    
    * add failing test case for unions
    
    * fix range copy/move implementation
    
    * fix wrong copy/move range
    
    * remove calls to dbg!
    tyrelr authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    b2753dd View commit details
  4. Fix compile time verification performance regression for sqlite (#1946)

    * add instruction, register, and cursor state memorization
    
    * fix: fixed formating
    liningpan authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    c4f57a5 View commit details
  5. Fix sqlite compilation (#2098)

    cycraig authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    a1b80e0 View commit details
  6. feat: Add set_connect_options method to Pool (#2088)

    * feat: Add set_connect_options method to Pool
    
    This allows external updates of the ConnectionOptions used when a new
    connection needs to be opened for the pool.  The primary use case
    is to support dynamically updated (read: rotated) credentials used
    by systems like AWS RDS.
    
    * Use Arc wrapper for ConnectOptions to reduce lock contention
    
    * sqlite fix
    
    * Use direct assignment instead of mem::swap
    
    Co-authored-by: Austin Bonander <austin.bonander@gmail.com>
    
    Co-authored-by: Austin Bonander <austin.bonander@gmail.com>
    2 people authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    a0e1f4a View commit details
  7. Sqlite EXPLAIN type inference improvements (#1984)

    * AggValue and ROW_NUMBER()
    
    * Some more functions
    
    * cargo fmt
    rongcuid authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    e021663 View commit details
  8. avoid improper path merger if cursor backing register, or cursor empt…

    …iness is different (#2120)
    tyrelr authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    243e15e View commit details
  9. Break drivers out into separate crates, clean up some technical debt (#…

    …2039)
    
    * WIP rt refactors
    
    * refactor: break drivers out into separate crates
    
    also cleans up significant technical debt
    abonander authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    5c19db5 View commit details
  10. Sqlite describe fixes (#2253)

    * add failing test for nested orderby
    
    * log query paths which were abandoned due to invalid state or looping.  Allow instructions to be executed a small number of times to fix nested order by query
    
    * add failing testcase using nested orderby
    
    * fix handling of sequence/offset and rewind
    
    * fix handling when sqlite nests records inside of records
    
    * add test of temporary table handling
    
    * WIP add test failure for temp table access
    
    * fix support for temp tables
    
    * add tests for sqlite datetime functions
    
    * add basic date and time function support
    
    * handle gosub opcode correctly
    
    * add group by test
    
    * fix group by handling
    
    * add additional passing group by test
    
    * add test case for simple limit query
    
    * fix IfPos & If touching wrong branches state, fix IfPos using wrong branch criteria
    
    * add test for large offsets
    
    * add short-circuit for possible query offset loops
    
    * add groupby query that is predicted incorrectly
    
    * fix handling of integer cast failures
    
    * add tests for single-row aggregate results
    
    * fix handling of null-based branching
    
    * add test for coercion of text by sum
    
    * fix calculation of sum value coercion
    
    * add failing test for recursive with query
    
    * add logic for delete operation to fix queries grouping by columns from a recursive query
    tyrelr authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    f99f7c2 View commit details
  11. Expose PoolOptions for reading (#2113)

    This allows reading back PoolOptions that have already been set.
    FSMaxB authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    00c1d2a View commit details
  12. Copy the full SHA
    dbec3da View commit details
  13. feat: better database errors (#2109)

    * feat(core): create error kind enum
    
    * feat(core): add error kind for postgres
    
    * feat(core): add error kind for sqlite
    
    * feat(core): add error kind for mysql
    
    * test(postgres): add error tests
    
    * test(sqlite): add error tests
    
    * test(mysql): add error tests
    
    * fix(tests): fix tests rebasing
    
    * refac(errors): add `ErrorKind::Other` variant
    saiintbrisson authored and Aandreba committed Mar 31, 2023

    Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    91a5110 View commit details
  14. fix: use owned Builder pattern for ConnectOptions (#2132)

    * CHANGELOG: mention that users should upgrade CLI
    
    * fix(sqlx-core): use owned builder pattern for ConnectOptions
    
    ---------
    
    Co-authored-by: Austin Bonander <austin.bonander@gmail.com>
    2 people authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    881063f View commit details
  15. Initial work to switch to tracing (#2185)

    * Add tracing dep
    
    * Switch over basic events
    
    * Switch over dynamically enabled events
    
    * Fix missing SocketAddr formatting
    
    * More format fixing
    
    * refactor: Apply tracing changes to new crate structure
    CosmicHorrorDev authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    0958a24 View commit details
  16. fix: ensures recover from fail with PgCopyIn (#2179)

    * CHANGELOG: mention that users should upgrade CLI
    
    * fix: ensures recover from fail with PgCopyIn
    
    ---------
    
    Co-authored-by: Austin Bonander <austin.bonander@gmail.com>
    2 people authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    7f18015 View commit details
  17. Start testing on Postgres 15 and drop Postgres 10 (#2193)

    * CHANGELOG: mention that users should upgrade CLI
    
    * Drop postgres 10 start testing postgres 15
    
    ---------
    
    Co-authored-by: Austin Bonander <austin.bonander@gmail.com>
    2 people authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    237a082 View commit details
  18. Added regexp support in sqlite (#2189)

    * CHANGELOG: mention that users should upgrade CLI
    
    * Added regexp support in sqlite
    
    * Added a with_regexp function to sqliteconnectoptions
    
    * Fixed tests
    
    * Undo CHANGELOG.md change
    
    ---------
    
    Co-authored-by: Austin Bonander <austin.bonander@gmail.com>
    Co-authored-by: Victor Koenders <victor.koenders@qrtech.se>
    3 people authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    503a36e View commit details
  19. Set whoami default-features to false (#2319)

    * Fixed typo. (#2156)
    
    * Set whoami default-features to false
    
    Otherwise, whoami pulls in web-sys, wasm-bindgen and a BUNCH of
    additional dependencies. This is really unnecessary, and if
    someone has an actual use case where they are attempting to connect
    to postgres from a browser, well ... they've probably already been
    pwned by now. If it is deemed necessary, then add an additional
    activation feature for that specific slew of deps.
    
    ---------
    
    Co-authored-by: Chris Foster <cdbfoster@gmail.com>
    2 people authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    626eca2 View commit details
  20. Allow using complex types in try_from when deriving FromRow (#2115)

    * Use `syn::Type` instead of `syn::Ident` to parse the value of `#[sqlx(try_from = "...")]`
    
    * Fix broken test after rebase
    95ulisse authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    ba91478 View commit details
  21. Postgres OID resolution query does not take into account current `sea…

    …rch_path` (#2133)
    
    * Fix oid resolution query
    
    * Address review comments
    95ulisse authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    bfce825 View commit details
  22. [SQLite] Add option to execute PRAGMA optimize; on close of a conne…

    …ction (#2116)
    
    * CHANGELOG: mention that users should upgrade CLI
    
    * [SQLite] Add option to execute `PRAGMA optimize;` on close of a connection
    
    * Update sqlx-sqlite/src/options/mod.rs
    
    * Update sqlx-sqlite/src/options/mod.rs
    
    * Update sqlx-sqlite/src/options/mod.rs
    
    ---------
    
    Co-authored-by: Austin Bonander <austin.bonander@gmail.com>
    2 people authored and Aandreba committed Mar 31, 2023

    Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    f064c3c View commit details
  23. Add client SSL authentication using key-file for Postgres, MySQL and …

    …MariaDB (#1850)
    
    * use native-tls API
    
    * Add client cert and key to MySQL connector
    
    * Add client ssl tests for PostgreSQL
    
    * Add client ssl tests for MariaDB and MySQL
    
    * Adapt GA tests
    
    * Fix RUSTFLAGS to run all tests
    
    * Remove containers to free the DB port before running SSL auth tests
    
    * Fix CI bad naming
    
    * Use docker-compose down to remove also the network
    
    * Fix main rebase
    
    * Stop trying to stop service using docker-compose, simply use docker cmd
    
    * Fix RUSTFLAGS for Postgres
    
    * Name the Docker images for MariaDB and MySQL so we can stop them using their name
    
    * Add the exception for mysql 5.7 not supporting compatible TLS version with RusTLS
    
    * Rebase fixes
    
    * Set correctly tls struct (fix merge)
    
    * Handle Elliptic Curve variant for private key
    
    * Fix tests suite
    
    * Fix features in CI
    
    * Add tests for Postgres 15 + rebase
    
    * Python tests: fix exception for MySQL 5.7 + remove unneeded for loops
    
    * CI: run SSL tests only when building with TLS support
    
    ---------
    
    Co-authored-by: Barry Simons <linuxuser586@gmail.com>
    2 people authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    00a7da0 View commit details
  24. Add context to metadata parse error

    laundmo authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    a8016ce View commit details
  25. 0.7.0-alpha.1 release

    abonander authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    f77f28d View commit details
  26. Copy the full SHA
    a660585 View commit details
  27. Copy the full SHA
    d7bda7a View commit details
  28. Copy the full SHA
    cc4fae7 View commit details
  29. Copy the full SHA
    e00905d View commit details
  30. Update dependencies

    paolobarbolini authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    8a36be6 View commit details
  31. Copy the full SHA
    90defd3 View commit details
  32. fix(docs): example code for sqlx::test

    kenkoooo authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    019a879 View commit details
  33. feat(prepare): move to one-file-per-query for offline mode

    Co-authored-by: Jonas Platte <jonas@lumeo.com>
    2 people authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    0eeca2f View commit details
  34. chore: test macros' offline mode in CI

    Co-authored-by: Austin Bonander <austin@launchbadge.com>
    2 people authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    16e6d5b View commit details
  35. Copy the full SHA
    f6fbd55 View commit details
  36. Copy the full SHA
    0138687 View commit details
  37. chore: Use tracing's fields to get structured logs

    This also enables on services that can query this data to get useful metrics
    jaysonsantos authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    eeac7d8 View commit details
  38. README: Fix build badge

    dbrgn authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    2bb9064 View commit details
  39. README: Link build badge to workflow runs

    dbrgn authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    6dc014c View commit details
  40. Copy the full SHA
    225d0c2 View commit details
  41. Don't run EXPLAIN nullability analysis on Materialize

    Materialize [0] is a PostgreSQL-like database that, similar to
    CockroachDB, does not support PostgreSQL's `EXPLAIN` output. Extend the
    fix from PR #1248 to Materialize, too, so that sqlx can still be used
    with Materialize.
    
    See #1248.
    
    [0]: https://materialize.com
    benesch authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    10a964b View commit details
  42. feat: support calling PostgreSQL procedures with the macros

    Fixes #1449 (I think). I verified that the code fixes the new test.
    
    I used INOUT in setup.sql because older versions of Postgres don't
    support OUT parameters.
    bgeron authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    970efb9 View commit details
  43. fix: sqlx-postgres bigdecimal feature

    0xdeafbeef authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    cb6126e View commit details
  44. Copy the full SHA
    30127eb View commit details
  45. fix test

    0xdeafbeef authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    e83c596 View commit details
  46. Add From impls for Json

    dbeckwith authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    d4087ba View commit details
  47. Remove invalid impl

    dbeckwith authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    caccbc1 View commit details
  48. bump libsqlite3-sys to patched version

    grantkee authored and Aandreba committed Mar 31, 2023
    Copy the full SHA
    d826bf3 View commit details
  49. Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    30a1d36 View commit details
Showing 431 changed files with 12,101 additions and 13,659 deletions.
272 changes: 220 additions & 52 deletions .github/workflows/sqlx.yml

Large diffs are not rendered by default.

1,654 changes: 534 additions & 1,120 deletions Cargo.lock

Large diffs are not rendered by default.

235 changes: 127 additions & 108 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -2,31 +2,31 @@
members = [
".",
"sqlx-core",
"sqlx-rt",
"sqlx-macros",
"sqlx-macros-core",
"sqlx-test",
"sqlx-cli",
"sqlx-bench",
"examples/mysql/todos",
"examples/postgres/axum-social-with-tests",
"examples/postgres/files",
"examples/postgres/json",
"examples/postgres/listen",
"examples/postgres/todos",
"examples/postgres/mockable-todos",
"examples/postgres/transaction",
"examples/sqlite/todos",
# "sqlx-bench",
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
# FIXME: uncomment these for full release
# "examples/mysql/todos",
# "examples/postgres/axum-social-with-tests",
# "examples/postgres/files",
# "examples/postgres/json",
# "examples/postgres/listen",
# "examples/postgres/todos",
# "examples/postgres/mockable-todos",
# "examples/postgres/transaction",
# "examples/sqlite/todos",
]

[package]
name = "sqlx"
version = "0.6.2"
[workspace.package]
version = "0.7.0-alpha.2"
license = "MIT OR Apache-2.0"
readme = "README.md"
repository = "https://github.com/launchbadge/sqlx"
documentation = "https://docs.rs/sqlx"
description = "🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, and SQLite."
edition = "2021"
repository = "https://github.com/launchbadge/sqlx"
keywords = ["database", "async", "postgres", "mysql", "sqlite"]
categories = ["database", "asynchronous"]
authors = [
@@ -36,97 +36,126 @@ authors = [
"Daniel Akhterov <akhterovd@gmail.com>",
]

[package]
name = "sqlx"
readme = "README.md"
documentation = "https://docs.rs/sqlx"
description = "🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, and SQLite."
version.workspace = true
license.workspace = true
edition.workspace = true
authors.workspace = true
repository.workspace = true

[package.metadata.docs.rs]
features = ["all", "runtime-tokio-native-tls"]
features = ["all-databases", "_unstable-all-types"]
rustdoc-args = ["--cfg", "docsrs"]

[features]
default = ["macros", "migrate"]
default = ["any", "macros", "migrate", "json"]
macros = ["sqlx-macros"]
migrate = ["sqlx-macros/migrate", "sqlx-core/migrate"]

# [deprecated] TLS is not possible to disable due to it being conditional on multiple features
# Hopefully Cargo can handle this in the future
tls = []

# offline building support in `sqlx-macros`
offline = ["sqlx-macros/offline", "sqlx-core/offline"]
migrate = ["sqlx-core/migrate", "sqlx-macros?/migrate", "sqlx-mysql?/migrate", "sqlx-postgres?/migrate", "sqlx-sqlite?/migrate"]

# intended mainly for CI and docs
all = ["tls", "all-databases", "all-types"]
all-databases = ["mysql", "sqlite", "postgres", "mssql", "any"]
all-types = [
all-databases = ["mysql", "sqlite", "postgres", "any"]
_unstable-all-types = [
"bigdecimal",
"decimal",
"rust_decimal",
"json",
"time",
"chrono",
"ipnetwork",
"mac_address",
"uuid",
"bit-vec",
"bstr",
"git2",
]

# previous runtimes, available as features for error messages better than just
# "feature doesn't exist"
runtime-actix = []
runtime-async-std = []
runtime-tokio = []

# actual runtimes
runtime-actix-native-tls = ["runtime-tokio-native-tls"]
runtime-async-std-native-tls = [
"sqlx-core/runtime-async-std-native-tls",
"sqlx-macros/runtime-async-std-native-tls",
"_rt-async-std",
]
runtime-tokio-native-tls = [
"sqlx-core/runtime-tokio-native-tls",
"sqlx-macros/runtime-tokio-native-tls",
"_rt-tokio",
]
# Base runtime features without TLS
runtime-async-std = ["_rt-async-std", "sqlx-core/_rt-async-std", "sqlx-macros/_rt-async-std"]
runtime-tokio = ["_rt-tokio", "sqlx-core/_rt-tokio", "sqlx-macros/_rt-tokio"]

runtime-actix-rustls = ["runtime-tokio-rustls"]
runtime-async-std-rustls = [
"sqlx-core/runtime-async-std-rustls",
"sqlx-macros/runtime-async-std-rustls",
"_rt-async-std",
]
runtime-tokio-rustls = [
"sqlx-core/runtime-tokio-rustls",
"sqlx-macros/runtime-tokio-rustls",
"_rt-tokio",
]
# TLS features
tls-native-tls = ["sqlx-core/_tls-native-tls", "sqlx-macros/_tls-native-tls"]
tls-rustls = ["sqlx-core/_tls-rustls", "sqlx-macros/_tls-rustls"]

# No-op feature used by the workflows to compile without TLS enabled. Not meant for general use.
tls-none = []

# Legacy Runtime + TLS features

runtime-async-std-native-tls = ["runtime-async-std", "tls-native-tls"]
runtime-async-std-rustls = ["runtime-async-std", "tls-rustls"]

runtime-tokio-native-tls = ["runtime-tokio", "tls-native-tls"]
runtime-tokio-rustls = ["runtime-tokio", "tls-rustls"]

# for conditional compilation
_rt-async-std = []
_rt-tokio = []

# database
any = ["sqlx-core/any"]
postgres = ["sqlx-core/postgres", "sqlx-macros/postgres"]
mysql = ["sqlx-core/mysql", "sqlx-macros/mysql"]
sqlite = ["sqlx-core/sqlite", "sqlx-macros/sqlite"]
mssql = ["sqlx-core/mssql", "sqlx-macros/mssql"]
any = ["sqlx-core/any", "sqlx-mysql?/any", "sqlx-postgres?/any", "sqlx-sqlite?/any"]
postgres = ["sqlx-postgres", "sqlx-macros?/postgres"]
mysql = ["sqlx-mysql", "sqlx-macros?/mysql"]
sqlite = ["sqlx-sqlite", "sqlx-macros?/sqlite"]

# types
bigdecimal = ["sqlx-core/bigdecimal", "sqlx-macros/bigdecimal"]
decimal = ["sqlx-core/decimal", "sqlx-macros/decimal"]
chrono = ["sqlx-core/chrono", "sqlx-macros/chrono"]
ipnetwork = ["sqlx-core/ipnetwork", "sqlx-macros/ipnetwork"]
mac_address = ["sqlx-core/mac_address", "sqlx-macros/mac_address"]
uuid = ["sqlx-core/uuid", "sqlx-macros/uuid"]
json = ["sqlx-core/json", "sqlx-macros/json"]
time = ["sqlx-core/time", "sqlx-macros/time"]
bit-vec = ["sqlx-core/bit-vec", "sqlx-macros/bit-vec"]
bstr = ["sqlx-core/bstr"]
git2 = ["sqlx-core/git2"]
json = ["sqlx-macros?/json", "sqlx-mysql?/json", "sqlx-postgres?/json", "sqlx-sqlite?/json"]

bigdecimal = ["sqlx-core/bigdecimal", "sqlx-macros?/bigdecimal", "sqlx-mysql?/bigdecimal", "sqlx-postgres?/bigdecimal"]
bit-vec = ["sqlx-core/bit-vec", "sqlx-macros?/bit-vec", "sqlx-postgres?/bit-vec"]
chrono = ["sqlx-core/chrono", "sqlx-macros?/chrono", "sqlx-mysql?/chrono", "sqlx-postgres?/chrono", "sqlx-sqlite?/chrono"]
ipnetwork = ["sqlx-core/ipnetwork", "sqlx-macros?/ipnetwork", "sqlx-postgres?/ipnetwork"]
mac_address = ["sqlx-core/mac_address", "sqlx-macros?/mac_address", "sqlx-postgres?/mac_address"]
rust_decimal = ["sqlx-core/rust_decimal", "sqlx-macros?/rust_decimal", "sqlx-mysql?/rust_decimal", "sqlx-postgres?/rust_decimal"]
time = ["sqlx-core/time", "sqlx-macros?/time", "sqlx-mysql?/time", "sqlx-postgres?/time", "sqlx-sqlite?/time"]
uuid = ["sqlx-core/uuid", "sqlx-macros?/uuid", "sqlx-mysql?/uuid", "sqlx-postgres?/uuid", "sqlx-sqlite?/uuid"]
regexp = ["sqlx-sqlite?/regexp"]

[workspace.dependencies]
# Core Crates
sqlx-core = { version = "=0.7.0-alpha.2", path = "sqlx-core" }
sqlx-macros-core = { version = "=0.7.0-alpha.2", path = "sqlx-macros-core" }
sqlx-macros = { version = "=0.7.0-alpha.2", path = "sqlx-macros" }

# Driver crates
sqlx-mysql = { version = "=0.7.0-alpha.2", path = "sqlx-mysql" }
sqlx-postgres = { version = "=0.7.0-alpha.2", path = "sqlx-postgres" }
sqlx-sqlite = { version = "=0.7.0-alpha.2", path = "sqlx-sqlite" }

# Facade crate (for reference from sqlx-cli)
sqlx = { version = "=0.7.0-alpha.2", path = "." }

# Common type integrations shared by multiple driver crates.
# These are optional unless enabled in a workspace crate.
bigdecimal = "0.3.0"
bit-vec = "0.6.3"
chrono = { version = "0.4.22", default-features = false }
ipnetwork = "0.20.0"
mac_address = "1.1.3"
rust_decimal = "1.26.1"
time = { version = "0.3.14", features = ["formatting", "parsing", "macros"] }
uuid = "1.1.2"

# Common utility crates
dotenvy = { version = "0.15.0", default-features = false }

# Runtimes
[workspace.dependencies.async-std]
version = "1"

[workspace.dependencies.tokio]
version = "1"
features = ["time", "net", "sync", "fs", "io-util", "rt"]
default-features = false

[dependencies]
sqlx-core = { version = "0.6.2", path = "sqlx-core", default-features = false }
sqlx-macros = { version = "0.6.2", path = "sqlx-macros", default-features = false, optional = true }
sqlx-core = { workspace = true, features = ["offline", "migrate"], default-features = false }
sqlx-macros = { workspace = true, default-features = false, optional = true }

sqlx-mysql = { workspace = true, optional = true }
sqlx-postgres = { workspace = true, optional = true }
sqlx-sqlite = { workspace = true, optional = true }

[dev-dependencies]
anyhow = "1.0.52"
@@ -137,7 +166,6 @@ async-std = { version = "1.10.0", features = ["attributes"] }
tokio = { version = "1.15.0", features = ["full"] }
dotenvy = "0.15.0"
trybuild = "1.0.53"
sqlx-rt = { path = "./sqlx-rt" }
sqlx-test = { path = "./sqlx-test" }
paste = "1.0.6"
serde = { version = "1.0.132", features = ["derive"] }
@@ -148,7 +176,7 @@ rand_xoshiro = "0.6.0"
hex = "0.4.3"
tempdir = "0.3.7"
# Needed to test SQLCipher
libsqlite3-sys = { version = "0.24", features = ["bundled-sqlcipher"] }
libsqlite3-sys = { version = "0.25.1", features = ["bundled-sqlcipher"] }

#
# Any
@@ -203,7 +231,12 @@ path = "tests/sqlite/derives.rs"
required-features = ["sqlite", "macros"]

[[test]]
name = "sqlcipher"
name = "sqlite-error"
path = "tests/sqlite/error.rs"
required-features = ["sqlite"]

[[test]]
name = "sqlite-sqlcipher"
path = "tests/sqlite/sqlcipher.rs"
required-features = ["sqlite"]

@@ -241,6 +274,11 @@ name = "mysql-macros"
path = "tests/mysql/macros.rs"
required-features = ["mysql", "macros"]

[[test]]
name = "mysql-error"
path = "tests/mysql/error.rs"
required-features = ["mysql"]

[[test]]
name = "mysql-test-attr"
path = "tests/mysql/test-attr.rs"
@@ -280,6 +318,11 @@ name = "postgres-derives"
path = "tests/postgres/derives.rs"
required-features = ["postgres", "macros"]

[[test]]
name = "postgres-error"
path = "tests/postgres/error.rs"
required-features = ["postgres"]

[[test]]
name = "postgres-test-attr"
path = "tests/postgres/test-attr.rs"
@@ -289,27 +332,3 @@ required-features = ["postgres", "macros", "migrate"]
name = "postgres-migrate"
path = "tests/postgres/migrate.rs"
required-features = ["postgres", "macros", "migrate"]

#
# Microsoft SQL Server (MSSQL)
#

[[test]]
name = "mssql"
path = "tests/mssql/mssql.rs"
required-features = ["mssql"]

[[test]]
name = "mssql-types"
path = "tests/mssql/types.rs"
required-features = ["mssql"]

[[test]]
name = "mssql-describe"
path = "tests/mssql/describe.rs"
required-features = ["mssql"]

[[test]]
name = "mssql-macros"
path = "tests/mssql/macros.rs"
required-features = ["mssql", "macros"]
31 changes: 23 additions & 8 deletions FAQ.md
Original file line number Diff line number Diff line change
@@ -180,26 +180,41 @@ See Also:
----
### How do I compile with the macros without needing a database, e.g. in CI?

The macros support an offline mode which saves data for existing queries to a JSON file,
so the macros can just read that file instead of talking to a database.
The macros support an offline mode which saves data for existing queries to a `.sqlx` directory,
so the macros can just read those instead of talking to a database.

See the following:

* [the docs for `query!()`](https://docs.rs/sqlx/0.5.5/sqlx/macro.query.html#offline-mode-requires-the-offline-feature)
* [the README for `sqlx-cli`](sqlx-cli/README.md#enable-building-in-offline-mode-with-query)

To keep `sqlx-data.json` up-to-date you need to run `cargo sqlx prepare` before every commit that
To keep `.sqlx` up-to-date you need to run `cargo sqlx prepare` before every commit that
adds or changes a query; you can do this with a Git pre-commit hook:

```shell
$ echo "cargo sqlx prepare > /dev/null 2>&1; git add sqlx-data.json > /dev/null" > .git/hooks/pre-commit
$ echo "cargo sqlx prepare > /dev/null 2>&1; git add .sqlx > /dev/null" > .git/hooks/pre-commit
```

Note that this may make committing take some time as it'll cause your project to be recompiled, and
as an ergonomic choice it does _not_ block committing if `cargo sqlx prepare` fails.

We're working on a way for the macros to save their data to the filesystem automatically which should be part of SQLx 0.6,
so your pre-commit hook would then just need to stage the changed files.
We're working on a way for the macros to save their data to the filesystem automatically which should be part of SQLx 0.7,
so your pre-commit hook would then just need to stage the changed files. This can be enabled by creating a directory
and setting the `SQLX_OFFLINE_DIR` environment variable to it before compiling.
Additionally, if you're not using Cargo or have a nonstandard setup, you may want to set the `SQLX_TMP`
variable in order to store temporary query files somewhere that isn't picked up by git.
These files should get cleaned up automatically, but they may not if there's a failure. For example:

```shell
$ mkdir .sqlx
$ export SQLX_OFFLINE_DIR="./.sqlx"`
$ # Optional and only useful if using a nonstandard setup, ensures temp files won't get picked up by git on failure
$ mkdir ./my-custom-target/sqlx
$ export SQLX_TMP="./my-custom-target/sqlx-tmp"
$ cargo check
```

However, this behaviour is not considered stable and it is still recommended to use `cargo sqlx prepare`.

----

@@ -253,9 +268,9 @@ Even Sisyphus would pity us.
### Why does my project using sqlx query macros not build on docs.rs?
Docs.rs doesn't have access to your database, so it needs to be provided a `sqlx-data.json` file and be instructed to set the `SQLX_OFFLINE` environment variable to true while compiling your project. Luckily for us, docs.rs creates a `DOCS_RS` environment variable that we can access in a custom build script to achieve this functionality.
Docs.rs doesn't have access to your database, so it needs to be provided prepared queries in a `.sqlx` directory and be instructed to set the `SQLX_OFFLINE` environment variable to true while compiling your project. Luckily for us, docs.rs creates a `DOCS_RS` environment variable that we can access in a custom build script to achieve this functionality.

To do so, first, make sure that you have run `cargo sqlx prepare` to generate a `sqlx-data.json` file in your project.
To do so, first, make sure that you have run `cargo sqlx prepare` to generate a `.sqlx` directory in your project.

Next, create a file called `build.rs` in the root of your project directory (at the same level as `Cargo.toml`). Add the following code to it:
```rs
73 changes: 41 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -9,7 +9,10 @@

<div align="center">
<!-- Github Actions -->
<img src="https://img.shields.io/github/workflow/status/launchbadge/sqlx/SQLx?style=flat-square" alt="actions status" />
<a href="https://github.com/launchbadge/sqlx/actions/workflows/sqlx.yml?query=branch%3Amain">
<img src="https://img.shields.io/github/actions/workflow/status/launchbadge/sqlx/sqlx.yml?branch=main&style=flat-square"
alt="actions status" />
</a>
<!-- Version -->
<a href="https://crates.io/crates/sqlx">
<img src="https://img.shields.io/crates/v/sqlx.svg?style=flat-square"
@@ -114,6 +117,9 @@ with C, those interactions are `unsafe`.

SQLx is compatible with the [`async-std`], [`tokio`] and [`actix`] runtimes; and, the [`native-tls`] and [`rustls`] TLS backends. When adding the dependency, you must chose a runtime feature that is `runtime` + `tls`.

NOTE: these examples are for the coming 0.7 release, which is currently in an alpha cycle.
For the last stable release, 0.6.2, see [the previous verison of this document](https://github.com/launchbadge/sqlx/blob/v0.6.2/README.md).

[`async-std`]: https://github.com/async-rs/async-std
[`tokio`]: https://github.com/tokio-rs/tokio
[`actix`]: https://github.com/actix/actix-net
@@ -123,27 +129,48 @@ SQLx is compatible with the [`async-std`], [`tokio`] and [`actix`] runtimes; and
```toml
# Cargo.toml
[dependencies]
# PICK ONE OF THE FOLLOWING:

# tokio (no TLS)
sqlx = { version = "0.7", features = [ "runtime-tokio" ] }
# tokio + native-tls
sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-native" ] }
# tokio + rustls
sqlx = { version = "0.6", features = [ "runtime-tokio-rustls" ] }
sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-rustls" ] }

# async-std (no TLS)
sqlx = { version = "0.7", features = [ "runtime-async-std" ] }
# async-std + native-tls
sqlx = { version = "0.6", features = [ "runtime-async-std-native-tls" ] }
sqlx = { version = "0.7", features = [ "runtime-async-std", "tls-native" ] }
# async-std + rustls
sqlx = { version = "0.7", features = [ "runtime-async-std", "tls-rustls" ] }
```

<small><small>The runtime and TLS backend not being separate feature sets to select is a workaround for a [Cargo issue](https://github.com/rust-lang/cargo/issues/3494).</small></small>

#### Cargo Feature Flags

- `runtime-async-std-native-tls`: Use the `async-std` runtime and `native-tls` TLS backend.
For backwards-compatibility reasons, the runtime and TLS features can either be chosen together as a single feature,
or separately.

For forward-compatibility, you should use the separate runtime and TLS features as the combination features may
be removed in the future.

- `runtime-async-std`: Use the `async-std` runtime without enabling a TLS backend.

- `runtime-async-std-native-tls`: Use the `async-std` runtime and `native-tls` TLS backend (SOFT-DEPRECATED).

- `runtime-async-std-rustls`: Use the `async-std` runtime and `rustls` TLS backend.
- `runtime-async-std-rustls`: Use the `async-std` runtime and `rustls` TLS backend (SOFT-DEPRECATED).

- `runtime-tokio-native-tls`: Use the `tokio` runtime and `native-tls` TLS backend.
- `runtime-tokio`: Use the `tokio` runtime without enabling a TLS backend.

- `runtime-tokio-rustls`: Use the `tokio` runtime and `rustls` TLS backend.
- `runtime-tokio-native-tls`: Use the `tokio` runtime and `native-tls` TLS backend (SOFT-DEPRECATED).

- `runtime-actix-native-tls`: Use the `actix` runtime and `native-tls` TLS backend.
- `runtime-tokio-rustls`: Use the `tokio` runtime and `rustls` TLS backend (SOFT-DEPRECATED).

- `runtime-actix-rustls`: Use the `actix` runtime and `rustls` TLS backend.
- Actix-web is fully compatible with Tokio and so a separate runtime feature is no longer needed.

- `tls-native`: Use the `native-tls` TLS backend (OpenSSL on *nix, SChannel on Windows, Secure Transport on macOS).

- `tls-rustls`: Use the `rustls` TLS backend (crossplatform backend, only supports TLS 1.2 and 1.3).

- `postgres`: Add support for the Postgres database server.

@@ -167,8 +194,6 @@ sqlx = { version = "0.6", features = [ "runtime-async-std-native-tls" ] }

- `bstr`: Add support for `bstr::BString`.

- `git2`: Add support for `git2::Oid`.

- `bigdecimal`: Add support for `NUMERIC` using the `bigdecimal` crate.

- `decimal`: Add support for `NUMERIC` using the `rust_decimal` crate.
@@ -177,10 +202,7 @@ sqlx = { version = "0.6", features = [ "runtime-async-std-native-tls" ] }

- `json`: Add support for `JSON` and `JSONB` (in postgres) using the `serde_json` crate.

- `tls`: Add support for TLS connections.

- `offline`: Enables building the macros in offline mode when a live database is not available (such as CI).
- Requires `sqlx-cli` installed to use. See [sqlx-cli/README.md][readme-offline].
- Offline mode is now always enabled. See [sqlx-cli/README.md][readme-offline].

[readme-offline]: sqlx-cli/README.md#enable-building-in-offline-mode-with-query

@@ -208,21 +230,8 @@ See the `examples/` folder for more in-depth usage.

### Quickstart

```toml
[dependencies]
# PICK ONE:
# Async-std:
sqlx = { version = "0.6", features = [ "runtime-async-std-native-tls", "postgres" ] }
async-std = { version = "1", features = [ "attributes" ] }

# Tokio:
sqlx = { version = "0.6", features = [ "runtime-tokio-native-tls" , "postgres" ] }
tokio = { version = "1", features = ["full"] }

# Actix-web:
sqlx = { version = "0.6", features = [ "runtime-actix-native-tls" , "postgres" ] }
actix-web = "4"
```
NOTE: these examples are for the coming 0.7.0 release, which is currently in an alpha cycle.
For the last stable release, 0.6.2, see [the previous verison of this document](https://github.com/launchbadge/sqlx/blob/v0.6.2/README.md).

```rust
use sqlx::postgres::PgPoolOptions;
2 changes: 1 addition & 1 deletion examples/postgres/files/Cargo.toml
Original file line number Diff line number Diff line change
@@ -7,6 +7,6 @@ edition = "2021"

[dependencies]
anyhow = "1.0"
sqlx = { path = "../../../", features = ["postgres", "offline", "runtime-tokio-native-tls"] }
sqlx = { path = "../../../", features = ["postgres", "runtime-tokio-native-tls"] }
tokio = { version = "1.20.0", features = ["macros"]}
dotenvy = "0.15.0"
2 changes: 1 addition & 1 deletion examples/postgres/listen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -5,6 +5,6 @@ edition = "2021"
workspace = "../../../"

[dependencies]
sqlx = { path = "../../../", features = [ "postgres", "tls" ] }
sqlx = { path = "../../../", features = [ "postgres" ] }
futures = "0.3.1"
tokio = { version = "1.20.0", features = ["macros"]}
2 changes: 1 addition & 1 deletion examples/postgres/mockable-todos/Cargo.toml
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ workspace = "../../../"
[dependencies]
anyhow = "1.0"
futures = "0.3"
sqlx = { path = "../../../", features = ["postgres", "offline", "runtime-tokio-native-tls"] }
sqlx = { path = "../../../", features = ["postgres", "runtime-tokio-native-tls"] }
structopt = "0.3"
tokio = { version = "1.20.0", features = ["macros"]}
dotenvy = "0.15.0"
2 changes: 1 addition & 1 deletion examples/postgres/todos/Cargo.toml
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ workspace = "../../../"
[dependencies]
anyhow = "1.0"
futures = "0.3"
sqlx = { path = "../../../", features = ["postgres", "offline", "runtime-tokio-native-tls"] }
sqlx = { path = "../../../", features = ["postgres", "runtime-tokio-native-tls"] }
structopt = "0.3"
tokio = { version = "1.20.0", features = ["macros"]}
dotenvy = "0.15.0"
2 changes: 1 addition & 1 deletion examples/postgres/transaction/Cargo.toml
Original file line number Diff line number Diff line change
@@ -5,6 +5,6 @@ edition = "2021"
workspace = "../../../"

[dependencies]
sqlx = { path = "../../../", features = [ "postgres", "tls", "runtime-tokio-native-tls" ] }
sqlx = { path = "../../../", features = [ "postgres", "runtime-tokio-native-tls" ] }
futures = "0.3.1"
tokio = { version = "1.20.0", features = ["macros"]}
16 changes: 0 additions & 16 deletions prep-release.sh

This file was deleted.

7 changes: 1 addition & 6 deletions sqlx-bench/Cargo.toml
Original file line number Diff line number Diff line change
@@ -9,21 +9,17 @@ publish = false
runtime-actix-native-tls = ["runtime-tokio-native-tls"]
runtime-async-std-native-tls = [
"sqlx/runtime-async-std-native-tls",
"sqlx-rt/runtime-async-std-native-tls",
]
runtime-tokio-native-tls = [
"sqlx/runtime-tokio-native-tls",
"sqlx-rt/runtime-tokio-native-tls",
]

runtime-actix-rustls = ["runtime-tokio-rustls"]
runtime-async-std-rustls = [
"sqlx/runtime-async-std-rustls",
"sqlx-rt/runtime-async-std-rustls",
]
runtime-tokio-rustls = [
"sqlx/runtime-tokio-rustls",
"sqlx-rt/runtime-tokio-rustls",
]

postgres = ["sqlx/postgres"]
@@ -33,8 +29,7 @@ sqlite = ["sqlx/sqlite"]
criterion = "0.3.3"
dotenvy = "0.15.0"
once_cell = "1.4"
sqlx = { version = "0.6", path = "../", default-features = false, features = ["macros"] }
sqlx-rt = { version = "0.6", path = "../sqlx-rt", default-features = false }
sqlx = { workspace = true, default-features = false, features = ["macros"] }

chrono = "0.4.19"

12 changes: 6 additions & 6 deletions sqlx-bench/benches/pg_pool.rs
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ fn bench_pgpool_acquire(c: &mut Criterion) {
}

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()
// we don't want timeouts because we want to see how the pool degrades
.acquire_timeout(Duration::from_secs(3600))
@@ -41,8 +41,8 @@ fn do_bench_acquire(b: &mut Bencher, concurrent: u32, fair: bool) {

for _ in 0..concurrent {
let pool = pool.clone();
sqlx_rt::enter_runtime(|| {
sqlx_rt::spawn(async move {
sqlx::__rt::enter_runtime(|| {
sqlx::__rt::spawn(async move {
while !pool.is_closed() {
let conn = match pool.acquire().await {
Ok(conn) => conn,
@@ -51,15 +51,15 @@ fn do_bench_acquire(b: &mut Bencher, concurrent: u32, fair: bool) {
};

// pretend we're using the connection
sqlx_rt::sleep(Duration::from_micros(500)).await;
sqlx::__rt::sleep(Duration::from_micros(500)).await;
drop(criterion::black_box(conn));
}
})
});
}

b.iter_custom(|iters| {
sqlx_rt::block_on(async {
sqlx::__rt::block_on(async {
// take the start time inside the future to make sure we only count once it's running
let start = Instant::now();
for _ in 0..iters {
@@ -73,7 +73,7 @@ fn do_bench_acquire(b: &mut Bencher, concurrent: u32, fair: bool) {
})
});

sqlx_rt::block_on(pool.close());
sqlx::__rt::block_on(pool.close());
}

criterion_group!(pg_pool, bench_pgpool_acquire);
2 changes: 1 addition & 1 deletion sqlx-bench/benches/sqlite_fetch_all.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ struct Test {
}

fn main() -> sqlx::Result<()> {
sqlx_rt::block_on(async {
sqlx::__rt::block_on(async {
let mut conn = sqlx::SqliteConnection::connect("sqlite://test.db?mode=rwc").await?;
let delete_sql = "DROP TABLE IF EXISTS test";
conn.execute(delete_sql).await?;
6 changes: 3 additions & 3 deletions sqlx-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sqlx-cli"
version = "0.6.2"
version.workspace = true
description = "Command-line utility for SQLx, the Rust SQL toolkit."
edition = "2021"
readme = "README.md"
@@ -27,10 +27,10 @@ path = "src/bin/cargo-sqlx.rs"
[dependencies]
dotenvy = "0.15.0"
tokio = { version = "1.15.0", features = ["macros", "rt", "rt-multi-thread"] }
sqlx = { version = "0.6.2", path = "..", default-features = false, features = [
sqlx = { workspace = true, default-features = false, features = [
"runtime-tokio",
"migrate",
"any",
"offline",
] }
futures = "0.3.19"
clap = { version = "3.1.0", features = ["derive", "env"] }
37 changes: 18 additions & 19 deletions sqlx-cli/README.md
Original file line number Diff line number Diff line change
@@ -106,44 +106,43 @@ error: cannot mix reversible migrations with simple migrations. All migrations s

### Enable building in "offline mode" with `query!()`

There are 3 steps to building with "offline mode":
There are 2 steps to building with "offline mode":

1. Enable the SQLx's Cargo feature `offline`
- E.g. in your `Cargo.toml`, `sqlx = { features = [ "offline", ... ] }`
2. Save query metadata for offline usage
1. Save query metadata for offline usage
- `cargo sqlx prepare`
3. Build
2. Build

Note: Saving query metadata must be run as `cargo sqlx`.

```bash
cargo sqlx prepare
```

Invoking `prepare` saves query metadata to `sqlx-data.json` in the current directory; check this file into version
control and an active database connection will no longer be needed to build your project.
Invoking `prepare` saves query metadata to `.sqlx` in the current directory.
For workspaces where several crates are using query macros, pass the `--workspace` flag
to generate a single `.sqlx` directory at the root of the workspace.

Has no effect unless the `offline` Cargo feature of `sqlx` is enabled in your project. Omitting that
feature is the most likely cause if you get a `sqlx-data.json` file that looks like this:

```json
{
"database": "PostgreSQL"
}
```bash
cargo sqlx prepare --workspace
```

Check this directory into version control and an active database connection will
no longer be needed to build your project.

---

```bash
cargo sqlx prepare --check
# OR
cargo sqlx prepare --check --workspace
```

Exits with a nonzero exit status if the data in `sqlx-data.json` is out of date with the current
database schema and queries in the project. Intended for use in Continuous Integration.
Exits with a nonzero exit status if the data in `.sqlx` is out of date with the current
database schema or queries in the project. Intended for use in Continuous Integration.

### Force building in offline mode

The presence of a `DATABASE_URL` environment variable will take precedence over the presence of `sqlx-data.json`, meaning SQLx will default to building against a database if it can. To make sure an accidentally-present `DATABASE_URL` environment variable or `.env` file does not
The presence of a `DATABASE_URL` environment variable will take precedence over the presence of `.sqlx`, meaning SQLx will default to building against a database if it can. To make sure an accidentally-present `DATABASE_URL` environment variable or `.env` file does not
result in `cargo build` (trying to) access the database, you can set the `SQLX_OFFLINE` environment
variable to `true`.

@@ -152,8 +151,8 @@ still do the right thing and connect to the database.

### Include queries behind feature flags (such as queries inside of tests)

In order for sqlx to be able to find queries behind certain feature flags you need to turn them
on by passing arguments to rustc.
In order for sqlx to be able to find queries behind certain feature flags or in tests, you need to turn them
on by passing arguments to `cargo`.

This is how you would turn all targets and features on.

49 changes: 14 additions & 35 deletions sqlx-cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::io;
use std::time::Duration;

use anyhow::Result;
use futures::{Future, TryFutureExt};

use sqlx::{AnyConnection, Connection};
use std::io;
use std::time::Duration;

use crate::opt::{Command, ConnectOpts, DatabaseCommand, MigrateCommand};

@@ -23,42 +25,24 @@ pub async fn run(opt: Opt) -> Result<()> {
source,
description,
reversible,
} => migrate::add(source.resolve(&migrate.source), &description, reversible).await?,
} => migrate::add(&source, &description, reversible).await?,
MigrateCommand::Run {
source,
dry_run,
ignore_missing,
connect_opts,
} => {
migrate::run(
source.resolve(&migrate.source),
&connect_opts,
dry_run,
*ignore_missing,
)
.await?
}
} => migrate::run(&source, &connect_opts, dry_run, *ignore_missing).await?,
MigrateCommand::Revert {
source,
dry_run,
ignore_missing,
connect_opts,
} => {
migrate::revert(
source.resolve(&migrate.source),
&connect_opts,
dry_run,
*ignore_missing,
)
.await?
}
} => migrate::revert(&source, &connect_opts, dry_run, *ignore_missing).await?,
MigrateCommand::Info {
source,
connect_opts,
} => migrate::info(source.resolve(&migrate.source), &connect_opts).await?,
MigrateCommand::BuildScript { source, force } => {
migrate::build_script(source.resolve(&migrate.source), force)?
}
} => migrate::info(&source, &connect_opts).await?,
MigrateCommand::BuildScript { source, force } => migrate::build_script(&source, force)?,
},

Command::Database(database) => match database.command {
@@ -79,18 +63,11 @@ pub async fn run(opt: Opt) -> Result<()> {
},

Command::Prepare {
check: false,
merged,
args,
check,
workspace,
connect_opts,
} => prepare::run(&connect_opts, merged, args).await?,

Command::Prepare {
check: true,
merged,
args,
connect_opts,
} => prepare::check(&connect_opts, merged, args).await?,
} => prepare::run(check, workspace, connect_opts, args).await?,
};

Ok(())
@@ -113,6 +90,8 @@ where
F: FnMut(&'a str) -> Fut,
Fut: Future<Output = sqlx::Result<T>> + 'a,
{
sqlx::any::install_default_drivers();

backoff::future::retry(
backoff::ExponentialBackoffBuilder::new()
.with_max_elapsed_time(Some(Duration::from_secs(opts.connect_timeout)))
55 changes: 49 additions & 6 deletions sqlx-cli/src/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use anyhow::{Context, Result};
use cargo_metadata::{
Metadata as CargoMetadata, Package as MetadataPackage, PackageId as MetadataId,
};

use std::{
collections::{btree_map, BTreeMap, BTreeSet},
ffi::OsStr,
path::{Path, PathBuf},
process::Command,
str::FromStr,
};

use anyhow::Context;
use cargo_metadata::{
Metadata as CargoMetadata, Package as MetadataPackage, PackageId as MetadataId,
};

/// The minimal amount of package information we care about
///
/// The package's `name` is used to `cargo clean -p` specific crates while the `src_paths` are
@@ -50,6 +52,8 @@ pub struct Metadata {
packages: BTreeMap<MetadataId, Package>,
/// All of the crates in the current workspace
workspace_members: Vec<MetadataId>,
/// Workspace root path.
workspace_root: PathBuf,
/// Maps each dependency to its set of dependents
reverse_deps: BTreeMap<MetadataId, BTreeSet<MetadataId>>,
/// The target directory of the project
@@ -62,6 +66,19 @@ pub struct Metadata {
}

impl Metadata {
/// Parse the manifest from the current working directory using `cargo metadata`.
pub fn from_current_directory(cargo: &OsStr) -> anyhow::Result<Self> {
let output = Command::new(cargo)
.args(["metadata", "--format-version=1"])
.output()
.context("Could not fetch metadata")?;

std::str::from_utf8(&output.stdout)
.context("Invalid `cargo metadata` output")?
.parse()
.context("Issue parsing `cargo metadata` output - consider manually running it to check for issues")
}

pub fn package(&self, id: &MetadataId) -> Option<&Package> {
self.packages.get(id)
}
@@ -74,6 +91,10 @@ impl Metadata {
&self.workspace_members
}

pub fn workspace_root(&self) -> &Path {
&self.workspace_root
}

pub fn target_directory(&self) -> &Path {
&self.target_directory
}
@@ -97,7 +118,7 @@ impl Metadata {
if let Some(immediate_dependents) = self.reverse_deps.get(id) {
for immediate_dependent in immediate_dependents {
if dependents.insert(immediate_dependent) {
self.all_dependents_of_helper(&immediate_dependent, dependents);
self.all_dependents_of_helper(immediate_dependent, dependents);
}
}
}
@@ -117,6 +138,7 @@ impl FromStr for Metadata {
let CargoMetadata {
packages: metadata_packages,
workspace_members,
workspace_root,
resolve,
target_directory,
..
@@ -142,14 +164,35 @@ impl FromStr for Metadata {
}
}

let workspace_root = workspace_root.into_std_path_buf();
let target_directory = target_directory.into_std_path_buf();

Ok(Self {
packages,
workspace_members,
workspace_root,
reverse_deps,
target_directory,
current_package,
})
}
}

/// The absolute path to the directory containing the `Cargo.toml` manifest.
/// Depends on the current working directory.
pub(crate) fn manifest_dir(cargo: &OsStr) -> anyhow::Result<PathBuf> {
let stdout = Command::new(cargo)
.args(["locate-project", "--message-format=plain"])
.output()
.context("could not locate manifest directory")?
.stdout;

let mut manifest_path: PathBuf = std::str::from_utf8(&stdout)
.context("output of `cargo locate-project` was not valid UTF-8")?
// remove trailing newline
.trim()
.into();

manifest_path.pop();
Ok(manifest_path)
}
47 changes: 12 additions & 35 deletions sqlx-cli/src/opt.rs
Original file line number Diff line number Diff line change
@@ -16,8 +16,8 @@ pub enum Command {

/// Generate query metadata to support offline compile-time verification.
///
/// Saves metadata for all invocations of `query!` and related macros to `sqlx-data.json`
/// in the current directory, overwriting if needed.
/// Saves metadata for all invocations of `query!` and related macros to a `.sqlx` directory
/// in the current directory (or workspace root with `--workspace`), overwriting if needed.
///
/// During project compilation, the absence of the `DATABASE_URL` environment variable or
/// the presence of `SQLX_OFFLINE` (with a value of `true` or `1`) will constrain the
@@ -29,9 +29,12 @@ pub enum Command {
#[clap(long)]
check: bool,

/// Generate a single top-level `sqlx-data.json` file when using a cargo workspace.
/// Generate a single workspace-level `.sqlx` folder.
///
/// This option is intended for workspaces where multiple crates use SQLx. If there is only
/// one, it is better to run `cargo sqlx prepare` without this option inside that crate.
#[clap(long)]
merged: bool,
workspace: bool,

/// Arguments to be passed to `cargo rustc ...`.
#[clap(last = true)]
@@ -94,11 +97,6 @@ pub enum DatabaseCommand {
/// Group of commands for creating and running migrations.
#[derive(Parser, Debug)]
pub struct MigrateOpt {
/// Path to folder containing migrations.
/// Warning: deprecated, use <SUBCOMMAND> --source <SOURCE>
#[clap(long, default_value = "migrations")]
pub source: String,

#[clap(subcommand)]
pub command: MigrateCommand,
}
@@ -111,7 +109,7 @@ pub enum MigrateCommand {
description: String,

#[clap(flatten)]
source: SourceOverride,
source: Source,

/// If true, creates a pair of up and down migration files with same version
/// else creates a single sql file
@@ -122,7 +120,7 @@ pub enum MigrateCommand {
/// Run all pending migrations.
Run {
#[clap(flatten)]
source: SourceOverride,
source: Source,

/// List all the migrations to be run without applying
#[clap(long)]
@@ -138,7 +136,7 @@ pub enum MigrateCommand {
/// Revert the latest migration with a down file.
Revert {
#[clap(flatten)]
source: SourceOverride,
source: Source,

/// List the migration to be reverted without applying
#[clap(long)]
@@ -154,7 +152,7 @@ pub enum MigrateCommand {
/// List all available migrations.
Info {
#[clap(flatten)]
source: SourceOverride,
source: Source,

#[clap(flatten)]
connect_opts: ConnectOpts,
@@ -165,7 +163,7 @@ pub enum MigrateCommand {
/// Must be run in a Cargo project root.
BuildScript {
#[clap(flatten)]
source: SourceOverride,
source: Source,

/// Overwrite the build script if it already exists.
#[clap(long)]
@@ -189,27 +187,6 @@ impl Deref for Source {
}
}

/// Argument for overriding migration scripts source.
// Note: once `MigrateOpt.source` is removed, usage can be replaced with `Source`.
#[derive(Args, Debug)]
pub struct SourceOverride {
/// Path to folder containing migrations [default: migrations]
#[clap(long)]
source: Option<String>,
}

impl SourceOverride {
/// Override command's `source` flag value with subcommand's
/// `source` flag value when provided.
#[inline]
pub(super) fn resolve<'a>(&'a self, source: &'a str) -> &'a str {
match self.source {
Some(ref source) => source,
None => source,
}
}
}

/// Argument for the database URL.
#[derive(Args, Debug)]
pub struct ConnectOpts {
443 changes: 203 additions & 240 deletions sqlx-cli/src/prepare.rs

Large diffs are not rendered by default.

163 changes: 41 additions & 122 deletions sqlx-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,182 +1,101 @@
[package]
name = "sqlx-core"
version = "0.6.2"
repository = "https://github.com/launchbadge/sqlx"
description = "Core of SQLx, the rust SQL toolkit. Not intended to be used directly."
license = "MIT OR Apache-2.0"
edition = "2021"
authors = [
"Ryan Leckey <leckey.ryan@gmail.com>",
"Austin Bonander <austin.bonander@gmail.com>",
"Chloe Ross <orangesnowfox@gmail.com>",
"Daniel Akhterov <akhterovd@gmail.com>",
]
version.workspace = true
license.workspace = true
edition.workspace = true
authors.workspace = true
repository.workspace = true

[package.metadata.docs.rs]
features = ["all-databases", "all-types", "offline", "runtime-tokio-native-tls"]
features = ["offline", "runtime-tokio-native-tls"]

[features]
default = ["migrate"]
default = []
migrate = ["sha2", "crc"]

# databases
all-databases = ["postgres", "mysql", "sqlite", "mssql", "any"]
postgres = [
"md-5",
"sha2",
"base64",
"sha1",
"rand",
"hmac",
"futures-channel/sink",
"futures-util/sink",
"json",
"dirs",
"whoami",
"hkdf"
]
mysql = [
"sha1",
"sha2",
"generic-array",
"num-bigint",
"digest",
"rand",
"rsa",
]
sqlite = ["libsqlite3-sys", "futures-executor", "flume"]
mssql = ["uuid", "encoding_rs", "regex"]
any = []

# types
all-types = [
"chrono",
"time",
"bigdecimal",
"decimal",
"ipnetwork",
"mac_address",
"json",
"uuid",
"bit-vec",
]
bigdecimal = ["bigdecimal_", "num-bigint"]
decimal = ["rust_decimal", "num-bigint"]
json = ["serde", "serde_json"]

# runtimes
runtime-actix-native-tls = ["runtime-tokio-native-tls"]
runtime-async-std-native-tls = [
"sqlx-rt/runtime-async-std-native-tls",
"sqlx/runtime-async-std-native-tls",
"_tls-native-tls",
"_rt-async-std",
]
runtime-tokio-native-tls = [
"sqlx-rt/runtime-tokio-native-tls",
"sqlx/runtime-tokio-native-tls",
"_tls-native-tls",
"_rt-tokio",
]

runtime-actix-rustls = ['runtime-tokio-rustls']
runtime-async-std-rustls = [
"sqlx-rt/runtime-async-std-rustls",
"sqlx/runtime-async-std-rustls",
"_tls-rustls",
"_rt-async-std",
]
runtime-tokio-rustls = [
"sqlx-rt/runtime-tokio-rustls",
"sqlx/runtime-tokio-rustls",
"_tls-rustls",
"_rt-tokio"
]

# for conditional compilation
_rt-async-std = []
_rt-tokio = ["tokio-stream"]
_tls-native-tls = []
_rt-async-std = ["async-std", "async-io"]
_rt-tokio = ["tokio", "tokio-stream"]
_tls-native-tls = ["native-tls"]
_tls-rustls = ["rustls", "rustls-pemfile", "webpki-roots"]
_tls-none = []

# support offline/decoupled building (enables serialization of `Describe`)
offline = ["serde", "either/serde"]

[dependencies]
# Runtimes
async-std = { workspace = true, optional = true }
tokio = { workspace = true, optional = true }

# TLS
native-tls = { version = "0.2.10", optional = true }

rustls = { version = "0.20.6", default-features = false, features = ["dangerous_configuration", "tls12"], optional = true }
rustls-pemfile = { version = "1.0", optional = true }
webpki-roots = { version = "0.22.0", optional = true }

# Type Integrations
bit-vec = { workspace = true, optional = true }
bigdecimal = { workspace = true, optional = true }
rust_decimal = { workspace = true, optional = true }
time = { workspace = true, optional = true }
ipnetwork = { workspace = true, optional = true }
mac_address = { workspace = true, optional = true }
uuid = { workspace = true, optional = true }

async-io = { version = "1.9.0", optional = true }
paste = "1.0.6"
ahash = "0.7.6"
atoi = "1.0"
sqlx-rt = { path = "../sqlx-rt", version = "0.6.2" }
base64 = { version = "0.13.0", default-features = false, optional = true, features = ["std"] }
bigdecimal_ = { version = "0.3.0", optional = true, package = "bigdecimal" }
rust_decimal = { version = "1.19.0", optional = true }
bit-vec = { version = "0.6.3", optional = true }
atoi = "2.0"

bitflags = { version = "1.3.2", default-features = false }
bytes = "1.1.0"
byteorder = { version = "1.4.3", default-features = false, features = ["std"] }
chrono = { version = "0.4.19", default-features = false, features = ["clock"], optional = true }
crc = { version = "3", optional = true }
crossbeam-queue = "0.3.2"
digest = { version = "0.10.0", default-features = false, optional = true, features = ["std"] }
dirs = { version = "4.0.0", optional = true }
encoding_rs = { version = "0.8.30", optional = true }
either = "1.6.1"
futures-channel = { version = "0.3.19", default-features = false, features = ["sink", "alloc", "std"] }
futures-core = { version = "0.3.19", default-features = false }
futures-intrusive = "0.4.0"
futures-util = { version = "0.3.19", default-features = false, features = ["alloc", "sink"] }
# used by the SQLite worker thread to block on the async mutex that locks the database handle
futures-executor = { version = "0.3.19", optional = true }
flume = { version = "0.10.9", optional = true, default-features = false, features = ["async"] }
futures-io = "0.3.24"
futures-intrusive = "0.5.0"
futures-util = { version = "0.3.19", default-features = false, features = ["alloc", "sink", "io"] }
generic-array = { version = "0.14.4", default-features = false, optional = true }
hex = "0.4.3"
hmac = { version = "0.12.0", default-features = false, optional = true }
itoa = "1.0.1"
ipnetwork = { version = "0.19.0", default-features = false, optional = true }
mac_address = { version = "1.1.2", default-features = false, optional = true }
libc = "0.2.112"
libsqlite3-sys = { version = "0.24.1", optional = true, default-features = false, features = [
"pkg-config",
"vcpkg",
"bundled",
"unlock_notify"
] }
log = { version = "0.4.14", default-features = false }
md-5 = { version = "0.10.0", default-features = false, optional = true }
memchr = { version = "2.4.1", default-features = false }
num-bigint = { version = "0.4.0", default-features = false, optional = true, features = ["std"] }
once_cell = "1.9.0"
percent-encoding = "2.1.0"
rand = { version = "0.8.4", default-features = false, optional = true, features = ["std", "std_rng"] }
regex = { version = "1.5.5", optional = true }
rsa = { version = "0.6.0", optional = true }
rustls = { version = "0.20.1", features = ["dangerous_configuration"], optional = true }
rustls-pemfile = { version = "1.0", optional = true }
rsa = { version = "0.8.0", optional = true }
serde = { version = "1.0.132", features = ["derive", "rc"], optional = true }
serde_json = { version = "1.0.73", features = ["raw_value"], optional = true }
sha1 = { version = "0.10.1", default-features = false, optional = true }
sha2 = { version = "0.10.0", default-features = false, optional = true }
sqlformat = "0.2.0"
thiserror = "1.0.30"
time = { version = "0.3.2", features = ["macros", "formatting", "parsing"], optional = true }
tokio-stream = { version = "0.1.8", features = ["fs"], optional = true }
tracing = { version = "0.1.37", features = ["log"] }
smallvec = "1.7.0"
url = { version = "2.2.2", default-features = false }
uuid = { version = "1.0", default-features = false, optional = true, features = ["std"] }
webpki-roots = { version = "0.22.0", optional = true }
whoami = { version = "1.2.1", optional = true }
stringprep = "0.1.2"
bstr = { version = "0.2.17", default-features = false, features = ["std"], optional = true }
git2 = { version = "0.14", default-features = false, optional = true }
bstr = { version = "1.0", default-features = false, features = ["std"], optional = true }
hashlink = "0.8.0"
# NOTE: *must* remain below 1.7.0 to allow users to avoid the `ahash` cyclic dependency problem by pinning the version
# https://github.com/tkaitchuck/aHash/issues/95#issuecomment-874150078
indexmap = "1.6.0"
hkdf = { version = "0.12.0", optional = true }
event-listener = "2.5.2"

dotenvy = "0.15"

[dev-dependencies]
sqlx = { version = "0.6.2", path = "..", features = ["postgres", "sqlite", "mysql"] }
sqlx = { workspace = true, features = ["postgres", "sqlite", "mysql", "migrate"] }
tokio = { version = "1", features = ["rt"] }
69 changes: 10 additions & 59 deletions sqlx-core/src/acquire.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::database::Database;
use crate::error::Error;
use crate::pool::{MaybePoolConnection, Pool, PoolConnection};

use crate::transaction::Transaction;
use futures_core::future::BoxFuture;
use std::ops::{Deref, DerefMut};
@@ -14,7 +15,7 @@ use std::ops::{Deref, DerefMut};
///
/// ```rust
/// # use sqlx::{Acquire, postgres::Postgres, error::BoxDynError};
/// # #[cfg(any(postgres_9_6, postgres_14))]
/// # #[cfg(any(postgres_9_6, postgres_15))]
/// async fn run_query<'a, A>(conn: A) -> Result<(), BoxDynError>
/// where
/// A: Acquire<'a, Database = Postgres>,
@@ -34,7 +35,7 @@ use std::ops::{Deref, DerefMut};
/// ```rust
/// # use std::future::Future;
/// # use sqlx::{Acquire, postgres::Postgres, error::BoxDynError};
/// # #[cfg(any(postgres_9_6, postgres_14))]
/// # #[cfg(any(postgres_9_6, postgres_15))]
/// fn run_query<'a, 'c, A>(conn: A) -> impl Future<Output = Result<(), BoxDynError>> + Send + 'a
/// where
/// A: Acquire<'c, Database = Postgres> + Send + 'a,
@@ -56,7 +57,7 @@ use std::ops::{Deref, DerefMut};
///
/// ```rust
/// # use sqlx::{postgres::PgConnection, error::BoxDynError};
/// # #[cfg(any(postgres_9_6, postgres_14))]
/// # #[cfg(any(postgres_9_6, postgres_15))]
/// async fn run_query(conn: &mut PgConnection) -> Result<(), BoxDynError> {
/// sqlx::query!("SELECT 1 as v").fetch_one(&mut *conn).await?;
/// sqlx::query!("SELECT 2 as v").fetch_one(&mut *conn).await?;
@@ -97,18 +98,18 @@ impl<'a, DB: Database> Acquire<'a> for &'_ Pool<DB> {
}
}

#[allow(unused_macros)]
#[macro_export]
macro_rules! impl_acquire {
($DB:ident, $C:ident) => {
impl<'c> crate::acquire::Acquire<'c> for &'c mut $C {
impl<'c> $crate::acquire::Acquire<'c> for &'c mut $C {
type Database = $DB;

type Connection = &'c mut <$DB as crate::database::Database>::Connection;
type Connection = &'c mut <$DB as $crate::database::Database>::Connection;

#[inline]
fn acquire(
self,
) -> futures_core::future::BoxFuture<'c, Result<Self::Connection, crate::error::Error>>
) -> futures_core::future::BoxFuture<'c, Result<Self::Connection, $crate::error::Error>>
{
Box::pin(futures_util::future::ok(self))
}
@@ -118,59 +119,9 @@ macro_rules! impl_acquire {
self,
) -> futures_core::future::BoxFuture<
'c,
Result<crate::transaction::Transaction<'c, $DB>, crate::error::Error>,
> {
crate::transaction::Transaction::begin(self)
}
}

impl<'c> crate::acquire::Acquire<'c> for &'c mut crate::pool::PoolConnection<$DB> {
type Database = $DB;

type Connection = &'c mut <$DB as crate::database::Database>::Connection;

#[inline]
fn acquire(
self,
) -> futures_core::future::BoxFuture<'c, Result<Self::Connection, crate::error::Error>>
{
Box::pin(futures_util::future::ok(&mut **self))
}

#[inline]
fn begin(
self,
) -> futures_core::future::BoxFuture<
'c,
Result<crate::transaction::Transaction<'c, $DB>, crate::error::Error>,
> {
crate::transaction::Transaction::begin(&mut **self)
}
}

impl<'c, 't> crate::acquire::Acquire<'t>
for &'t mut crate::transaction::Transaction<'c, $DB>
{
type Database = $DB;

type Connection = &'t mut <$DB as crate::database::Database>::Connection;

#[inline]
fn acquire(
self,
) -> futures_core::future::BoxFuture<'t, Result<Self::Connection, crate::error::Error>>
{
Box::pin(futures_util::future::ok(&mut **self))
}

#[inline]
fn begin(
self,
) -> futures_core::future::BoxFuture<
't,
Result<crate::transaction::Transaction<'t, $DB>, crate::error::Error>,
Result<$crate::transaction::Transaction<'c, $DB>, $crate::error::Error>,
> {
crate::transaction::Transaction::begin(&mut **self)
$crate::transaction::Transaction::begin(self)
}
}
};
143 changes: 40 additions & 103 deletions sqlx-core/src/any/arguments.rs
Original file line number Diff line number Diff line change
@@ -1,133 +1,70 @@
use crate::any::value::AnyValueKind;
use crate::any::Any;
use crate::arguments::Arguments;
use crate::encode::Encode;
use crate::types::Type;

#[derive(Default)]
pub struct AnyArguments<'q> {
values: Vec<Box<dyn Encode<'q, Any> + Send + 'q>>,
#[doc(hidden)]
pub values: AnyArgumentBuffer<'q>,
}

impl<'q> Arguments<'q> for AnyArguments<'q> {
type Database = Any;

fn reserve(&mut self, additional: usize, _size: usize) {
self.values.reserve(additional);
self.values.0.reserve(additional);
}

fn add<T>(&mut self, value: T)
where
T: 'q + Send + Encode<'q, Self::Database> + Type<Self::Database>,
{
self.values.push(Box::new(value));
let _ = value.encode(&mut self.values);
}
}

pub struct AnyArgumentBuffer<'q>(pub(crate) AnyArgumentBufferKind<'q>);
pub struct AnyArgumentBuffer<'q>(#[doc(hidden)] pub Vec<AnyValueKind<'q>>);

pub(crate) enum AnyArgumentBufferKind<'q> {
#[cfg(feature = "postgres")]
Postgres(
crate::postgres::PgArguments,
std::marker::PhantomData<&'q ()>,
),

#[cfg(feature = "mysql")]
MySql(
crate::mysql::MySqlArguments,
std::marker::PhantomData<&'q ()>,
),

#[cfg(feature = "sqlite")]
Sqlite(crate::sqlite::SqliteArguments<'q>),

#[cfg(feature = "mssql")]
Mssql(
crate::mssql::MssqlArguments,
std::marker::PhantomData<&'q ()>,
),
}

// control flow inferred type bounds would be fun
// the compiler should know the branch is totally unreachable

#[cfg(feature = "sqlite")]
#[allow(irrefutable_let_patterns)]
impl<'q> From<AnyArguments<'q>> for crate::sqlite::SqliteArguments<'q> {
fn from(args: AnyArguments<'q>) -> Self {
let mut buf = AnyArgumentBuffer(AnyArgumentBufferKind::Sqlite(Default::default()));

for value in args.values {
let _ = value.encode_by_ref(&mut buf);
}

if let AnyArgumentBufferKind::Sqlite(args) = buf.0 {
args
} else {
unreachable!()
impl<'q> Default for AnyArguments<'q> {
fn default() -> Self {
AnyArguments {
values: AnyArgumentBuffer(vec![]),
}
}
}

#[cfg(feature = "mysql")]
#[allow(irrefutable_let_patterns)]
impl<'q> From<AnyArguments<'q>> for crate::mysql::MySqlArguments {
fn from(args: AnyArguments<'q>) -> Self {
let mut buf = AnyArgumentBuffer(AnyArgumentBufferKind::MySql(
Default::default(),
std::marker::PhantomData,
));

for value in args.values {
let _ = value.encode_by_ref(&mut buf);
}

if let AnyArgumentBufferKind::MySql(args, _) = buf.0 {
args
} else {
unreachable!()
}
}
}

#[cfg(feature = "mssql")]
#[allow(irrefutable_let_patterns)]
impl<'q> From<AnyArguments<'q>> for crate::mssql::MssqlArguments {
fn from(args: AnyArguments<'q>) -> Self {
let mut buf = AnyArgumentBuffer(AnyArgumentBufferKind::Mssql(
Default::default(),
std::marker::PhantomData,
));

for value in args.values {
let _ = value.encode_by_ref(&mut buf);
}

if let AnyArgumentBufferKind::Mssql(args, _) = buf.0 {
args
} else {
unreachable!()
}
}
}

#[cfg(feature = "postgres")]
#[allow(irrefutable_let_patterns)]
impl<'q> From<AnyArguments<'q>> for crate::postgres::PgArguments {
fn from(args: AnyArguments<'q>) -> Self {
let mut buf = AnyArgumentBuffer(AnyArgumentBufferKind::Postgres(
Default::default(),
std::marker::PhantomData,
));

for value in args.values {
let _ = value.encode_by_ref(&mut buf);
impl<'q> AnyArguments<'q> {
#[doc(hidden)]
pub fn convert_to<'a, A: Arguments<'a>>(&'a self) -> A
where
'q: 'a,
Option<i32>: Type<A::Database> + Encode<'a, A::Database>,
bool: Type<A::Database> + Encode<'a, A::Database>,
i16: Type<A::Database> + Encode<'a, A::Database>,
i32: Type<A::Database> + Encode<'a, A::Database>,
i64: Type<A::Database> + Encode<'a, A::Database>,
f32: Type<A::Database> + Encode<'a, A::Database>,
f64: Type<A::Database> + Encode<'a, A::Database>,
&'a str: Type<A::Database> + Encode<'a, A::Database>,
&'a [u8]: Type<A::Database> + Encode<'a, A::Database>,
{
let mut out = A::default();

for arg in &self.values.0 {
match arg {
AnyValueKind::Null => out.add(Option::<i32>::None),
AnyValueKind::Bool(b) => out.add(b),
AnyValueKind::SmallInt(i) => out.add(i),
AnyValueKind::Integer(i) => out.add(i),
AnyValueKind::BigInt(i) => out.add(i),
AnyValueKind::Real(r) => out.add(r),
AnyValueKind::Double(d) => out.add(d),
AnyValueKind::Text(t) => out.add(&**t),
AnyValueKind::Blob(b) => out.add(&**b),
}
}

if let AnyArgumentBufferKind::Postgres(args, _) = buf.0 {
args
} else {
unreachable!()
}
out
}
}
434 changes: 11 additions & 423 deletions sqlx-core/src/any/column.rs

Large diffs are not rendered by default.

91 changes: 91 additions & 0 deletions sqlx-core/src/any/connection/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use crate::any::{Any, AnyArguments, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo};
use crate::describe::Describe;
use either::Either;
use futures_core::future::BoxFuture;
use futures_core::stream::BoxStream;
use std::fmt::Debug;

pub trait AnyConnectionBackend: std::any::Any + Debug + Send + 'static {
/// The backend name.
fn name(&self) -> &str;

/// 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: Box<Self>) -> BoxFuture<'static, crate::Result<()>>;

/// Immediately close the connection without sending a graceful shutdown.
///
/// This should still at least send a TCP `FIN` frame to let the server know we're dying.
#[doc(hidden)]
fn close_hard(self: Box<Self>) -> BoxFuture<'static, crate::Result<()>>;

/// Checks if a connection to the database is still valid.
fn ping(&mut self) -> BoxFuture<'_, crate::Result<()>>;

/// Begin a new transaction or establish a savepoint within the active transaction.
///
/// Returns a [`Transaction`] for controlling and tracking the new transaction.
fn begin(&mut self) -> BoxFuture<'_, crate::Result<()>>;

fn commit(&mut self) -> BoxFuture<'_, crate::Result<()>>;

fn rollback(&mut self) -> BoxFuture<'_, crate::Result<()>>;

fn start_rollback(&mut self);

/// The number of statements currently cached in the connection.
fn cached_statements_size(&self) -> usize {
0
}

/// Removes all statements from the cache, closing them on the server if
/// needed.
fn clear_cached_statements(&mut self) -> BoxFuture<'_, crate::Result<()>> {
Box::pin(async move { Ok(()) })
}

/// Forward to [`Connection::shrink_buffers()`].
///
/// [`Connection::shrink_buffers()`]: method@crate::connection::Connection::shrink_buffers
fn shrink_buffers(&mut self);

#[doc(hidden)]
fn flush(&mut self) -> BoxFuture<'_, crate::Result<()>>;

#[doc(hidden)]
fn should_flush(&self) -> bool;

#[cfg(feature = "migrate")]
fn as_migrate(&mut self) -> crate::Result<&mut (dyn crate::migrate::Migrate + Send + 'static)> {
Err(crate::Error::Configuration(
format!(
"{} driver does not support migrations or `migrate` feature was not enabled",
self.name()
)
.into(),
))
}

fn fetch_many<'q>(
&'q mut self,
query: &'q str,
arguments: Option<AnyArguments<'q>>,
) -> BoxStream<'q, crate::Result<Either<AnyQueryResult, AnyRow>>>;

fn fetch_optional<'q>(
&'q mut self,
query: &'q str,
arguments: Option<AnyArguments<'q>>,
) -> BoxFuture<'q, crate::Result<Option<AnyRow>>>;

fn prepare_with<'c, 'q: 'c>(
&'c mut self,
sql: &'q str,
parameters: &[AnyTypeInfo],
) -> BoxFuture<'c, crate::Result<AnyStatement<'q>>>;

fn describe<'q>(&'q mut self, sql: &'q str) -> BoxFuture<'q, crate::Result<Describe<Any>>>;
}
40 changes: 0 additions & 40 deletions sqlx-core/src/any/connection/establish.rs

This file was deleted.

122 changes: 7 additions & 115 deletions sqlx-core/src/any/connection/executor.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
use crate::any::connection::AnyConnectionKind;
use crate::any::{
Any, AnyColumn, AnyConnection, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo,
};
use crate::database::Database;
use crate::any::{Any, AnyConnection, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo};
use crate::describe::Describe;
use crate::error::Error;
use crate::executor::{Execute, Executor};
use either::Either;
use futures_core::future::BoxFuture;
use futures_core::stream::BoxStream;
use futures_util::{StreamExt, TryStreamExt};

impl<'c> Executor<'c> for &'c mut AnyConnection {
type Database = Any;
@@ -20,36 +15,10 @@ impl<'c> Executor<'c> for &'c mut AnyConnection {
) -> BoxStream<'e, Result<Either<AnyQueryResult, AnyRow>, Error>>
where
'c: 'e,
E: Execute<'q, Self::Database>,
E: Execute<'q, Any>,
{
let arguments = query.take_arguments();
let query = query.sql();

match &mut self.0 {
#[cfg(feature = "postgres")]
AnyConnectionKind::Postgres(conn) => conn
.fetch_many((query, arguments.map(Into::into)))
.map_ok(|v| v.map_right(Into::into).map_left(Into::into))
.boxed(),

#[cfg(feature = "mysql")]
AnyConnectionKind::MySql(conn) => conn
.fetch_many((query, arguments.map(Into::into)))
.map_ok(|v| v.map_right(Into::into).map_left(Into::into))
.boxed(),

#[cfg(feature = "sqlite")]
AnyConnectionKind::Sqlite(conn) => conn
.fetch_many((query, arguments.map(Into::into)))
.map_ok(|v| v.map_right(Into::into).map_left(Into::into))
.boxed(),

#[cfg(feature = "mssql")]
AnyConnectionKind::Mssql(conn) => conn
.fetch_many((query, arguments.map(Into::into)))
.map_ok(|v| v.map_right(Into::into).map_left(Into::into))
.boxed(),
}
self.backend.fetch_many(query.sql(), arguments)
}

fn fetch_optional<'e, 'q: 'e, E: 'q>(
@@ -61,61 +30,18 @@ impl<'c> Executor<'c> for &'c mut AnyConnection {
E: Execute<'q, Self::Database>,
{
let arguments = query.take_arguments();
let query = query.sql();

Box::pin(async move {
Ok(match &mut self.0 {
#[cfg(feature = "postgres")]
AnyConnectionKind::Postgres(conn) => conn
.fetch_optional((query, arguments.map(Into::into)))
.await?
.map(Into::into),

#[cfg(feature = "mysql")]
AnyConnectionKind::MySql(conn) => conn
.fetch_optional((query, arguments.map(Into::into)))
.await?
.map(Into::into),

#[cfg(feature = "sqlite")]
AnyConnectionKind::Sqlite(conn) => conn
.fetch_optional((query, arguments.map(Into::into)))
.await?
.map(Into::into),

#[cfg(feature = "mssql")]
AnyConnectionKind::Mssql(conn) => conn
.fetch_optional((query, arguments.map(Into::into)))
.await?
.map(Into::into),
})
})
self.backend.fetch_optional(query.sql(), arguments)
}

fn prepare_with<'e, 'q: 'e>(
self,
sql: &'q str,
_parameters: &[AnyTypeInfo],
parameters: &[AnyTypeInfo],
) -> BoxFuture<'e, Result<AnyStatement<'q>, Error>>
where
'c: 'e,
{
Box::pin(async move {
Ok(match &mut self.0 {
// To match other databases here, we explicitly ignore the parameter types
#[cfg(feature = "postgres")]
AnyConnectionKind::Postgres(conn) => conn.prepare(sql).await.map(Into::into)?,

#[cfg(feature = "mysql")]
AnyConnectionKind::MySql(conn) => conn.prepare(sql).await.map(Into::into)?,

#[cfg(feature = "sqlite")]
AnyConnectionKind::Sqlite(conn) => conn.prepare(sql).await.map(Into::into)?,

#[cfg(feature = "mssql")]
AnyConnectionKind::Mssql(conn) => conn.prepare(sql).await.map(Into::into)?,
})
})
self.backend.prepare_with(sql, parameters)
}

fn describe<'e, 'q: 'e>(
@@ -125,40 +51,6 @@ impl<'c> Executor<'c> for &'c mut AnyConnection {
where
'c: 'e,
{
Box::pin(async move {
Ok(match &mut self.0 {
#[cfg(feature = "postgres")]
AnyConnectionKind::Postgres(conn) => conn.describe(sql).await.map(map_describe)?,

#[cfg(feature = "mysql")]
AnyConnectionKind::MySql(conn) => conn.describe(sql).await.map(map_describe)?,

#[cfg(feature = "sqlite")]
AnyConnectionKind::Sqlite(conn) => conn.describe(sql).await.map(map_describe)?,

#[cfg(feature = "mssql")]
AnyConnectionKind::Mssql(conn) => conn.describe(sql).await.map(map_describe)?,
})
})
}
}

fn map_describe<DB: Database>(info: Describe<DB>) -> Describe<Any>
where
AnyTypeInfo: From<DB::TypeInfo>,
AnyColumn: From<DB::Column>,
{
let parameters = match info.parameters {
None => None,
Some(Either::Right(num)) => Some(Either::Right(num)),
Some(Either::Left(params)) => {
Some(Either::Left(params.into_iter().map(Into::into).collect()))
}
};

Describe {
parameters,
nullable: info.nullable,
columns: info.columns.into_iter().map(Into::into).collect(),
self.backend.describe(sql)
}
}
219 changes: 48 additions & 171 deletions sqlx-core/src/any/connection/mod.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
use futures_core::future::BoxFuture;

use crate::any::{Any, AnyConnectOptions, AnyKind};
use crate::connection::Connection;
use crate::any::{Any, AnyConnectOptions};
use crate::connection::{ConnectOptions, Connection};
use crate::error::Error;

#[cfg(feature = "postgres")]
use crate::postgres;
use crate::database::Database;
pub use backend::AnyConnectionBackend;

#[cfg(feature = "sqlite")]
use crate::sqlite;

#[cfg(feature = "mssql")]
use crate::mssql;

#[cfg(feature = "mysql")]
use crate::mysql;
use crate::transaction::Transaction;

mod establish;
mod backend;
mod executor;

/// A connection to _any_ SQLx database.
@@ -30,89 +22,48 @@ mod executor;
/// sqlite://a.sqlite
/// ```
#[derive(Debug)]
pub struct AnyConnection(pub(super) AnyConnectionKind);

#[derive(Debug)]
// Used internally in `sqlx-macros`
#[doc(hidden)]
pub enum AnyConnectionKind {
#[cfg(feature = "postgres")]
Postgres(postgres::PgConnection),

#[cfg(feature = "mssql")]
Mssql(mssql::MssqlConnection),

#[cfg(feature = "mysql")]
MySql(mysql::MySqlConnection),

#[cfg(feature = "sqlite")]
Sqlite(sqlite::SqliteConnection),
}

impl AnyConnectionKind {
pub fn kind(&self) -> AnyKind {
match self {
#[cfg(feature = "postgres")]
AnyConnectionKind::Postgres(_) => AnyKind::Postgres,

#[cfg(feature = "mysql")]
AnyConnectionKind::MySql(_) => AnyKind::MySql,

#[cfg(feature = "sqlite")]
AnyConnectionKind::Sqlite(_) => AnyKind::Sqlite,

#[cfg(feature = "mssql")]
AnyConnectionKind::Mssql(_) => AnyKind::Mssql,
}
}
pub struct AnyConnection {
pub(crate) backend: Box<dyn AnyConnectionBackend>,
}

impl AnyConnection {
pub fn kind(&self) -> AnyKind {
self.0.kind()
/// Returns the name of the database backend in use (e.g. PostgreSQL, MySQL, SQLite, etc.)
pub fn backend_name(&self) -> &str {
self.backend.name()
}

// Used internally in `sqlx-macros`
#[doc(hidden)]
pub fn private_get_mut(&mut self) -> &mut AnyConnectionKind {
&mut self.0
pub(crate) fn connect(options: &AnyConnectOptions) -> BoxFuture<'_, crate::Result<Self>> {
Box::pin(async {
let driver = crate::any::driver::from_url(&options.database_url)?;
(driver.connect)(options).await
})
}
}

macro_rules! delegate_to {
($self:ident.$method:ident($($arg:ident),*)) => {
match &$self.0 {
#[cfg(feature = "postgres")]
AnyConnectionKind::Postgres(conn) => conn.$method($($arg),*),

#[cfg(feature = "mysql")]
AnyConnectionKind::MySql(conn) => conn.$method($($arg),*),

#[cfg(feature = "sqlite")]
AnyConnectionKind::Sqlite(conn) => conn.$method($($arg),*),

#[cfg(feature = "mssql")]
AnyConnectionKind::Mssql(conn) => conn.$method($($arg),*),
}
};
}

macro_rules! delegate_to_mut {
($self:ident.$method:ident($($arg:ident),*)) => {
match &mut $self.0 {
#[cfg(feature = "postgres")]
AnyConnectionKind::Postgres(conn) => conn.$method($($arg),*),
pub(crate) fn connect_with_db<DB: Database>(
options: &AnyConnectOptions,
) -> BoxFuture<'_, crate::Result<Self>>
where
DB::Connection: AnyConnectionBackend,
<DB::Connection as Connection>::Options:
for<'a> TryFrom<&'a AnyConnectOptions, Error = Error>,
{
let res = TryFrom::try_from(options);

#[cfg(feature = "mysql")]
AnyConnectionKind::MySql(conn) => conn.$method($($arg),*),
Box::pin(async {
let options: <DB::Connection as Connection>::Options = res?;

#[cfg(feature = "sqlite")]
AnyConnectionKind::Sqlite(conn) => conn.$method($($arg),*),
Ok(AnyConnection {
backend: Box::new(options.connect().await?),
})
})
}

#[cfg(feature = "mssql")]
AnyConnectionKind::Mssql(conn) => conn.$method($($arg),*),
}
};
#[cfg(feature = "migrate")]
pub(crate) fn get_migrate(
&mut self,
) -> crate::Result<&mut (dyn crate::migrate::Migrate + Send + 'static)> {
self.backend.as_migrate()
}
}

impl Connection for AnyConnection {
@@ -121,39 +72,15 @@ impl Connection for AnyConnection {
type Options = AnyConnectOptions;

fn close(self) -> BoxFuture<'static, Result<(), Error>> {
match self.0 {
#[cfg(feature = "postgres")]
AnyConnectionKind::Postgres(conn) => conn.close(),

#[cfg(feature = "mysql")]
AnyConnectionKind::MySql(conn) => conn.close(),

#[cfg(feature = "sqlite")]
AnyConnectionKind::Sqlite(conn) => conn.close(),

#[cfg(feature = "mssql")]
AnyConnectionKind::Mssql(conn) => conn.close(),
}
self.backend.close()
}

fn close_hard(self) -> BoxFuture<'static, Result<(), Error>> {
match self.0 {
#[cfg(feature = "postgres")]
AnyConnectionKind::Postgres(conn) => conn.close_hard(),

#[cfg(feature = "mysql")]
AnyConnectionKind::MySql(conn) => conn.close_hard(),

#[cfg(feature = "sqlite")]
AnyConnectionKind::Sqlite(conn) => conn.close_hard(),

#[cfg(feature = "mssql")]
AnyConnectionKind::Mssql(conn) => conn.close_hard(),
}
self.backend.close()
}

fn ping(&mut self) -> BoxFuture<'_, Result<(), Error>> {
delegate_to_mut!(self.ping())
self.backend.ping()
}

fn begin(&mut self) -> BoxFuture<'_, Result<Transaction<'_, Self::Database>, Error>>
@@ -164,74 +91,24 @@ impl Connection for AnyConnection {
}

fn cached_statements_size(&self) -> usize {
match &self.0 {
#[cfg(feature = "postgres")]
AnyConnectionKind::Postgres(conn) => conn.cached_statements_size(),

#[cfg(feature = "mysql")]
AnyConnectionKind::MySql(conn) => conn.cached_statements_size(),

#[cfg(feature = "sqlite")]
AnyConnectionKind::Sqlite(conn) => conn.cached_statements_size(),

// no cache
#[cfg(feature = "mssql")]
AnyConnectionKind::Mssql(_) => 0,
}
self.backend.cached_statements_size()
}

fn clear_cached_statements(&mut self) -> BoxFuture<'_, Result<(), Error>> {
match &mut self.0 {
#[cfg(feature = "postgres")]
AnyConnectionKind::Postgres(conn) => conn.clear_cached_statements(),

#[cfg(feature = "mysql")]
AnyConnectionKind::MySql(conn) => conn.clear_cached_statements(),

#[cfg(feature = "sqlite")]
AnyConnectionKind::Sqlite(conn) => conn.clear_cached_statements(),
fn clear_cached_statements(&mut self) -> BoxFuture<'_, crate::Result<()>> {
self.backend.clear_cached_statements()
}

// no cache
#[cfg(feature = "mssql")]
AnyConnectionKind::Mssql(_) => Box::pin(futures_util::future::ok(())),
}
fn shrink_buffers(&mut self) {
self.backend.shrink_buffers()
}

#[doc(hidden)]
fn flush(&mut self) -> BoxFuture<'_, Result<(), Error>> {
delegate_to_mut!(self.flush())
self.backend.flush()
}

#[doc(hidden)]
fn should_flush(&self) -> bool {
delegate_to!(self.should_flush())
}
}

#[cfg(feature = "postgres")]
impl From<postgres::PgConnection> for AnyConnection {
fn from(conn: postgres::PgConnection) -> Self {
AnyConnection(AnyConnectionKind::Postgres(conn))
}
}

#[cfg(feature = "mssql")]
impl From<mssql::MssqlConnection> for AnyConnection {
fn from(conn: mssql::MssqlConnection) -> Self {
AnyConnection(AnyConnectionKind::Mssql(conn))
}
}

#[cfg(feature = "mysql")]
impl From<mysql::MySqlConnection> for AnyConnection {
fn from(conn: mysql::MySqlConnection) -> Self {
AnyConnection(AnyConnectionKind::MySql(conn))
}
}

#[cfg(feature = "sqlite")]
impl From<sqlite::SqliteConnection> for AnyConnection {
fn from(conn: sqlite::SqliteConnection) -> Self {
AnyConnection(AnyConnectionKind::Sqlite(conn))
self.backend.should_flush()
}
}
3 changes: 3 additions & 0 deletions sqlx-core/src/any/database.rs
Original file line number Diff line number Diff line change
@@ -23,6 +23,9 @@ impl Database for Any {
type TypeInfo = AnyTypeInfo;

type Value = AnyValue;
const NAME: &'static str = "Any";

const URL_SCHEMES: &'static [&'static str] = &[];
}

impl<'r> HasValueRef<'r> for Any {
363 changes: 0 additions & 363 deletions sqlx-core/src/any/decode.rs

This file was deleted.

144 changes: 144 additions & 0 deletions sqlx-core/src/any/driver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
use crate::any::connection::AnyConnectionBackend;
use crate::any::{AnyConnectOptions, AnyConnection};
use crate::common::DebugFn;
use crate::connection::Connection;
use crate::database::Database;
use crate::Error;
use futures_core::future::BoxFuture;
use once_cell::sync::OnceCell;
use std::fmt::{Debug, Formatter};
use url::Url;

static DRIVERS: OnceCell<&'static [AnyDriver]> = OnceCell::new();

#[macro_export]
macro_rules! declare_driver_with_optional_migrate {
($name:ident = $db:path) => {
#[cfg(feature = "migrate")]
pub const $name: $crate::any::driver::AnyDriver =
$crate::any::driver::AnyDriver::with_migrate::<$db>();

#[cfg(not(feature = "migrate"))]
pub const $name: $crate::any::driver::AnyDriver =
$crate::any::driver::AnyDriver::without_migrate::<$db>();
};
}

#[non_exhaustive]
pub struct AnyDriver {
pub(crate) name: &'static str,
pub(crate) url_schemes: &'static [&'static str],
pub(crate) connect:
DebugFn<fn(&AnyConnectOptions) -> BoxFuture<'_, crate::Result<AnyConnection>>>,
pub(crate) migrate_database: Option<AnyMigrateDatabase>,
}

impl AnyDriver {
pub const fn without_migrate<DB: Database>() -> Self
where
DB::Connection: AnyConnectionBackend,
<DB::Connection as Connection>::Options:
for<'a> TryFrom<&'a AnyConnectOptions, Error = Error>,
{
Self {
name: DB::NAME,
url_schemes: DB::URL_SCHEMES,
connect: DebugFn(AnyConnection::connect_with_db::<DB>),
migrate_database: None,
}
}

#[cfg(not(feature = "migrate"))]
pub const fn with_migrate<DB: Database>() -> Self
where
DB::Connection: AnyConnectionBackend,
<DB::Connection as Connection>::Options:
for<'a> TryFrom<&'a AnyConnectOptions, Error = Error>,
{
Self::without_migrate::<DB>()
}

#[cfg(feature = "migrate")]
pub const fn with_migrate<DB: Database + crate::migrate::MigrateDatabase>() -> Self
where
DB::Connection: AnyConnectionBackend,
<DB::Connection as Connection>::Options:
for<'a> TryFrom<&'a AnyConnectOptions, Error = Error>,
{
Self {
migrate_database: Some(AnyMigrateDatabase {
create_database: DebugFn(DB::create_database),
database_exists: DebugFn(DB::database_exists),
drop_database: DebugFn(DB::drop_database),
}),
..Self::without_migrate::<DB>()
}
}

pub fn get_migrate_database(&self) -> crate::Result<&AnyMigrateDatabase> {
self.migrate_database.as_ref()
.ok_or_else(|| Error::Configuration(format!("{} driver does not support migrations or the `migrate` feature was not enabled for it", self.name).into()))
}
}

impl Debug for AnyDriver {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AnyDriver")
.field("name", &self.name)
.field("url_schemes", &self.url_schemes)
.finish()
}
}

pub struct AnyMigrateDatabase {
create_database: DebugFn<fn(&str) -> BoxFuture<'_, crate::Result<()>>>,
database_exists: DebugFn<fn(&str) -> BoxFuture<'_, crate::Result<bool>>>,
drop_database: DebugFn<fn(&str) -> BoxFuture<'_, crate::Result<()>>>,
}

impl AnyMigrateDatabase {
pub fn create_database<'a>(&self, url: &'a str) -> BoxFuture<'a, crate::Result<()>> {
(self.create_database)(url)
}

pub fn database_exists<'a>(&self, url: &'a str) -> BoxFuture<'a, crate::Result<bool>> {
(self.database_exists)(url)
}

pub fn drop_database<'a>(&self, url: &'a str) -> BoxFuture<'a, crate::Result<()>> {
(self.drop_database)(url)
}
}

/// Install the list of drivers for [`AnyConnection`] to use.
///
/// Must be called before an `AnyConnection` or `AnyPool` can be connected.
///
/// ### Errors
/// If called more than once.
pub fn install_drivers(
drivers: &'static [AnyDriver],
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
DRIVERS
.set(drivers)
.map_err(|_| "drivers already installed".into())
}

pub(crate) fn from_url_str(url: &str) -> crate::Result<&'static AnyDriver> {
from_url(&url.parse().map_err(Error::config)?)
}

pub(crate) fn from_url(url: &Url) -> crate::Result<&'static AnyDriver> {
let scheme = url.scheme();

let drivers: &[AnyDriver] = DRIVERS
.get()
.expect("No drivers installed. Please see the documentation in `sqlx::any` for details.");

drivers
.iter()
.find(|driver| driver.url_schemes.contains(&url.scheme()))
.ok_or_else(|| {
Error::Configuration(format!("no driver found for URL scheme {:?}", scheme).into())
})
}
Loading