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}