doc(sqlite): cover lack of NUMERIC support (#2890)

This commit is contained in:
Austin Bonander 2023-11-20 20:14:20 -08:00 committed by GitHub
parent d3a28d461f
commit ed1b030e91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -79,6 +79,57 @@
//! In addition, `Option<T>` is supported where `T` implements `Type`. An `Option<T>` represents
//! a potentially `NULL` value from SQLite.
//!
//! # Non-feature: `NUMERIC` / `rust_decimal` / `bigdecimal` Support
//! Support for mapping `rust_decimal::Decimal` and `bigdecimal::BigDecimal` to SQLite has been
//! deliberately omitted because SQLite does not have native support for high-
//! or arbitrary-precision decimal arithmetic, and to pretend so otherwise would be a
//! significant misstep in API design.
//!
//! The in-tree [`decimal.c`] extension is unfortunately not included in the [amalgamation],
//! which is used to build the bundled version of SQLite3 for `libsqlite3-sys` (which we have
//! enabled by default for the simpler setup experience), otherwise we could support that.
//!
//! The `NUMERIC` type affinity, while seemingly designed for storing decimal values,
//! stores non-integer real numbers as double-precision IEEE-754 floating point,
//! i.e. `REAL` in SQLite, `f64` in Rust, `double` in C/C++, etc.
//!
//! [Datatypes in SQLite: Type Affinity][type-affinity] (accessed 2023/11/20):
//!
//! > A column with NUMERIC affinity may contain values using all five storage classes.
//! When text data is inserted into a NUMERIC column, the storage class of the text is converted to
//! INTEGER or REAL (in order of preference) if the text is a well-formed integer or real literal,
//! respectively. If the TEXT value is a well-formed integer literal that is too large to fit in a
//! 64-bit signed integer, it is converted to REAL. For conversions between TEXT and REAL storage
//! classes, only the first 15 significant decimal digits of the number are preserved.
//!
//! With the SQLite3 interactive CLI, we can see that a higher-precision value
//! (20 digits in this case) is rounded off:
//!
//! ```text
//! sqlite> CREATE TABLE foo(bar NUMERIC);
//! sqlite> INSERT INTO foo(bar) VALUES('1.2345678901234567890');
//! sqlite> SELECT * FROM foo;
//! 1.23456789012346
//! ```
//!
//! It appears the `TEXT` storage class is only used if the value contains invalid characters
//! or extra whitespace.
//!
//! Thus, the `NUMERIC` type affinity is **unsuitable** for storage of high-precision decimal values
//! and should be **avoided at all costs**.
//!
//! Support for `rust_decimal` and `bigdecimal` would only be a trap because users will naturally
//! want to use the `NUMERIC` type affinity, and might otherwise encounter serious bugs caused by
//! rounding errors that they were deliberately avoiding when they chose an arbitrary-precision type
//! over a floating-point type in the first place.
//!
//! Instead, you should only use a type affinity that SQLite will not attempt to convert implicitly,
//! such as `TEXT` or `BLOB`, and map values to/from SQLite as strings.
//!
//! [`decimal.c`]: https://www.sqlite.org/floatingpoint.html#the_decimal_c_extension
//! [amalgamation]: https://www.sqlite.org/amalgamation.html
//! [type-affinity]: https://www.sqlite.org/datatype3.html#type_affinity
//!
pub(crate) use sqlx_core::types::*;