Skip to content

(BLOCKED: merge after 0.9.0) Add example for jiff-sqlx using sqlx.toml #3818

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 41 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
afa3a93
feat: create `sqlx.toml` format
abonander Jul 26, 2024
062a06f
feat: add support for ignored_chars config to sqlx_core::migrate
abonander Sep 9, 2024
9f34fc8
chore: test ignored_chars with `U+FEFF` (ZWNBSP/BOM)
abonander Sep 9, 2024
e775d2a
refactor: make `Config` always compiled
abonander Sep 18, 2024
bf90a47
refactor: add origin information to `Column`
abonander Sep 18, 2024
5cb3de3
feat(macros): implement `type_override` and `column_override` from `s…
abonander Sep 19, 2024
8604b51
refactor(sqlx.toml): make all keys kebab-case, create `macros.preferr…
abonander Sep 20, 2024
13f6ef0
feat: make macros aware of `macros.preferred-crates`
abonander Sep 20, 2024
65ef27f
feat: make `sqlx-cli` aware of `database-url-var`
abonander Sep 20, 2024
9d1bc64
feat: teach macros about `migrate.table-name`, `migrations-dir`
abonander Sep 23, 2024
ba7740d
feat: teach macros about `migrate.ignored-chars`
abonander Sep 23, 2024
e951d8e
chore: delete unused source file `sqlx-cli/src/migration.rs`
abonander Oct 5, 2024
367f2cc
feat: teach `sqlx-cli` about `migrate.defaults`
abonander Oct 5, 2024
1ff6a8a
feat: teach `sqlx-cli` about `migrate.migrations-dir`
abonander Jan 15, 2025
45c0b85
feat: teach `sqlx-cli` about `migrate.table-name`
abonander Jan 22, 2025
3765f67
feat: introduce `migrate.create-schemas`
abonander Jan 22, 2025
017ffce
Merge branch 'main' into sqlx-toml
abonander Jan 22, 2025
28b6450
WIP feat: create multi-tenant database example
abonander Jan 26, 2025
7d646a9
fix(postgres): don't fetch `ColumnOrigin` for transparently-prepared …
abonander Feb 2, 2025
c2b9f87
feat: progress on axum-multi-tenant example
abonander Feb 2, 2025
d9fc489
feat(config): better errors for mislabeled fields
abonander Feb 21, 2025
0b79b51
WIP feat: filling out axum-multi-tenant example
abonander Feb 26, 2025
46878e8
feat: multi-tenant example
abonander Feb 28, 2025
1b0c64a
chore(ci): test multi-tenant example
abonander Feb 28, 2025
8429f2e
Merge remote-tracking branch 'origin/main' into sqlx-toml
abonander Feb 28, 2025
f4d22fb
fixup after merge
abonander Feb 28, 2025
15df159
fix(ci): enable `sqlx-toml` in CLI build for examples
abonander Feb 28, 2025
4fb7102
fix: CI, README for `multi-tenant`
abonander Feb 28, 2025
2b69150
fix: clippy warnings
abonander Feb 28, 2025
a9a4d00
fix: multi-tenant README
abonander Feb 28, 2025
ac7f270
fix: sequential versioning inference for migrations
abonander Feb 28, 2025
8ddcd06
fix: migration versioning with explicit overrides
abonander Feb 28, 2025
4e95baf
Merge remote-tracking branch 'origin/main' into sqlx-toml
abonander Mar 29, 2025
732233a
fix: only warn on ambiguous crates if the invocation relies on it
abonander Mar 30, 2025
0a32d01
fix: remove unused imports
abonander Mar 30, 2025
1eb0f49
fix: doctest
abonander Mar 31, 2025
52bb9d0
fix: `sqlx mig add` behavior and tests
abonander Mar 31, 2025
80abc5b
fix: restore original type-checking order
abonander Mar 31, 2025
b989b36
fix: deprecation warning in `tests/postgres/macros.rs`
abonander Mar 31, 2025
db6d28b
feat: create postgres/multi-database example
abonander Apr 11, 2025
febc62a
feat: create postgres/jiff-sqlx example
abonander Apr 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor: make Config always compiled
simplifies usage while still making parsing optional for less generated code
  • Loading branch information
