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::{
170 JoinExplicit, JoinForward, JoinKey, JoinReverse, Joinable, Relation, SchemaHasTable,
171};
172#[cfg(any(feature = "tokio-postgres", feature = "postgres-sync"))]
173#[doc(hidden)]
174pub use row::AnyNullProbe;
175pub use row::{
176 DecodeSelectedRef, ExprValueType, FromDrizzleRow, GroupByIdentity, HasSelectModel, IntoGroupBy,
177 IntoSelectTarget, JoinedStarRow, LeftLateralSelection, MarkerAggValidFor,
178 MarkerColumnCountValid, MarkerScopeValidFor, NullProbeRow, PkGroup, ResolveRow, RowColumnList,
179 SQLTypeToRust, SelectAs, SelectAsFrom, SelectCols, SelectExpr, SelectStar, SelectTableFields,
180 SelectedExpressionList, TableFields, WrapNullable,
181};
182#[doc(hidden)]
183pub use row::{MaybeNull, ProjectionIn};
184pub use schema::{OrderBy, OrderTerm, Ordered, asc, desc};
185// With both PostgreSQL drivers enabled, the row conversions take the shared
186// `postgres-types` items from `tokio_postgres`; `postgres` is still a needed
187// dependency for `postgres-sync`-only builds.
188#[cfg(all(feature = "postgres-sync", feature = "tokio-postgres"))]
189use ::postgres as _;
190
191pub use scope::{
192 AliasKey, FromMarker, FullJoin, HasScope, InnerJoin, JoinStep, Lateral, LeftJoin, OuterJoined,
193 RightJoin, ScopeContains, ScopeEntry, Scoped, SelectSources, SetOperand, Src,
194};
195pub use sql::{
196 ColumnDialect, ColumnFlags, ColumnRef, ColumnSqlRef, ConstraintRef, EnumVariantRef,
197 ForeignKeyRef, OwnedSQL, OwnedSQLChunk, PrimaryKeyRef, SQL, SQLChunk, SQLEnumVariants,
198 TableDialect, TableRef, TableSqlRef, Token,
199};
200pub use traits::*;
201
202// =============================================================================
203// Helper Macros - Used by proc macros for code generation
204// =============================================================================
205
206/// Implements `TryFrom<int>` for several integer types by converting to
207/// `i64` and calling the type's `TryFrom<i64>`.
208///
209/// The type must already implement `TryFrom<i64, Error = DrizzleError>`.
210/// The `SQLiteEnum` derive uses this; you rarely need it directly.
211///
212/// # Examples
213///
214/// ```
215/// use drizzle_core::error::DrizzleError;
216/// use drizzle_core::impl_try_from_int;
217///
218/// #[derive(Debug, PartialEq)]
219/// enum Role {
220/// User,
221/// Admin,
222/// }
223///
224/// impl TryFrom<i64> for Role {
225/// type Error = DrizzleError;
226///
227/// fn try_from(value: i64) -> Result<Self, Self::Error> {
228/// match value {
229/// 0 => Ok(Role::User),
230/// 1 => Ok(Role::Admin),
231/// _ => Err(DrizzleError::ConversionError("unknown role".into())),
232/// }
233/// }
234/// }
235///
236/// impl_try_from_int!(Role => i32, u8);
237///
238/// assert_eq!(Role::try_from(1_i32).unwrap(), Role::Admin);
239/// assert!(Role::try_from(7_u8).is_err());
240/// ```
241#[macro_export]
242macro_rules! impl_try_from_int {
243 ($name:ty => $($int_type:ty),+ $(,)?) => {
244 $(
245 impl TryFrom<$int_type> for $name {
246 type Error = $crate::error::DrizzleError;
247
248 fn try_from(value: $int_type) -> ::core::result::Result<Self, Self::Error> {
249 Self::try_from(value as i64)
250 }
251 }
252 )+
253 };
254}