sqlx_core/config/macros.rs
1use std::collections::BTreeMap;
2
3/// Configuration for the `query!()` family of macros.
4///
5/// See also [`common::Config`][crate::config::common::Config] for renaming `DATABASE_URL`.
6#[derive(Debug, Default)]
7#[cfg_attr(
8    feature = "sqlx-toml",
9    derive(serde::Deserialize),
10    serde(default, rename_all = "kebab-case", deny_unknown_fields)
11)]
12pub struct Config {
13    /// Specify which crates' types to use when types from multiple crates apply.
14    ///
15    /// See [`PreferredCrates`] for details.
16    pub preferred_crates: PreferredCrates,
17
18    /// Specify global overrides for mapping SQL type names to Rust type names.
19    ///
20    /// Default type mappings are defined by the database driver.
21    /// Refer to the `sqlx::types` module for details.
22    ///
23    /// ## Note: Case-Sensitive
24    /// Currently, the case of the type name MUST match the name SQLx knows it by.
25    /// Built-in types are spelled in all-uppercase to match SQL convention.
26    ///
27    /// However, user-created types in Postgres are all-lowercase unless quoted.
28    ///
29    /// ## Note: Orthogonal to Nullability
30    /// These overrides do not affect whether `query!()` decides to wrap a column in `Option<_>`
31    /// or not. They only override the inner type used.
32    ///
33    /// ## Note: Schema Qualification (Postgres)
34    /// Type names may be schema-qualified in Postgres. If so, the schema should be part
35    /// of the type string, e.g. `'foo.bar'` to reference type `bar` in schema `foo`.
36    ///
37    /// The schema and/or type name may additionally be quoted in the string
38    /// for a quoted identifier (see next section).
39    ///
40    /// Schema qualification should not be used for types in the search path.
41    ///
42    /// ## Note: Quoted Identifiers (Postgres)
43    /// Type names using [quoted identifiers in Postgres] must also be specified with quotes here.
44    ///
45    /// Note, however, that the TOML format parses way the outer pair of quotes,
46    /// so for quoted names in Postgres, double-quoting is necessary,
47    /// e.g. `'"Foo"'` for SQL type `"Foo"`.
48    ///
49    /// To reference a schema-qualified type with a quoted name, use double-quotes after the
50    /// dot, e.g. `'foo."Bar"'` to reference type `"Bar"` of schema `foo`, and vice versa for
51    /// quoted schema names.
52    ///
53    /// We recommend wrapping all type names in single quotes, as shown below,
54    /// to avoid confusion.
55    ///
56    /// MySQL/MariaDB and SQLite do not support custom types, so quoting type names should
57    /// never be necessary.
58    ///
59    /// [quoted identifiers in Postgres]: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
60    // Note: we wanted to be able to handle this intelligently,
61    // but the `toml` crate authors weren't interested: https://github.com/toml-rs/toml/issues/761
62    //
63    // We decided to just encourage always quoting type names instead.
64    /// Example: Custom Wrapper Types
65    /// -------
66    /// Does SQLx not support a type that you need? Do you want additional semantics not
67    /// implemented on the built-in types? You can create a custom wrapper,
68    /// or use an external crate.
69    ///
70    /// #### `sqlx.toml`
71    /// ```toml
72    /// [macros.type-overrides]
73    /// # Override a built-in type
74    /// 'UUID' = "crate::types::MyUuid"
75    ///
76    /// # Support an external or custom wrapper type (e.g. from the `isn` Postgres extension)
77    /// # (NOTE: FOR DOCUMENTATION PURPOSES ONLY; THIS CRATE/TYPE DOES NOT EXIST AS OF WRITING)
78    /// 'isbn13' = "isn_rs::sqlx::ISBN13"
79    /// ```
80    ///
81    /// Example: Custom Types in Postgres
82    /// -------
83    /// If you have a custom type in Postgres that you want to map without needing to use
84    /// the type override syntax in `sqlx::query!()` every time, you can specify a global
85    /// override here.
86    ///
87    /// For example, a custom enum type `foo`:
88    ///
89    /// #### Migration or Setup SQL (e.g. `migrations/0_setup.sql`)
90    /// ```sql
91    /// CREATE TYPE foo AS ENUM ('Bar', 'Baz');
92    /// ```
93    ///
94    /// #### `src/types.rs`
95    /// ```rust,no_run
96    /// #[derive(sqlx::Type)]
97    /// pub enum Foo {
98    ///     Bar,
99    ///     Baz
100    /// }
101    /// ```
102    ///
103    /// If you're not using `PascalCase` in your enum variants then you'll want to use
104    /// `#[sqlx(rename_all = "<strategy>")]` on your enum.
105    /// See [`Type`][crate::type::Type] for details.
106    ///
107    /// #### `sqlx.toml`
108    /// ```toml
109    /// [macros.type-overrides]
110    /// # Map SQL type `foo` to `crate::types::Foo`
111    /// 'foo' = "crate::types::Foo"
112    /// ```
113    ///
114    /// Example: Schema-Qualified Types
115    /// -------
116    /// (See `Note` section above for details.)
117    ///
118    /// ```toml
119    /// [macros.type-overrides]
120    /// # Map SQL type `foo.foo` to `crate::types::Foo`
121    /// 'foo.foo' = "crate::types::Foo"
122    /// ```
123    ///
124    /// Example: Quoted Identifiers
125    /// -------
126    /// If a type or schema uses quoted identifiers,
127    /// it must be wrapped in quotes _twice_ for SQLx to know the difference:
128    ///
129    /// ```toml
130    /// [macros.type-overrides]
131    /// # `"Foo"` in SQLx
132    /// '"Foo"' = "crate::types::Foo"
133    /// # **NOT** `"Foo"` in SQLx (parses as just `Foo`)
134    /// "Foo" = "crate::types::Foo"
135    ///
136    /// # Schema-qualified
137    /// '"foo".foo' = "crate::types::Foo"
138    /// 'foo."Foo"' = "crate::types::Foo"
139    /// '"foo"."Foo"' = "crate::types::Foo"
140    /// ```
141    ///
142    /// (See `Note` section above for details.)
143    // TODO: allow specifying different types for input vs output
144    // e.g. to accept `&[T]` on input but output `Vec<T>`
145    pub type_overrides: BTreeMap<SqlType, RustType>,
146
147    /// Specify per-table and per-column overrides for mapping SQL types to Rust types.
148    ///
149    /// Default type mappings are defined by the database driver.
150    /// Refer to the `sqlx::types` module for details.
151    ///
152    /// The supported syntax is similar to [`type_overrides`][Self::type_overrides],
153    /// (with the same caveat for quoted names!) but column names must be qualified
154    /// by a separately quoted table name, which may optionally be schema-qualified.
155    ///
156    /// Multiple columns for the same SQL table may be written in the same table in TOML
157    /// (see examples below).
158    ///
159    /// ## Note: Orthogonal to Nullability
160    /// These overrides do not affect whether `query!()` decides to wrap a column in `Option<_>`
161    /// or not. They only override the inner type used.
162    ///
163    /// ## Note: Schema Qualification
164    /// Table names may be schema-qualified. If so, the schema should be part
165    /// of the table name string, e.g. `'foo.bar'` to reference table `bar` in schema `foo`.
166    ///
167    /// The schema and/or type name may additionally be quoted in the string
168    /// for a quoted identifier (see next section).
169    ///
170    /// Postgres users: schema qualification should not be used for tables in the search path.
171    ///
172    /// ## Note: Quoted Identifiers
173    /// Schema, table, or column names using quoted identifiers ([MySQL], [Postgres], [SQLite])
174    /// in SQL must also be specified with quotes here.
175    ///
176    /// Postgres and SQLite use double-quotes (`"Foo"`) while MySQL uses backticks (`\`Foo\`).
177    ///
178    /// Note, however, that the TOML format parses way the outer pair of quotes,
179    /// so for quoted names in Postgres, double-quoting is necessary,
180    /// e.g. `'"Foo"'` for SQL name `"Foo"`.
181    ///
182    /// To reference a schema-qualified table with a quoted name, use the appropriate quotation
183    /// characters after the dot, e.g. `'foo."Bar"'` to reference table `"Bar"` of schema `foo`,
184    /// and vice versa for quoted schema names.
185    ///
186    /// We recommend wrapping all table and column names in single quotes, as shown below,
187    /// to avoid confusion.
188    ///
189    /// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/identifiers.html
190    /// [Postgres]: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
191    /// [SQLite]: https://sqlite.org/lang_keywords.html
192    // Note: we wanted to be able to handle this intelligently,
193    // but the `toml` crate authors weren't interested: https://github.com/toml-rs/toml/issues/761
194    //
195    // We decided to just encourage always quoting type names instead.
196    ///
197    /// Example
198    /// -------
199    ///
200    /// #### `sqlx.toml`
201    /// ```toml
202    /// [macros.table-overrides.'foo']
203    /// # Map column `bar` of table `foo` to Rust type `crate::types::Foo`:
204    /// 'bar' = "crate::types::Bar"
205    ///
206    /// # Quoted column name
207    /// # Note: same quoting requirements as `macros.type_overrides`
208    /// '"Bar"' = "crate::types::Bar"
209    ///
210    /// # Note: will NOT work (parses as `Bar`)
211    /// # "Bar" = "crate::types::Bar"
212    ///
213    /// # Table name may be quoted (note the wrapping single-quotes)
214    /// [macros.table-overrides.'"Foo"']
215    /// 'bar' = "crate::types::Bar"
216    /// '"Bar"' = "crate::types::Bar"
217    ///
218    /// # Table name may also be schema-qualified.
219    /// # Note how the dot is inside the quotes.
220    /// [macros.table-overrides.'my_schema.my_table']
221    /// 'my_column' = "crate::types::MyType"
222    ///
223    /// # Quoted schema, table, and column names
224    /// [macros.table-overrides.'"My Schema"."My Table"']
225    /// '"My Column"' = "crate::types::MyType"
226    /// ```
227    pub table_overrides: BTreeMap<TableName, BTreeMap<ColumnName, RustType>>,
228}
229
230#[derive(Debug, Default)]
231#[cfg_attr(
232    feature = "sqlx-toml",
233    derive(serde::Deserialize),
234    serde(default, rename_all = "kebab-case")
235)]
236pub struct PreferredCrates {
237    /// Specify the crate to use for mapping date/time types to Rust.
238    ///
239    /// The default behavior is to use whatever crate is enabled,
240    /// [`chrono`] or [`time`] (the latter takes precedent).
241    ///
242    /// [`chrono`]: crate::types::chrono
243    /// [`time`]: crate::types::time
244    ///
245    /// Example: Always Use Chrono
246    /// -------
247    /// Thanks to Cargo's [feature unification], a crate in the dependency graph may enable
248    /// the `time` feature of SQLx which will force it on for all crates using SQLx,
249    /// which will result in problems if your crate wants to use types from [`chrono`].
250    ///
251    /// You can use the type override syntax (see `sqlx::query!` for details),
252    /// or you can force an override globally by setting this option.
253    ///
254    /// #### `sqlx.toml`
255    /// ```toml
256    /// [macros.preferred-crates]
257    /// date-time = "chrono"
258    /// ```
259    ///
260    /// [feature unification]: https://doc.rust-lang.org/cargo/reference/features.html#feature-unification
261    pub date_time: DateTimeCrate,
262
263    /// Specify the crate to use for mapping `NUMERIC` types to Rust.
264    ///
265    /// The default behavior is to use whatever crate is enabled,
266    /// [`bigdecimal`] or [`rust_decimal`] (the latter takes precedent).
267    ///
268    /// [`bigdecimal`]: crate::types::bigdecimal
269    /// [`rust_decimal`]: crate::types::rust_decimal
270    ///
271    /// Example: Always Use `bigdecimal`
272    /// -------
273    /// Thanks to Cargo's [feature unification], a crate in the dependency graph may enable
274    /// the `rust_decimal` feature of SQLx which will force it on for all crates using SQLx,
275    /// which will result in problems if your crate wants to use types from [`bigdecimal`].
276    ///
277    /// You can use the type override syntax (see `sqlx::query!` for details),
278    /// or you can force an override globally by setting this option.
279    ///
280    /// #### `sqlx.toml`
281    /// ```toml
282    /// [macros.preferred-crates]
283    /// numeric = "bigdecimal"
284    /// ```
285    ///
286    /// [feature unification]: https://doc.rust-lang.org/cargo/reference/features.html#feature-unification
287    pub numeric: NumericCrate,
288}
289
290/// The preferred crate to use for mapping date/time types to Rust.
291#[derive(Debug, Default, PartialEq, Eq)]
292#[cfg_attr(
293    feature = "sqlx-toml",
294    derive(serde::Deserialize),
295    serde(rename_all = "snake_case")
296)]
297pub enum DateTimeCrate {
298    /// Use whichever crate is enabled (`time` then `chrono`).
299    #[default]
300    Inferred,
301
302    /// Always use types from [`chrono`][crate::types::chrono].
303    ///
304    /// ```toml
305    /// [macros.preferred-crates]
306    /// date-time = "chrono"
307    /// ```
308    Chrono,
309
310    /// Always use types from [`time`][crate::types::time].
311    ///
312    /// ```toml
313    /// [macros.preferred-crates]
314    /// date-time = "time"
315    /// ```
316    Time,
317}
318
319/// The preferred crate to use for mapping `NUMERIC` types to Rust.
320#[derive(Debug, Default, PartialEq, Eq)]
321#[cfg_attr(
322    feature = "sqlx-toml",
323    derive(serde::Deserialize),
324    serde(rename_all = "snake_case")
325)]
326pub enum NumericCrate {
327    /// Use whichever crate is enabled (`rust_decimal` then `bigdecimal`).
328    #[default]
329    Inferred,
330
331    /// Always use types from [`bigdecimal`][crate::types::bigdecimal].
332    ///
333    /// ```toml
334    /// [macros.preferred-crates]
335    /// numeric = "bigdecimal"
336    /// ```
337    #[cfg_attr(feature = "sqlx-toml", serde(rename = "bigdecimal"))]
338    BigDecimal,
339
340    /// Always use types from [`rust_decimal`][crate::types::rust_decimal].
341    ///
342    /// ```toml
343    /// [macros.preferred-crates]
344    /// numeric = "rust_decimal"
345    /// ```
346    RustDecimal,
347}
348
349/// A SQL type name; may optionally be schema-qualified.
350///
351/// See [`macros.type-overrides`][Config::type_overrides] for usages.
352pub type SqlType = Box<str>;
353
354/// A SQL table name; may optionally be schema-qualified.
355///
356/// See [`macros.table-overrides`][Config::table_overrides] for usages.
357pub type TableName = Box<str>;
358
359/// A column in a SQL table.
360///
361/// See [`macros.table-overrides`][Config::table_overrides] for usages.
362pub type ColumnName = Box<str>;
363
364/// A Rust type name or path.
365///
366/// Should be a global path (not relative).
367pub type RustType = Box<str>;
368
369/// Internal getter methods.
370impl Config {
371    /// Get the override for a given type name (optionally schema-qualified).
372    pub fn type_override(&self, type_name: &str) -> Option<&str> {
373        // TODO: make this case-insensitive
374        self.type_overrides.get(type_name).map(|s| &**s)
375    }
376
377    /// Get the override for a given column and table name (optionally schema-qualified).
378    pub fn column_override(&self, table: &str, column: &str) -> Option<&str> {
379        self.table_overrides
380            .get(table)
381            .and_then(|by_column| by_column.get(column))
382            .map(|s| &**s)
383    }
384}
385
386impl DateTimeCrate {
387    /// Returns `self == Self::Inferred`
388    #[inline(always)]
389    pub fn is_inferred(&self) -> bool {
390        *self == Self::Inferred
391    }
392
393    #[inline(always)]
394    pub fn crate_name(&self) -> Option<&str> {
395        match self {
396            Self::Inferred => None,
397            Self::Chrono => Some("chrono"),
398            Self::Time => Some("time"),
399        }
400    }
401}
402
403impl NumericCrate {
404    /// Returns `self == Self::Inferred`
405    #[inline(always)]
406    pub fn is_inferred(&self) -> bool {
407        *self == Self::Inferred
408    }
409
410    #[inline(always)]
411    pub fn crate_name(&self) -> Option<&str> {
412        match self {
413            Self::Inferred => None,
414            Self::BigDecimal => Some("bigdecimal"),
415            Self::RustDecimal => Some("rust_decimal"),
416        }
417    }
418}