Skip to content

Commit

Permalink
SQLite extension loading via sqlx.toml for CLI and query macros
Browse files Browse the repository at this point in the history
  • Loading branch information
djarb committed Jan 29, 2025
1 parent 28b6450 commit b5da0d7
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 2 deletions.
2 changes: 1 addition & 1 deletion sqlx-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ pub async fn run(opt: Opt) -> Result<()> {

/// Attempt to connect to the database server, retrying up to `ops.connect_timeout`.
async fn connect(opts: &ConnectOpts) -> anyhow::Result<AnyConnection> {
retry_connect_errors(opts, AnyConnection::connect).await
retry_connect_errors(opts, AnyConnection::connect_with_config).await
}

/// Attempt an operation that may return errors like `ConnectionRefused`,
Expand Down
14 changes: 14 additions & 0 deletions sqlx-core/src/any/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@ impl AnyConnection {
})
}

/// UNSTABLE: for use with `sqlx-cli`
///
/// Connect to the database, and instruct the nested driver to
/// read options from the sqlx.toml file as appropriate.
#[doc(hidden)]
pub fn connect_with_config(url: &str) -> BoxFuture<'static, Result<Self, Error>>
where
Self: Sized,
{
let options: Result<AnyConnectOptions, Error> = url.parse();

Box::pin(async move { Self::connect_with(&options?.allow_config_file()).await })
}

pub(crate) fn connect_with_db<DB: Database>(
options: &AnyConnectOptions,
) -> BoxFuture<'_, crate::Result<Self>>
Expand Down
15 changes: 15 additions & 0 deletions sqlx-core/src/any/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use url::Url;
pub struct AnyConnectOptions {
pub database_url: Url,
pub log_settings: LogSettings,
pub enable_config: bool,
}
impl FromStr for AnyConnectOptions {
type Err = Error;
Expand All @@ -29,6 +30,7 @@ impl FromStr for AnyConnectOptions {
.parse::<Url>()
.map_err(|e| Error::Configuration(e.into()))?,
log_settings: LogSettings::default(),
enable_config: false,
})
}
}
Expand All @@ -40,6 +42,7 @@ impl ConnectOptions for AnyConnectOptions {
Ok(AnyConnectOptions {
database_url: url.clone(),
log_settings: LogSettings::default(),
enable_config: false,
})
}

Expand All @@ -63,3 +66,15 @@ impl ConnectOptions for AnyConnectOptions {
self
}
}

impl AnyConnectOptions {
/// UNSTABLE: for use with `sqlx-cli`
///
/// Allow nested drivers to extract configuration information from
/// the sqlx.toml file.
#[doc(hidden)]
pub fn allow_config_file(mut self) -> Self {
self.enable_config = true;
self
}
}
39 changes: 39 additions & 0 deletions sqlx-core/src/config/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,49 @@ pub struct Config {
/// The query macros used in `foo` will use `FOO_DATABASE_URL`,
/// and the ones used in `bar` will use `BAR_DATABASE_URL`.
pub database_url_var: Option<String>,

/// Settings for specific database drivers.
///
/// These settings apply when checking queries, or when applying
/// migrations via `sqlx-cli`. These settings *do not* apply when
/// applying migrations via the macro, as that uses the run-time
/// database connection configured by the application.
pub drivers: Drivers,
}

impl Config {
pub fn database_url_var(&self) -> &str {
self.database_url_var.as_deref().unwrap_or("DATABASE_URL")
}
}

/// Configuration for specific database drivers.
#[derive(Debug, Default)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(default, rename_all = "kebab-case")
)]
pub struct Drivers {
/// Specify options for the SQLite driver.
pub sqlite: SQLite,
}

/// Configuration for the SQLite database driver.
#[derive(Debug, Default)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(default, rename_all = "kebab-case")
)]
pub struct SQLite {
/// Specify extensions to load.
///
/// ### Example: Load the "uuid" and "vsv" extensions
/// `sqlx.toml`:
/// ```toml
/// [common.drivers.sqlite]
/// load-extensions = ["uuid", "vsv"]
/// ```
pub load_extensions: Vec<String>,
}
6 changes: 6 additions & 0 deletions sqlx-core/src/config/reference.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
# If not specified, defaults to `DATABASE_URL`
database-url-var = "FOO_DATABASE_URL"

[common.drivers.sqlite]
# Load extensions into SQLite when running macros or migrations
#
# Defaults to an empty list, which has no effect.
load-extensions = ["uuid", "vsv"]

###############################################################################################

# Configuration for the `query!()` family of macros.
Expand Down
8 changes: 8 additions & 0 deletions sqlx-sqlite/src/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,14 @@ impl<'a> TryFrom<&'a AnyConnectOptions> for SqliteConnectOptions {
fn try_from(opts: &'a AnyConnectOptions) -> Result<Self, Self::Error> {
let mut opts_out = SqliteConnectOptions::from_url(&opts.database_url)?;
opts_out.log_settings = opts.log_settings.clone();

if opts.enable_config {
let config = sqlx_core::config::Config::from_crate();
for extension in config.common.drivers.sqlite.load_extensions.iter() {
opts_out = opts_out.extension(extension);
}
}

Ok(opts_out)
}
}
Expand Down
9 changes: 8 additions & 1 deletion sqlx-sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,15 @@ pub static CREATE_DB_WAL: AtomicBool = AtomicBool::new(true);
/// UNSTABLE: for use by `sqlite-macros-core` only.
#[doc(hidden)]
pub fn describe_blocking(query: &str, database_url: &str) -> Result<Describe<Sqlite>, Error> {
let opts: SqliteConnectOptions = database_url.parse()?;
let mut opts: SqliteConnectOptions = database_url.parse()?;

let config = sqlx_core::config::Config::from_crate();
for extension in config.common.drivers.sqlite.load_extensions.iter() {
opts = opts.extension(extension);
}

let params = EstablishParams::from_options(&opts)?;

let mut conn = params.establish()?;

// Execute any ancillary `PRAGMA`s
Expand Down

0 comments on commit b5da0d7

Please sign in to comment.