abonander committed Jan 7, 2025
commit e775d2a3eb1e6a70ddc78d12e49b0854edf1dd99
10 changes: 4 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,14 @@ features = ["all-databases", "_unstable-all-types", "_unstable-doc"]
rustdoc-args = ["--cfg", "docsrs"]

[features]
default = ["any", "macros", "migrate", "json", "config-all"]
default = ["any", "macros", "migrate", "json", "sqlx-toml"]

derive = ["sqlx-macros/derive"]
macros = ["derive", "sqlx-macros/macros"]
migrate = ["sqlx-core/migrate", "sqlx-macros?/migrate", "sqlx-mysql?/migrate", "sqlx-postgres?/migrate", "sqlx-sqlite?/migrate"]

# Enable parsing of `sqlx.toml` for configuring macros, migrations, or both.
config-macros = ["sqlx-macros?/config-macros"]
config-migrate = ["sqlx-macros?/config-migrate"]
config-all = ["config-macros", "config-migrate"]
# Enable parsing of `sqlx.toml` for configuring macros and migrations.
sqlx-toml = ["sqlx-core/sqlx-toml", "sqlx-macros?/sqlx-toml"]

# intended mainly for CI and docs
all-databases = ["mysql", "sqlite", "postgres", "any"]
Expand All @@ -79,7 +77,7 @@ _unstable-all-types = [
"bit-vec",
]
# Render documentation that wouldn't otherwise be shown (e.g. `sqlx_core::config`).
_unstable-doc = ["config-all", "sqlx-core/_unstable-doc"]
_unstable-doc = []

# Base runtime features without TLS
runtime-async-std = ["_rt-async-std", "sqlx-core/_rt-async-std", "sqlx-macros?/_rt-async-std"]
Expand Down
5 changes: 4 additions & 1 deletion sqlx-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ filetime = "0.2"
backoff = { version = "0.4.0", features = ["futures", "tokio"] }

[features]
default = ["postgres", "sqlite", "mysql", "native-tls", "completions"]
default = ["postgres", "sqlite", "mysql", "native-tls", "completions", "sqlx-toml"]

rustls = ["sqlx/runtime-tokio-rustls"]
native-tls = ["sqlx/runtime-tokio-native-tls"]

Expand All @@ -64,6 +65,8 @@ openssl-vendored = ["openssl/vendored"]

completions = ["dep:clap_complete"]

sqlx-toml = ["sqlx/sqlx-toml"]

[dev-dependencies]
assert_cmd = "2.0.11"
tempfile = "3.10.1"
Expand Down
12 changes: 7 additions & 5 deletions sqlx-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ features = ["offline"]

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

any = []

Expand All @@ -31,11 +31,13 @@ _tls-none = []
# support offline/decoupled building (enables serialization of `Describe`)
offline = ["serde", "either/serde"]

config = ["serde", "toml/parse"]
config-macros = ["config"]
config-migrate = ["config"]
# Enable parsing of `sqlx.toml`.
# For simplicity, the `config` module is always enabled,
# but disabling this disables the `serde` derives and the `toml` crate,
# which is a good bit less code to compile if the feature isn't being used.
sqlx-toml = ["serde", "toml/parse"]

_unstable-doc = ["config-macros", "config-migrate"]
_unstable-doc = ["sqlx-toml"]

