1use crate::SQLConstraintKind;
2use crate::prelude::*;
3use crate::{Param, Placeholder, SQLParam, sql::tokens::Token};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum ColumnDialect {
11 SQLite {
12 autoincrement: bool,
13 },
14 PostgreSQL {
15 postgres_type: &'static str,
16 is_serial: bool,
17 is_bigserial: bool,
18 is_generated_identity: bool,
19 is_identity_always: bool,
20 generated_expression: Option<&'static str>,
21 collate: Option<&'static str>,
22 },
23}
24
25#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
27pub enum TableDialect {
28 #[default]
29 PostgreSQL,
30 SQLite {
31 without_rowid: bool,
32 strict: bool,
33 },
34}
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub struct ForeignKeyRef {
41 pub target_table: &'static str,
42 pub source_columns: &'static [&'static str],
43 pub target_columns: &'static [&'static str],
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub struct PrimaryKeyRef {
49 pub columns: &'static [&'static str],
50}
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54pub struct ConstraintRef {
55 pub name: Option<&'static str>,
56 pub kind: SQLConstraintKind,
57 pub columns: &'static [&'static str],
58 pub check_expression: Option<&'static str>,
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69pub struct TableRef {
70 pub name: &'static str,
72 pub column_names: &'static [&'static str],
73
74 pub schema: Option<&'static str>,
76 pub qualified_name: &'static str,
77 pub columns: &'static [ColumnRef],
78 pub primary_key: Option<PrimaryKeyRef>,
79 pub foreign_keys: &'static [ForeignKeyRef],
80 pub constraints: &'static [ConstraintRef],
81 pub dependency_names: &'static [&'static str],
82
83 pub dialect: TableDialect,
85}
86
87impl TableRef {
88 #[must_use]
93 pub const fn sql(name: &'static str, column_names: &'static [&'static str]) -> Self {
94 Self {
95 name,
96 column_names,
97 schema: None,
98 qualified_name: "",
99 columns: &[],
100 primary_key: None,
101 foreign_keys: &[],
102 constraints: &[],
103 dependency_names: &[],
104 dialect: TableDialect::PostgreSQL,
105 }
106 }
107}
108
109#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
115pub struct ColumnFlags(u8);
116
117impl ColumnFlags {
118 pub const NOT_NULL: Self = Self(1 << 0);
120 pub const PRIMARY_KEY: Self = Self(1 << 1);
122 pub const UNIQUE: Self = Self(1 << 2);
124 pub const HAS_DEFAULT: Self = Self(1 << 3);
126
127 #[must_use]
129 pub const fn empty() -> Self {
130 Self(0)
131 }
132
133 #[must_use]
135 pub const fn from_bits(bits: u8) -> Self {
136 Self(bits)
137 }
138
139 #[must_use]
141 pub const fn bits(self) -> u8 {
142 self.0
143 }
144
145 #[must_use]
147 pub const fn contains(self, other: Self) -> bool {
148 (self.0 & other.0) == other.0
149 }
150
151 #[must_use]
153 pub const fn union(self, other: Self) -> Self {
154 Self(self.0 | other.0)
155 }
156}
157
158impl core::ops::BitOr for ColumnFlags {
159 type Output = Self;
160 fn bitor(self, rhs: Self) -> Self {
161 self.union(rhs)
162 }
163}
164
165impl core::ops::BitOrAssign for ColumnFlags {
166 fn bitor_assign(&mut self, rhs: Self) {
167 *self = self.union(rhs);
168 }
169}
170
171#[derive(Clone, Copy, Debug, PartialEq, Eq)]
177pub struct ColumnRef {
178 pub table: &'static str,
180 pub name: &'static str,
181
182 pub sql_type: &'static str,
184 pub flags: ColumnFlags,
185
186 pub dialect: ColumnDialect,
188}
189
190impl ColumnRef {
191 #[must_use]
196 pub const fn sql(table: &'static str, name: &'static str) -> Self {
197 Self {
198 table,
199 name,
200 sql_type: "",
201 flags: ColumnFlags::empty(),
202 dialect: ColumnDialect::SQLite {
203 autoincrement: false,
204 },
205 }
206 }
207
208 #[must_use]
210 pub const fn not_null(&self) -> bool {
211 self.flags.contains(ColumnFlags::NOT_NULL)
212 }
213
214 #[must_use]
216 pub const fn primary_key(&self) -> bool {
217 self.flags.contains(ColumnFlags::PRIMARY_KEY)
218 }
219
220 #[must_use]
222 pub const fn unique(&self) -> bool {
223 self.flags.contains(ColumnFlags::UNIQUE)
224 }
225
226 #[must_use]
228 pub const fn has_default(&self) -> bool {
229 self.flags.contains(ColumnFlags::HAS_DEFAULT)
230 }
231}
232
233#[inline]
246pub fn write_quoted_ident(buf: &mut impl core::fmt::Write, name: &str) {
247 let _ = buf.write_char('"');
248 if name.contains('"') {
249 for ch in name.chars() {
250 if ch == '"' {
251 let _ = buf.write_str("\"\"");
252 } else {
253 let _ = buf.write_char(ch);
254 }
255 }
256 } else {
257 let _ = buf.write_str(name);
258 }
259 let _ = buf.write_char('"');
260}
261
262#[derive(Clone)]
274pub enum SQLChunk<'a, V: SQLParam> {
275 Token(Token),
278
279 Ident(Cow<'a, str>),
283
284 Raw(Cow<'a, str>),
288
289 Number(usize),
294
295 Param(Param<'a, V>),
298
299 Table(TableRef),
303
304 Column(ColumnRef),
307}
308
309impl<'a, V: SQLParam> SQLChunk<'a, V> {
310 #[inline]
314 #[must_use]
315 pub const fn token(t: Token) -> Self {
316 Self::Token(t)
317 }
318
319 #[inline]
321 #[must_use]
322 pub const fn ident_static(name: &'static str) -> Self {
323 Self::Ident(Cow::Borrowed(name))
324 }
325
326 #[inline]
328 #[must_use]
329 pub const fn raw_static(text: &'static str) -> Self {
330 Self::Raw(Cow::Borrowed(text))
331 }
332
333 #[inline]
335 #[must_use]
336 pub const fn table(table: TableRef) -> Self {
337 Self::Table(table)
338 }
339
340 #[inline]
342 #[must_use]
343 pub const fn column(column: ColumnRef) -> Self {
344 Self::Column(column)
345 }
346
347 #[inline]
349 pub const fn param_borrowed(value: &'a V, placeholder: Placeholder) -> Self {
350 Self::Param(Param {
351 value: Some(Cow::Borrowed(value)),
352 placeholder,
353 })
354 }
355
356 #[inline]
360 pub fn ident(name: impl Into<Cow<'a, str>>) -> Self {
361 Self::Ident(name.into())
362 }
363
364 #[inline]
366 pub fn raw(text: impl Into<Cow<'a, str>>) -> Self {
367 Self::Raw(text.into())
368 }
369
370 #[inline]
372 #[must_use]
373 pub const fn number(value: usize) -> Self {
374 Self::Number(value)
375 }
376
377 #[inline]
379 pub fn param(value: impl Into<Cow<'a, V>>, placeholder: Placeholder) -> Self {
380 Self::Param(Param {
381 value: Some(value.into()),
382 placeholder,
383 })
384 }
385
386 pub(crate) fn write(&self, buf: &mut impl core::fmt::Write) {
390 match self {
391 SQLChunk::Token(token) => {
392 let _ = buf.write_str(token.as_str());
393 }
394 SQLChunk::Ident(name) => {
395 write_quoted_ident(buf, name);
396 }
397 SQLChunk::Raw(text) => {
398 let _ = buf.write_str(text);
399 }
400 SQLChunk::Number(value) => {
401 let _ = write!(buf, "{value}");
402 }
403 SQLChunk::Param(Param { placeholder, .. }) => {
404 let _ = write!(buf, "{placeholder}");
405 }
406 SQLChunk::Table(t) => {
407 write_quoted_ident(buf, t.name);
408 }
409 SQLChunk::Column(c) => {
410 write_quoted_ident(buf, c.table);
411 let _ = buf.write_char('.');
412 write_quoted_ident(buf, c.name);
413 }
414 }
415 }
416
417 #[inline]
419 pub(crate) const fn is_word_like(&self) -> bool {
420 match self {
421 SQLChunk::Token(t) => !t.is_punctuation() && !t.is_operator(),
422 SQLChunk::Ident(_)
423 | SQLChunk::Raw(_)
424 | SQLChunk::Number(_)
425 | SQLChunk::Param(_)
426 | SQLChunk::Table(_)
427 | SQLChunk::Column(_) => true,
428 }
429 }
430}
431
432impl<V: SQLParam + core::fmt::Debug> core::fmt::Debug for SQLChunk<'_, V> {
433 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
434 match self {
435 SQLChunk::Token(token) => f.debug_tuple("Token").field(token).finish(),
436 SQLChunk::Ident(name) => f.debug_tuple("Ident").field(name).finish(),
437 SQLChunk::Raw(text) => f.debug_tuple("Raw").field(text).finish(),
438 SQLChunk::Number(value) => f.debug_tuple("Number").field(value).finish(),
439 SQLChunk::Param(param) => f.debug_tuple("Param").field(param).finish(),
440 SQLChunk::Table(t) => f.debug_tuple("Table").field(&t.name).finish(),
441 SQLChunk::Column(c) => f
442 .debug_tuple("Column")
443 .field(&format!("{}.{}", c.table, c.name))
444 .finish(),
445 }
446 }
447}
448
449impl<V: SQLParam> From<Token> for SQLChunk<'_, V> {
452 #[inline]
453 fn from(value: Token) -> Self {
454 Self::Token(value)
455 }
456}
457
458impl<V: SQLParam> From<TableRef> for SQLChunk<'_, V> {
459 #[inline]
460 fn from(value: TableRef) -> Self {
461 Self::Table(value)
462 }
463}
464
465impl<V: SQLParam> From<ColumnRef> for SQLChunk<'_, V> {
466 #[inline]
467 fn from(value: ColumnRef) -> Self {
468 Self::Column(value)
469 }
470}
471
472impl<'a, V: SQLParam> From<Param<'a, V>> for SQLChunk<'a, V> {
473 #[inline]
474 fn from(value: Param<'a, V>) -> Self {
475 Self::Param(value)
476 }
477}