/
germanubis
/
sqlx
Обзор
Документация
Войти
/
germanubis
/
sqlx
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
sqlx-core/src/config/macros.rs
418 строк
15 KB
Austin Bonander
feat: create `sqlx.toml` format (#3383)
01 июл 2025, 02:34
Не верифицирован
01 июл 2025, 02:34
25cbeed
Код
Авторство
О чём код?
use std::collections::BTreeMap; /// Configuration for the `query!()` family of macros. /// /// See also [`common::Config`][crate::config::common::Config] for renaming `DATABASE_URL`. #[derive(Debug, Default)] #[cfg_attr( feature = "sqlx-toml", derive(serde::Deserialize), serde(default, rename_all = "kebab-case", deny_unknown_fields) )] pub struct Config { /// Specify which crates' types to use when types from multiple crates apply. /// /// See [`PreferredCrates`] for details. pub preferred_crates: PreferredCrates, /// Specify global overrides for mapping SQL type names to Rust type names. /// /// Default type mappings are defined by the database driver. /// Refer to the `sqlx::types` module for details. /// /// ## Note: Case-Sensitive /// Currently, the case of the type name MUST match the name SQLx knows it by. /// Built-in types are spelled in all-uppercase to match SQL convention. /// /// However, user-created types in Postgres are all-lowercase unless quoted. /// /// ## Note: Orthogonal to Nullability /// These overrides do not affect whether `query!()` decides to wrap a column in `Option<_>` /// or not. They only override the inner type used. /// /// ## Note: Schema Qualification (Postgres) /// Type names may be schema-qualified in Postgres. If so, the schema should be part /// of the type string, e.g. `'foo.bar'` to reference type `bar` in schema `foo`. /// /// The schema and/or type name may additionally be quoted in the string /// for a quoted identifier (see next section). /// /// Schema qualification should not be used for types in the search path. /// /// ## Note: Quoted Identifiers (Postgres) /// Type names using [quoted identifiers in Postgres] must also be specified with quotes here. /// /// Note, however, that the TOML format parses way the outer pair of quotes, /// so for quoted names in Postgres, double-quoting is necessary, /// e.g. `'"Foo"'` for SQL type `"Foo"`. /// /// To reference a schema-qualified type with a quoted name, use double-quotes after the /// dot, e.g. `'foo."Bar"'` to reference type `"Bar"` of schema `foo`, and vice versa for /// quoted schema names. /// /// We recommend wrapping all type names in single quotes, as shown below, /// to avoid confusion. /// /// MySQL/MariaDB and SQLite do not support custom types, so quoting type names should /// never be necessary. /// /// [quoted identifiers in Postgres]: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS // Note: we wanted to be able to handle this intelligently, // but the `toml` crate authors weren't interested: https://github.com/toml-rs/toml/issues/761 // // We decided to just encourage always quoting type names instead. /// Example: Custom Wrapper Types /// ------- /// Does SQLx not support a type that you need? Do you want additional semantics not /// implemented on the built-in types? You can create a custom wrapper, /// or use an external crate. /// /// #### `sqlx.toml` /// ```toml /// [macros.type-overrides] /// # Override a built-in type /// 'UUID' = "crate::types::MyUuid" /// /// # Support an external or custom wrapper type (e.g. from the `isn` Postgres extension) /// # (NOTE: FOR DOCUMENTATION PURPOSES ONLY; THIS CRATE/TYPE DOES NOT EXIST AS OF WRITING) /// 'isbn13' = "isn_rs::sqlx::ISBN13" /// ``` /// /// Example: Custom Types in Postgres /// ------- /// If you have a custom type in Postgres that you want to map without needing to use /// the type override syntax in `sqlx::query!()` every time, you can specify a global /// override here. /// /// For example, a custom enum type `foo`: /// /// #### Migration or Setup SQL (e.g. `migrations/0_setup.sql`) /// ```sql /// CREATE TYPE foo AS ENUM ('Bar', 'Baz'); /// ``` /// /// #### `src/types.rs` /// ```rust,no_run /// #[derive(sqlx::Type)] /// pub enum Foo { /// Bar, /// Baz /// } /// ``` /// /// If you're not using `PascalCase` in your enum variants then you'll want to use /// `#[sqlx(rename_all = "<strategy>")]` on your enum. /// See [`Type`][crate::type::Type] for details. /// /// #### `sqlx.toml` /// ```toml /// [macros.type-overrides] /// # Map SQL type `foo` to `crate::types::Foo` /// 'foo' = "crate::types::Foo" /// ``` /// /// Example: Schema-Qualified Types /// ------- /// (See `Note` section above for details.) /// /// ```toml /// [macros.type-overrides] /// # Map SQL type `foo.foo` to `crate::types::Foo` /// 'foo.foo' = "crate::types::Foo" /// ``` /// /// Example: Quoted Identifiers /// ------- /// If a type or schema uses quoted identifiers, /// it must be wrapped in quotes _twice_ for SQLx to know the difference: /// /// ```toml /// [macros.type-overrides] /// # `"Foo"` in SQLx /// '"Foo"' = "crate::types::Foo" /// # **NOT** `"Foo"` in SQLx (parses as just `Foo`) /// "Foo" = "crate::types::Foo" /// /// # Schema-qualified /// '"foo".foo' = "crate::types::Foo" /// 'foo."Foo"' = "crate::types::Foo" /// '"foo"."Foo"' = "crate::types::Foo" /// ``` /// /// (See `Note` section above for details.) // TODO: allow specifying different types for input vs output // e.g. to accept `&[T]` on input but output `Vec<T>` pub type_overrides: BTreeMap<SqlType, RustType>, /// Specify per-table and per-column overrides for mapping SQL types to Rust types. /// /// Default type mappings are defined by the database driver. /// Refer to the `sqlx::types` module for details. /// /// The supported syntax is similar to [`type_overrides`][Self::type_overrides], /// (with the same caveat for quoted names!) but column names must be qualified /// by a separately quoted table name, which may optionally be schema-qualified. /// /// Multiple columns for the same SQL table may be written in the same table in TOML /// (see examples below). /// /// ## Note: Orthogonal to Nullability /// These overrides do not affect whether `query!()` decides to wrap a column in `Option<_>` /// or not. They only override the inner type used. /// /// ## Note: Schema Qualification /// Table names may be schema-qualified. If so, the schema should be part /// of the table name string, e.g. `'foo.bar'` to reference table `bar` in schema `foo`. /// /// The schema and/or type name may additionally be quoted in the string /// for a quoted identifier (see next section). /// /// Postgres users: schema qualification should not be used for tables in the search path. /// /// ## Note: Quoted Identifiers /// Schema, table, or column names using quoted identifiers ([MySQL], [Postgres], [SQLite]) /// in SQL must also be specified with quotes here. /// /// Postgres and SQLite use double-quotes (`"Foo"`) while MySQL uses backticks (`\`Foo\`). /// /// Note, however, that the TOML format parses way the outer pair of quotes, /// so for quoted names in Postgres, double-quoting is necessary, /// e.g. `'"Foo"'` for SQL name `"Foo"`. /// /// To reference a schema-qualified table with a quoted name, use the appropriate quotation /// characters after the dot, e.g. `'foo."Bar"'` to reference table `"Bar"` of schema `foo`, /// and vice versa for quoted schema names. /// /// We recommend wrapping all table and column names in single quotes, as shown below, /// to avoid confusion. /// /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/identifiers.html /// [Postgres]: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS /// [SQLite]: https://sqlite.org/lang_keywords.html // Note: we wanted to be able to handle this intelligently, // but the `toml` crate authors weren't interested: https://github.com/toml-rs/toml/issues/761 // // We decided to just encourage always quoting type names instead. /// /// Example /// ------- /// /// #### `sqlx.toml` /// ```toml /// [macros.table-overrides.'foo'] /// # Map column `bar` of table `foo` to Rust type `crate::types::Foo`: /// 'bar' = "crate::types::Bar" /// /// # Quoted column name /// # Note: same quoting requirements as `macros.type_overrides` /// '"Bar"' = "crate::types::Bar" /// /// # Note: will NOT work (parses as `Bar`) /// # "Bar" = "crate::types::Bar" /// /// # Table name may be quoted (note the wrapping single-quotes) /// [macros.table-overrides.'"Foo"'] /// 'bar' = "crate::types::Bar" /// '"Bar"' = "crate::types::Bar" /// /// # Table name may also be schema-qualified. /// # Note how the dot is inside the quotes. /// [macros.table-overrides.'my_schema.my_table'] /// 'my_column' = "crate::types::MyType" /// /// # Quoted schema, table, and column names /// [macros.table-overrides.'"My Schema"."My Table"'] /// '"My Column"' = "crate::types::MyType" /// ``` pub table_overrides: BTreeMap<TableName, BTreeMap<ColumnName, RustType>>, } #[derive(Debug, Default)] #[cfg_attr( feature = "sqlx-toml", derive(serde::Deserialize), serde(default, rename_all = "kebab-case") )] pub struct PreferredCrates { /// Specify the crate to use for mapping date/time types to Rust. /// /// The default behavior is to use whatever crate is enabled, /// [`chrono`] or [`time`] (the latter takes precedent). /// /// [`chrono`]: crate::types::chrono /// [`time`]: crate::types::time /// /// Example: Always Use Chrono /// ------- /// Thanks to Cargo's [feature unification], a crate in the dependency graph may enable /// the `time` feature of SQLx which will force it on for all crates using SQLx, /// which will result in problems if your crate wants to use types from [`chrono`]. /// /// You can use the type override syntax (see `sqlx::query!` for details), /// or you can force an override globally by setting this option. /// /// #### `sqlx.toml` /// ```toml /// [macros.preferred-crates] /// date-time = "chrono" /// ``` /// /// [feature unification]: https://doc.rust-lang.org/cargo/reference/features.html#feature-unification pub date_time: DateTimeCrate, /// Specify the crate to use for mapping `NUMERIC` types to Rust. /// /// The default behavior is to use whatever crate is enabled, /// [`bigdecimal`] or [`rust_decimal`] (the latter takes precedent). /// /// [`bigdecimal`]: crate::types::bigdecimal /// [`rust_decimal`]: crate::types::rust_decimal /// /// Example: Always Use `bigdecimal` /// ------- /// Thanks to Cargo's [feature unification], a crate in the dependency graph may enable /// the `rust_decimal` feature of SQLx which will force it on for all crates using SQLx, /// which will result in problems if your crate wants to use types from [`bigdecimal`]. /// /// You can use the type override syntax (see `sqlx::query!` for details), /// or you can force an override globally by setting this option. /// /// #### `sqlx.toml` /// ```toml /// [macros.preferred-crates] /// numeric = "bigdecimal" /// ``` /// /// [feature unification]: https://doc.rust-lang.org/cargo/reference/features.html#feature-unification pub numeric: NumericCrate, } /// The preferred crate to use for mapping date/time types to Rust. #[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] Inferred, /// Always use types from [`chrono`][crate::types::chrono]. /// /// ```toml /// [macros.preferred-crates] /// date-time = "chrono" /// ``` Chrono, /// Always use types from [`time`][crate::types::time]. /// /// ```toml /// [macros.preferred-crates] /// date-time = "time" /// ``` Time, } /// The preferred crate to use for mapping `NUMERIC` types to Rust. #[derive(Debug, Default, PartialEq, Eq)] #[cfg_attr( feature = "sqlx-toml", derive(serde::Deserialize), serde(rename_all = "snake_case") )] pub enum NumericCrate { /// Use whichever crate is enabled (`rust_decimal` then `bigdecimal`). #[default] Inferred, /// Always use types from [`bigdecimal`][crate::types::bigdecimal]. /// /// ```toml /// [macros.preferred-crates] /// numeric = "bigdecimal" /// ``` #[cfg_attr(feature = "sqlx-toml", serde(rename = "bigdecimal"))] BigDecimal, /// Always use types from [`rust_decimal`][crate::types::rust_decimal]. /// /// ```toml /// [macros.preferred-crates] /// numeric = "rust_decimal" /// ``` RustDecimal, } /// A SQL type name; may optionally be schema-qualified. /// /// See [`macros.type-overrides`][Config::type_overrides] for usages. pub type SqlType = Box<str>; /// A SQL table name; may optionally be schema-qualified. /// /// See [`macros.table-overrides`][Config::table_overrides] for usages. pub type TableName = Box<str>; /// A column in a SQL table. /// /// See [`macros.table-overrides`][Config::table_overrides] for usages. pub type ColumnName = Box<str>; /// A Rust type name or path. /// /// Should be a global path (not relative). pub type RustType = Box<str>; /// Internal getter methods. impl Config { /// Get the override for a given type name (optionally schema-qualified). pub fn type_override(&self, type_name: &str) -> Option<&str> { // TODO: make this case-insensitive self.type_overrides.get(type_name).map(|s| &**s) } /// Get the override for a given column and table name (optionally schema-qualified). pub fn column_override(&self, table: &str, column: &str) -> Option<&str> { self.table_overrides .get(table) .and_then(|by_column| by_column.get(column)) .map(|s| &**s) } } impl DateTimeCrate { /// Returns `self == Self::Inferred` #[inline(always)] pub fn is_inferred(&self) -> bool { *self == Self::Inferred } #[inline(always)] pub fn crate_name(&self) -> Option<&str> { match self { Self::Inferred => None, Self::Chrono => Some("chrono"), Self::Time => Some("time"), } } } impl NumericCrate { /// Returns `self == Self::Inferred` #[inline(always)] pub fn is_inferred(&self) -> bool { *self == Self::Inferred } #[inline(always)] pub fn crate_name(&self) -> Option<&str> { match self { Self::Inferred => None, Self::BigDecimal => Some("bigdecimal"), Self::RustDecimal => Some("rust_decimal"), } } }