[dependencies]
# Runtimes
Expand Down
9 changes: 8 additions & 1 deletion sqlx-core/src/config/common.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// Configuration shared by multiple components.
#[derive(Debug, Default, serde::Deserialize)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize))]
pub struct Config {
/// Override the database URL environment variable.
///
Expand Down Expand Up @@ -36,3 +37,9 @@ pub struct Config {
/// and the ones used in `bar` will use `BAR_DATABASE_URL`.
pub database_url_var: Option<String>,
}

impl Config {
pub fn database_url_var(&self) -> &str {
self.database_url_var.as_deref().unwrap_or("DATABASE_URL")
}
}
12 changes: 8 additions & 4 deletions sqlx-core/src/config/macros.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::collections::BTreeMap;

/// Configuration for the `query!()` family of macros.
#[derive(Debug, Default, serde::Deserialize)]
#[serde(default)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize), serde(default))]
pub struct Config {
/// Specify the crate to use for mapping date/time types to Rust.
///
Expand Down Expand Up @@ -235,8 +235,12 @@ pub struct Config {
}

/// The crate to use for mapping date/time types to Rust.
#[derive(Debug, Default, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub enum DateTimeCrate {
/// Use whichever crate is enabled (`time` then `chrono`).
#[default]
Expand Down
20 changes: 14 additions & 6 deletions sqlx-core/src/config/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use std::collections::BTreeSet;
/// if the proper precautions are not taken.
///
/// Be sure you know what you are doing and that you read all relevant documentation _thoroughly_.
#[derive(Debug, Default, serde::Deserialize)]
#[serde(default)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize), serde(default))]
pub struct Config {
/// Override the name of the table used to track executed migrations.
///
Expand Down Expand Up @@ -118,8 +118,12 @@ pub struct Config {
}

/// The default type of migration that `sqlx migrate create` should create by default.
#[derive(Debug, Default, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub enum DefaultMigrationType {
/// Create the same migration type as that of the latest existing migration,
/// or `Simple` otherwise.
Expand All @@ -134,8 +138,12 @@ pub enum DefaultMigrationType {
}

/// The default scheme that `sqlx migrate create` should use for version integers.
#[derive(Debug, Default, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub enum DefaultVersioning {
/// Infer the versioning scheme from existing migrations:
///
Expand Down
95 changes: 63 additions & 32 deletions sqlx-core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//!
//! See the [reference][`_reference`] for the full `sqlx.toml` file.

use std::error::Error;
use std::fmt::Debug;
use std::io;
use std::path::{Path, PathBuf};
Expand All @@ -23,13 +24,11 @@ pub mod common;
/// Configuration for the `query!()` family of macros.
///
/// See [`macros::Config`] for details.
#[cfg(feature = "config-macros")]
pub mod macros;

/// Configuration for migrations when executed using `sqlx::migrate!()` or through `sqlx-cli`.
///
/// See [`migrate::Config`] for details.
#[cfg(feature = "config-migrate")]
pub mod migrate;

/// Reference for `sqlx.toml` files
Expand All @@ -41,11 +40,12 @@ pub mod migrate;
/// ```
pub mod _reference {}

#[cfg(test)]
#[cfg(all(test, feature = "sqlx-toml"))]
mod tests;

/// The parsed structure of a `sqlx.toml` file.
#[derive(Debug, Default, serde::Deserialize)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize))]
pub struct Config {
/// Configuration shared by multiple components.
///
Expand All @@ -55,21 +55,11 @@ pub struct Config {
/// Configuration for the `query!()` family of macros.
///
/// See [`macros::Config`] for details.
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "config-all", feature = "config-macros")))
)]
#[cfg(feature = "config-macros")]
pub macros: macros::Config,

/// Configuration for migrations when executed using `sqlx::migrate!()` or through `sqlx-cli`.
///
/// See [`migrate::Config`] for details.
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "config-all", feature = "config-migrate")))
)]
#[cfg(feature = "config-migrate")]
pub migrate: migrate::Config,
}

Expand All @@ -90,13 +80,17 @@ pub enum ConfigError {
std::env::VarError,
),

/// No configuration file was found. Not necessarily fatal.
#[error("config file {path:?} not found")]
NotFound {
path: PathBuf,
},

