sqlx_sqlite/types/
mod.rs

1//! Conversions between Rust and **SQLite** types.
2//!
3//! # Types
4//!
5//! | Rust type                             | SQLite type(s)                                       |
6//! |---------------------------------------|------------------------------------------------------|
7//! | `bool`                                | BOOLEAN                                              |
8//! | `i8`                                  | INTEGER                                              |
9//! | `i16`                                 | INTEGER                                              |
10//! | `i32`                                 | INTEGER, INT4                                        |
11//! | `i64`                                 | BIGINT, INT8                                         |
12//! | `u8`                                  | INTEGER                                              |
13//! | `u16`                                 | INTEGER                                              |
14//! | `u32`                                 | INTEGER                                              |
15//! | `u64`                                 | INTEGER (Decode only; see note)                      |
16//! | `f32`                                 | REAL                                                 |
17//! | `f64`                                 | REAL                                                 |
18//! | `&str`, [`String`]                    | TEXT                                                 |
19//! | `&[u8]`, `Vec<u8>`                    | BLOB                                                 |
20//!
21//! #### Note: Unsigned Integers
22//! Decoding of unsigned integer types simply performs a checked conversion
23//! to ensure that overflow does not occur.
24//!
25//! Encoding of the unsigned integer types `u8`, `u16` and `u32` is implemented by zero-extending to
26//! the next-larger signed type. So `u8` becomes `i16`, `u16` becomes `i32`, and `u32` becomes `i64`
27//! while still retaining their semantic values.
28//!
29//! SQLite stores integers in a variable-width encoding and always handles them in memory as 64-bit
30//! signed values, so no space is wasted by this implicit widening.
31//!
32//! However, there is no corresponding larger type for `u64` in SQLite
33//! (it would require a native 16-byte integer, i.e. the equivalent of `i128`),
34//! and so encoding is not supported for this type.
35//!
36//! Bit-casting `u64` to `i64`, or storing it as `REAL`, `BLOB` or `TEXT`,
37//! would change the semantics of the value in SQL and so violates the principle of least surprise.
38//!
39//! ### [`chrono`](https://crates.io/crates/chrono)
40//!
41//! Requires the `chrono` Cargo feature flag.
42//!
43//! | Rust type                             | Sqlite type(s)                                       |
44//! |---------------------------------------|------------------------------------------------------|
45//! | `chrono::NaiveDateTime`               | DATETIME (TEXT, INTEGER, REAL)                       |
46//! | `chrono::DateTime<Utc>`               | DATETIME (TEXT, INTEGER, REAL)                       |
47//! | `chrono::DateTime<Local>`             | DATETIME (TEXT, INTEGER, REAL)                       |
48//! | `chrono::DateTime<FixedOffset>`       | DATETIME (TEXT, INTEGER, REAL)                       |
49//! | `chrono::NaiveDate`                   | DATE (TEXT only)                                     |
50//! | `chrono::NaiveTime`                   | TIME (TEXT only)                                     |
51//!
52//! ##### NOTE: `DATETIME` conversions
53//! SQLite may represent `DATETIME` values as one of three types: `TEXT`, `REAL`, or `INTEGER`.
54//! Which one is used is entirely up to you and how you store timestamps in your database.
55//!
56//! The deserialization for `NaiveDateTime`, `DateTime<Utc>` and `DateTime<Local>` infer the date
57//! format from the type of the value they're being decoded from:
58//!
59//! * If `TEXT`, the format is assumed to be an ISO-8601 compatible datetime string.
60//!   A number of possible formats are tried; see `sqlx-sqlite/src/types/chrono.rs` for the current
61//!   set of formats.
62//! * If `INTEGER`, it is expected to be the number of seconds since January 1, 1970 00:00 UTC,
63//!   as if returned from the `unixepoch()` function (without the `subsec` modifier).
64//! * If `REAL`, it is expected to be the (possibly fractional) number of days since the Julian epoch,
65//!   November 24, 4714 BCE 12:00 UTC, as if returned from the `julianday()` function.
66//!
67//! These types will always encode to a datetime string, either
68//! with a timezone offset (`DateTime<Tz>` for any `Tz: TimeZone`) or without (`NaiveDateTime`).
69//!
70//! ##### NOTE: `CURRENT_TIMESTAMP` and comparison/interoperability of `DATETIME` values
71//! As stated previously, `DateTime<Tz>` always encodes to a date-time string
72//! _with_ a timezone offset,
73//! in [RFC 3339 format][::chrono::DateTime::to_rfc3339_opts] (with `use_z: false`).
74//!
75//! However, most of SQLite's datetime functions
76//! (including `datetime()` and `DEFAULT CURRENT_TIMESTAMP`)
77//! do not use this format. They instead use `YYYY-MM-DD HH:MM:SS.SSSS` without a timezone offset.
78//!
79//! This may cause problems with interoperability with other applications, and especially
80//! when comparing datetime values, which compares the actual string values lexicographically.
81//!
82//! Date-time strings in the SQLite format will generally _not_ compare consistently
83//! with date-time strings in the RFC 3339 format.
84//!
85//! We recommend that you decide up-front whether `DATETIME` values should be stored
86//! with explicit time zones or not, and use the corresponding type
87//! (and its corresponding offset, if applicable) _consistently_ throughout your
88//! application:
89//!
90//! * RFC 3339 format: `DateTime<Tz>` (e.g. `DateTime<Utc>`, `DateTime<Local>`, `DateTime<FixedOffset>`)
91//!   * Changing or mixing and matching offsets may break comparisons with existing timestamps.
92//!   * `DateTime<Local>` is **not recommended** for portable applications.
93//!   * `DateTime<FixedOffset>` is only recommended if the offset is **constant**.
94//! * SQLite format: `NaiveDateTime`
95//!
96//! Note that non-constant offsets may still cause issues when comparing timestamps,
97//! as the comparison operators are not timezone-aware.
98//!
99//! ### [`time`](https://crates.io/crates/time)
100//!
101//! Requires the `time` Cargo feature flag.
102//!
103//! | Rust type                             | Sqlite type(s)                                       |
104//! |---------------------------------------|------------------------------------------------------|
105//! | `time::PrimitiveDateTime`             | DATETIME (TEXT, INTEGER)                             |
106//! | `time::OffsetDateTime`                | DATETIME (TEXT, INTEGER)                             |
107//! | `time::Date`                          | DATE (TEXT only)                                     |
108//! | `time::Time`                          | TIME (TEXT only)                                     |
109//!
110//! ##### NOTE: `DATETIME` conversions
111//! The behavior here is identical to the corresponding `chrono` types, minus the support for `REAL`
112//! values as Julian days (it's just not implemented).
113//!
114//! `PrimitiveDateTime` and `OffsetDateTime` will always encode to a datetime string, either
115//! with a timezone offset (`OffsetDateTime`) or without (`PrimitiveDateTime`).
116//!
117//! ##### NOTE: `CURRENT_TIMESTAMP` and comparison/interoperability of `DATETIME` values
118//! As stated previously, `OffsetDateTime` always encodes to a datetime string _with_ a timezone offset,
119//! in [RFC 3339 format][::time::format_description::well_known::Rfc3339] (using `Z` for UTC offsets).
120//!
121//! However, most of SQLite's datetime functions
122//! (including `datetime()` and `DEFAULT CURRENT_TIMESTAMP`)
123//! do not use this format. They instead use `YYYY-MM-DD HH:MM:SS.SSSS` without a timezone offset.
124//!
125//! This may cause problems with interoperability with other applications, and especially
126//! when comparing datetime values, which compares the actual string values lexicographically.
127//!
128//! Date-time strings in the SQLite format will generally _not_ compare consistently
129//! with date-time strings in the RFC 3339 format.
130//!
131//! We recommend that you decide up-front whether `DATETIME` values should be stored
132//! with explicit time zones or not, and use the corresponding type
133//! (and its corresponding offset, if applicable) _consistently_ throughout your
134//! application:
135//!
136//! * RFC 3339 format: `OffsetDateTime` with a **constant** offset.
137//!   * Changing or mixing and matching offsets may break comparisons with existing timestamps.
138//!   * `OffsetDateTime::now_local()` is **not recommended** for portable applications.
139//!   * Non-UTC offsets are only recommended if the offset is **constant**.
140//! * SQLite format: `PrimitiveDateTime`
141//!
142//! Note that non-constant offsets may still cause issues when comparing timestamps,
143//! as the comparison operators are not timezone-aware.
144//!
145//! ### [`uuid`](https://crates.io/crates/uuid)
146//!
147//! Requires the `uuid` Cargo feature flag.
148//!
149//! | Rust type                             | Sqlite type(s)                                       |
150//! |---------------------------------------|------------------------------------------------------|
151//! | `uuid::Uuid`                          | BLOB, TEXT                                           |
152//! | `uuid::fmt::Hyphenated`               | TEXT                                                 |
153//! | `uuid::fmt::Simple`                   | TEXT                                                 |
154//!
155//! ### [`json`](https://crates.io/crates/serde_json)
156//!
157//! Requires the `json` Cargo feature flag.
158//!
159//! | Rust type                             | Sqlite type(s)                                       |
160//! |---------------------------------------|------------------------------------------------------|
161//! | [`Json<T>`]                           | TEXT                                                 |
162//! | `serde_json::JsonValue`               | TEXT                                                 |
163//! | `&serde_json::value::RawValue`        | TEXT                                                 |
164//!
165//! # Nullable
166//!
167//! In addition, `Option<T>` is supported where `T` implements `Type`. An `Option<T>` represents
168//! a potentially `NULL` value from SQLite.
169//!
170//! # Non-feature: `NUMERIC` / `rust_decimal` / `bigdecimal` Support
171//! Support for mapping `rust_decimal::Decimal` and `bigdecimal::BigDecimal` to SQLite has been
172//! deliberately omitted because SQLite does not have native support for high-
173//! or arbitrary-precision decimal arithmetic, and to pretend so otherwise would be a
174//! significant misstep in API design.
175//!
176//! The in-tree [`decimal.c`] extension is unfortunately not included in the [amalgamation],
177//! which is used to build the bundled version of SQLite3 for `libsqlite3-sys` (which we have
178//! enabled by default for the simpler setup experience), otherwise we could support that.
179//!
180//! The `NUMERIC` type affinity, while seemingly designed for storing decimal values,
181//! stores non-integer real numbers as double-precision IEEE-754 floating point,
182//! i.e. `REAL` in SQLite, `f64` in Rust, `double` in C/C++, etc.
183//!
184//! [Datatypes in SQLite: Type Affinity][type-affinity] (accessed 2023/11/20):
185//!
186//! > A column with NUMERIC affinity may contain values using all five storage classes.
187//! > When text data is inserted into a NUMERIC column, the storage class of the text is converted to
188//! > INTEGER or REAL (in order of preference) if the text is a well-formed integer or real literal,
189//! > respectively. If the TEXT value is a well-formed integer literal that is too large to fit in a
190//! > 64-bit signed integer, it is converted to REAL. For conversions between TEXT and REAL storage
191//! > classes, only the first 15 significant decimal digits of the number are preserved.
192//!
193//! With the SQLite3 interactive CLI, we can see that a higher-precision value
194//! (20 digits in this case) is rounded off:
195//!
196//! ```text
197//! sqlite> CREATE TABLE foo(bar NUMERIC);
198//! sqlite> INSERT INTO foo(bar) VALUES('1.2345678901234567890');
199//! sqlite> SELECT * FROM foo;
200//! 1.23456789012346
201//! ```
202//!
203//! It appears the `TEXT` storage class is only used if the value contains invalid characters
204//! or extra whitespace.
205//!
206//! Thus, the `NUMERIC` type affinity is **unsuitable** for storage of high-precision decimal values
207//! and should be **avoided at all costs**.
208//!
209//! Support for `rust_decimal` and `bigdecimal` would only be a trap because users will naturally
210//! want to use the `NUMERIC` type affinity, and might otherwise encounter serious bugs caused by
211//! rounding errors that they were deliberately avoiding when they chose an arbitrary-precision type
212//! over a floating-point type in the first place.
213//!
214//! Instead, you should only use a type affinity that SQLite will not attempt to convert implicitly,
215//! such as `TEXT` or `BLOB`, and map values to/from SQLite as strings. You can do this easily
216//! using [the `Text` adapter].
217//!
218//!
219//! [`decimal.c`]: https://www.sqlite.org/floatingpoint.html#the_decimal_c_extension
220//! [amalgamation]: https://www.sqlite.org/amalgamation.html
221//! [type-affinity]: https://www.sqlite.org/datatype3.html#type_affinity
222//! [the `Text` adapter]: Text
223
224pub(crate) use sqlx_core::types::*;
225
226mod bool;
227mod bytes;
228#[cfg(feature = "chrono")]
229mod chrono;
230mod float;
231mod int;
232#[cfg(feature = "json")]
233mod json;
234mod str;
235mod text;
236#[cfg(feature = "time")]
237mod time;
238mod uint;
239#[cfg(feature = "uuid")]
240mod uuid;