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}