/// An I/O error occurred while attempting to read the config file at `path`.
///
/// This includes [`io::ErrorKind::NotFound`].
///
/// [`Self::not_found_path()`] will return the path if the file was not found.
/// If the error is [`io::ErrorKind::NotFound`], [`Self::NotFound`] is returned instead.
#[error("error reading config file {path:?}")]
Read {
Io {
path: PathBuf,
#[source]
error: io::Error,
Expand All @@ -105,22 +99,41 @@ pub enum ConfigError {
/// An error in the TOML was encountered while parsing the config file at `path`.
///
/// The error gives line numbers and context when printed with `Display`/`ToString`.
///
/// Only returned if the `sqlx-toml` feature is enabled.
#[error("error parsing config file {path:?}")]
Parse {
path: PathBuf,
/// Type-erased [`toml::de::Error`].
#[source]
error: toml::de::Error,
error: Box<dyn Error + Send + Sync + 'static>,
},

/// A `sqlx.toml` file was found or specified, but the `sqlx-toml` feature is not enabled.
#[error("SQLx found config file at {path:?} but the `sqlx-toml` feature was not enabled")]
ParseDisabled {
path: PathBuf
},
}

impl ConfigError {
/// Create a [`ConfigError`] from a [`std::io::Error`].
///
/// Maps to either `NotFound` or `Io`.
pub fn from_io(path: PathBuf, error: io::Error) -> Self {
if error.kind() == io::ErrorKind::NotFound {
Self::NotFound { path }
} else {
Self::Io { path, error }
}
}

/// If this error means the file was not found, return the path that was attempted.
pub fn not_found_path(&self) -> Option<&Path> {
match self {
ConfigError::Read { path, error } if error.kind() == io::ErrorKind::NotFound => {
Some(path)
}
_ => None,
if let Self::NotFound { path } = self {
Some(path)
} else {
None
}
}
}
Expand All @@ -140,14 +153,22 @@ impl Config {
/// If the file exists but an unrecoverable error was encountered while parsing it.
pub fn from_crate() -> &'static Self {
Self::try_from_crate().unwrap_or_else(|e| {
if let Some(path) = e.not_found_path() {
// Non-fatal
tracing::debug!("Not reading config, file {path:?} not found (error: {e})");
CACHE.get_or_init(Config::default)
} else {
match e {
ConfigError::NotFound { path } => {
// Non-fatal
tracing::debug!("Not reading config, file {path:?} not found");
CACHE.get_or_init(Config::default)
}
// FATAL ERRORS BELOW:
// In the case of migrations,
// we can't proceed with defaults as they may be completely wrong.
panic!("failed to read sqlx config: {e}")
e @ ConfigError::ParseDisabled { .. } => {
// Only returned if the file exists but the feature is not enabled.
panic!("{e}")
}
e => {
panic!("failed to read sqlx config: {e}")
}
}
})
}
Expand Down Expand Up @@ -188,19 +209,29 @@ impl Config {
})
}

#[cfg(feature = "sqlx-toml")]
fn read_from(path: PathBuf) -> Result<Self, ConfigError> {
// The `toml` crate doesn't provide an incremental reader.
let toml_s = match std::fs::read_to_string(&path) {
Ok(toml) => toml,
Err(error) => {
return Err(ConfigError::Read { path, error });
return Err(ConfigError::from_io(path, error));
}
};

// TODO: parse and lint TOML structure before deserializing
// Motivation: https://github.com/toml-rs/toml/issues/761
tracing::debug!("read config TOML from {path:?}:\n{toml_s}");

toml::from_str(&toml_s).map_err(|error| ConfigError::Parse { path, error })
toml::from_str(&toml_s).map_err(|error| ConfigError::Parse { path, error: Box::new(error) })
}

#[cfg(not(feature = "sqlx-toml"))]
fn read_from(path: PathBuf) -> Result<Self, ConfigError> {
match path.try_exists() {
Ok(true) => Err(ConfigError::ParseDisabled { path }),
Ok(false) => Err(ConfigError::NotFound { path }),
Err(e) => Err(ConfigError::from_io(path, e))
}
}
}
2 changes: 0 additions & 2 deletions sqlx-core/src/config/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ fn assert_common_config(config: &config::common::Config) {
assert_eq!(config.database_url_var.as_deref(), Some("FOO_DATABASE_URL"));
}

#[cfg(feature = "config-macros")]
fn assert_macros_config(config: &config::macros::Config) {
use config::macros::*;

Expand Down Expand Up @@ -74,7 +73,6 @@ fn assert_macros_config(config: &config::macros::Config) {
);
}

#[cfg(feature = "config-migrate")]
fn assert_migrate_config(config: &config::migrate::Config) {
use config::migrate::*;

Expand Down
1 change: 0 additions & 1 deletion sqlx-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ pub mod any;
#[cfg(feature = "migrate")]
pub mod testing;

#[cfg(feature = "config")]
pub mod config;

pub use error::{Error, Result};
Expand Down
4 changes: 1 addition & 3 deletions sqlx-macros-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ derive = []
macros = []
migrate = ["sqlx-core/migrate"]

config = ["sqlx-core/config"]
config-macros = ["config", "sqlx-core/config-macros"]
config-migrate = ["config", "sqlx-core/config-migrate"]
sqlx-toml = ["sqlx-core/sqlx-toml"]

# database
mysql = ["sqlx-mysql"]
Expand Down
Loading