Skip to main content

drizzle_core/
lib.rs

1//! SQL building blocks shared by every Drizzle dialect.
2//!
3//! Most users depend on the `drizzle` crate, which re-exports this one as
4//! `drizzle::core`. This crate holds the parts that do not depend on a
5//! database:
6//!
7//! - [`SQL`] and [`ToSQL`]: SQL fragments with bound parameters.
8//! - [`expr`]: typed expressions and functions (`eq`, `count`, `coalesce`, ...).
9//! - [`row`] and [`scope`]: compile-time row-type inference and query checks.
10//! - [`traits`]: the table, column, and schema traits the macros implement.
11//! - [`dialect`]: dialect markers and dialect-only feature gates.
12//! - [`error::DrizzleError`]: the error type every driver returns.
13//!
14//! # Examples
15//!
16//! Building a fragment by hand. `Value` stands in for a driver's value type
17//! (`SQLiteValue`, `PostgresValue`, ...):
18//!
19//! ```
20//! use drizzle_core::{SQL, Token};
21//! # use drizzle_core::{Dialect, SQLParam, SQLiteDialect};
22//! # use std::borrow::Cow;
23//! # #[derive(Debug, Clone)]
24//! # struct Value(i64);
25//! # impl SQLParam for Value {
26//! #     const DIALECT: Dialect = Dialect::SQLite;
27//! #     type DialectMarker = SQLiteDialect;
28//! # }
29//! # impl From<Value> for Cow<'_, Value> {
30//! #     fn from(value: Value) -> Self { Cow::Owned(value) }
31//! # }
32//!
33//! let query: SQL<'_, Value> = SQL::raw("SELECT * FROM")
34//!     .append(SQL::ident("users"))
35//!     .push(Token::WHERE)
36//!     .append(SQL::ident("id"))
37//!     .push(Token::EQ)
38//!     .append(SQL::param(Value(42)));
39//!
40//! assert_eq!(query.sql(), r#"SELECT * FROM "users" WHERE "id" = ?"#);
41//! assert_eq!(query.params().count(), 1);
42//! ```
43//!
44//! # Compile-time checks
45//!
46//! Queries are checked when they are built and run, not when they reach the
47//! database. The compiler rejects a query that:
48//!
49//! - reads a table it never added with `.from(...)` or a join
50//!   ([`scope`], [`MarkerScopeValidFor`]);
51//! - decodes a column from the nullable side of an outer join as `T`
52//!   instead of `Option<T>` ([`MarkerColumnCountValid`]);
53//! - selects a non-aggregate column that is not in GROUP BY
54//!   ([`MarkerAggValidFor`]);
55//! - calls a clause out of order, such as `.r#where(...)` after
56//!   `.limit(...)` ([`ClauseAllowed`]);
57//! - uses a function the dialect lacks ([`DialectSupports`]).
58//!
59//! # `no_std` Support
60//!
61//! This crate supports `no_std` environments with an allocator:
62//!
63//! ```toml
64//! # With std (default)
65//! drizzle-core = "0.2"
66//!
67//! # no_std with allocator
68//! drizzle-core = { version = "0.2", default-features = false, features = ["alloc"] }
69//! ```
70
71#![cfg_attr(not(feature = "std"), no_std)]
72#![recursion_limit = "512"]
73
74#[cfg(not(feature = "std"))]
75extern crate alloc;
76
77// Prelude for std/alloc compatibility
78pub(crate) mod prelude {
79    // Re-export alloc types for std builds too (they're the same underlying types)
80    #[cfg(feature = "std")]
81    pub use std::{
82        borrow::Cow,
83        boxed::Box,
84        collections::{HashMap, HashSet},
85        format,
86        rc::Rc,
87        string::{String, ToString},
88        sync::Arc,
89        vec,
90        vec::Vec,
91    };
92
93    #[cfg(not(feature = "std"))]
94    pub use alloc::{
95        borrow::Cow,
96        boxed::Box,
97        format,
98        string::{String, ToString},
99        vec,
100        vec::Vec,
101    };
102
103    #[cfg(all(not(feature = "std"), feature = "alloc"))]
104    pub use alloc::{rc::Rc, sync::Arc};
105
106    // For no_std, use hashbrown instead of std::collections::{HashMap, HashSet}
107    #[cfg(not(feature = "std"))]
108    pub use hashbrown::{HashMap, HashSet};
109}
110
111pub mod bind;
112pub mod builder;
113pub mod conv;
114pub mod cte;
115pub mod dialect;
116pub mod error;
117#[macro_use]
118pub mod traits;
119pub mod derived;
120pub mod expr;
121pub mod helpers;
122pub mod join;
123#[cfg(feature = "serde")]
124pub mod json;
125pub mod pagination;
126pub mod param;
127pub mod placeholder;
128pub mod prepared;
129#[cfg(feature = "profiling")]
130pub mod profiling;
131#[cfg(feature = "query")]
132pub mod query;
133pub mod relation;
134#[cfg(any(feature = "serde", feature = "query"))]
135#[doc(hidden)]
136pub use serde;
137#[cfg(any(feature = "serde", feature = "query"))]
138#[doc(hidden)]
139pub use serde_json;
140pub mod row;
141pub mod schema;
142pub mod scope;
143pub mod sql;
144pub mod tracing;
145pub mod types;
146
147// Re-export key types and traits
148pub use bind::{BindValue, NullableBindValue, ValueTypeForDialect};
149pub use builder::{
150    BuilderInit, ClauseAllowed, ExecutableState, IncludesRequired, InsertColumn, InsertColumnsSet,
151    InsertSelectAllColumns, InsertSelectColumns, InsertSelectCompatible, InsertSelectTable,
152    InsertTargetColumnList, InsertTargetColumns, InsertTargetMarker, PartialInsertSelectCompatible,
153    clause,
154};
155pub use derived::{
156    Derived, DerivedField, DerivedProjection, DerivedSelection, ProjectionOutput, TableProjection,
157};
158pub use dialect::{
159    Dialect, DialectSupports, DialectTypes, MySQLDialect, PostgresDialect, SQLiteDialect, feature,
160};
161pub use join::{Join, JoinType, LateralArg, LateralSource};
162#[cfg(feature = "serde")]
163pub use json::Json;
164pub use pagination::PaginationArg;
165pub use param::{OwnedParam, Param, ParamBind, ParamSet};
166pub use placeholder::*;
167#[cfg(feature = "query")]
168pub use relation::{AssembleRel, CardWrap, Many, One, OptionalOne, RelationDef};
169pub use relation::{Joinable, Relation, SchemaHasTable};
170pub use row::{
171    DecodeSelectedRef, ExprValueType, FromDrizzleRow, GroupByIdentity, HasSelectModel, IntoGroupBy,
172    IntoSelectTarget, JoinedStarRow, LeftLateralSelection, MarkerAggValidFor,
173    MarkerColumnCountValid, MarkerScopeValidFor, NullProbeRow, PkGroup, ResolveRow, RowColumnList,
174    SQLTypeToRust, SelectAs, SelectAsFrom, SelectCols, SelectExpr, SelectStar, SelectTableFields,
175    SelectedExpressionList, TableFields, WrapNullable,
176};
177#[doc(hidden)]
178pub use row::{MaybeNull, ProjectionIn};
179pub use schema::{OrderBy, OrderTerm, Ordered, asc, desc};
180// With both PostgreSQL drivers enabled, the row conversions take the shared
181// `postgres-types` items from `tokio_postgres`; `postgres` is still a needed
182// dependency for `postgres-sync`-only builds.
183#[cfg(all(feature = "postgres-sync", feature = "tokio-postgres"))]
184use ::postgres as _;
185
186pub use scope::{
187    AliasKey, FromMarker, FullJoin, HasScope, InnerJoin, JoinStep, Lateral, LeftJoin, OuterJoined,
188    RightJoin, ScopeContains, ScopeEntry, Scoped, SelectSources, SetOperand, Src,
189};
190pub use sql::{
191    ColumnDialect, ColumnFlags, ColumnRef, ColumnSqlRef, ConstraintRef, EnumVariantRef,
192    ForeignKeyRef, OwnedSQL, OwnedSQLChunk, PrimaryKeyRef, SQL, SQLChunk, SQLEnumVariants,
193    TableDialect, TableRef, TableSqlRef, Token,
194};
195pub use traits::*;
196
197// =============================================================================
198// Helper Macros - Used by proc macros for code generation
199// =============================================================================
200
201/// Implements `TryFrom<int>` for several integer types by converting to
202/// `i64` and calling the type's `TryFrom<i64>`.
203///
204/// The type must already implement `TryFrom<i64, Error = DrizzleError>`.
205/// The `SQLiteEnum` derive uses this; you rarely need it directly.
206///
207/// # Examples
208///
209/// ```
210/// use drizzle_core::error::DrizzleError;
211/// use drizzle_core::impl_try_from_int;
212///
213/// #[derive(Debug, PartialEq)]
214/// enum Role {
215///     User,
216///     Admin,
217/// }
218///
219/// impl TryFrom<i64> for Role {
220///     type Error = DrizzleError;
221///
222///     fn try_from(value: i64) -> Result<Self, Self::Error> {
223///         match value {
224///             0 => Ok(Role::User),
225///             1 => Ok(Role::Admin),
226///             _ => Err(DrizzleError::ConversionError("unknown role".into())),
227///         }
228///     }
229/// }
230///
231/// impl_try_from_int!(Role => i32, u8);
232///
233/// assert_eq!(Role::try_from(1_i32).unwrap(), Role::Admin);
234/// assert!(Role::try_from(7_u8).is_err());
235/// ```
236#[macro_export]
237macro_rules! impl_try_from_int {
238    ($name:ty => $($int_type:ty),+ $(,)?) => {
239        $(
240            impl TryFrom<$int_type> for $name {
241                type Error = $crate::error::DrizzleError;
242
243                fn try_from(value: $int_type) -> ::core::result::Result<Self, Self::Error> {
244                    Self::try_from(value as i64)
245                }
246            }
247        )+
248    };
249}