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 generated_stored: bool,
22 collate: Option<&'static str>,
23 },
24}
25
26#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
28pub enum TableDialect {
29 #[default]
30 PostgreSQL,
31 SQLite {
32 without_rowid: bool,
33 strict: bool,
34 },
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub struct ForeignKeyRef {
42 pub target_table: &'static str,
43 pub source_columns: &'static [&'static str],
44 pub target_columns: &'static [&'static str],
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49pub struct PrimaryKeyRef {
50 pub columns: &'static [&'static str],
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55pub struct ConstraintRef {
56 pub name: Option<&'static str>,
57 pub kind: SQLConstraintKind,
58 pub columns: &'static [&'static str],
59 pub check_expression: Option<&'static str>,
60}
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70pub struct TableRef {
71 pub name: &'static str,
73 pub column_names: &'static [&'static str],
74
75 pub schema: Option<&'static str>,
77 pub qualified_name: &'static str,
78 pub columns: &'static [ColumnRef],
79 pub primary_key: Option<PrimaryKeyRef>,
80 pub foreign_keys: &'static [ForeignKeyRef],
81 pub constraints: &'static [ConstraintRef],
82 pub dependency_names: &'static [&'static str],
83
84 pub dialect: TableDialect,
86}
87
88impl TableRef {
89 #[must_use]
94 pub const fn sql(name: &'static str, column_names: &'static [&'static str]) -> Self {
95 Self {
96 name,
97 column_names,
98 schema: None,
99 qualified_name: "",
100 columns: &[],
101 primary_key: None,
102 foreign_keys: &[],
103 constraints: &[],
104 dependency_names: &[],
105 dialect: TableDialect::PostgreSQL,
106 }
107 }
108}
109
110#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
116pub struct ColumnFlags(u8);
117
118impl ColumnFlags {
119 pub const NOT_NULL: Self = Self(1 << 0);
121 pub const PRIMARY_KEY: Self = Self(1 << 1);
123 pub const UNIQUE: Self = Self(1 << 2);
125 pub const HAS_DEFAULT: Self = Self(1 << 3);
127
128 #[must_use]
130 pub const fn empty() -> Self {
131 Self(0)
132 }
133
134 #[must_use]
136 pub const fn from_bits(bits: u8) -> Self {
137 Self(bits)
138 }
139
140 #[must_use]
142 pub const fn bits(self) -> u8 {
143 self.0
144 }
145
146 #[must_use]
148 pub const fn contains(self, other: Self) -> bool {
149 (self.0 & other.0) == other.0
150 }
151
152 #[must_use]
154 pub const fn union(self, other: Self) -> Self {
155 Self(self.0 | other.0)
156 }
157}
158
159impl core::ops::BitOr for ColumnFlags {
160 type Output = Self;
161 fn bitor(self, rhs: Self) -> Self {
162 self.union(rhs)
163 }
164}
165
166impl core::ops::BitOrAssign for ColumnFlags {
167 fn bitor_assign(&mut self, rhs: Self) {
168 *self = self.union(rhs);
169 }
170}
171
172#[derive(Clone, Copy, Debug, PartialEq, Eq)]
178pub struct ColumnRef {
179 pub table: &'static str,
181 pub name: &'static str,
182
183 pub sql_type: &'static str,
185 pub flags: ColumnFlags,
186
187 pub dialect: ColumnDialect,
189}
190
191impl ColumnRef {
192 #[must_use]
197 pub const fn sql(table: &'static str, name: &'static str) -> Self {
198 Self {
199 table,
200 name,
201 sql_type: "",
202 flags: ColumnFlags::empty(),
203 dialect: ColumnDialect::SQLite {
204 autoincrement: false,
205 },
206 }
207 }
208
209 #[must_use]
211 pub const fn not_null(&self) -> bool {
212 self.flags.contains(ColumnFlags::NOT_NULL)
213 }
214
215 #[must_use]
217 pub const fn primary_key(&self) -> bool {
218 self.flags.contains(ColumnFlags::PRIMARY_KEY)
219 }
220
221 #[must_use]
223 pub const fn unique(&self) -> bool {
224 self.flags.contains(ColumnFlags::UNIQUE)
225 }
226
227 #[must_use]
229 pub const fn has_default(&self) -> bool {
230 self.flags.contains(ColumnFlags::HAS_DEFAULT)
231 }
232}
233
234#[inline]
247pub fn write_quoted_ident(buf: &mut impl core::fmt::Write, name: &str) {
248 let _ = buf.write_char('"');
249 if name.contains('"') {
250 for ch in name.chars() {
251 if ch == '"' {
252 let _ = buf.write_str("\"\"");
253 } else {
254 let _ = buf.write_char(ch);
255 }
256 }
257 } else {
258 let _ = buf.write_str(name);
259 }
260 let _ = buf.write_char('"');
261}
262
263#[derive(Clone)]
275pub enum SQLChunk<'a, V: SQLParam> {
276 Token(Token),
279
280 Ident(Cow<'a, str>),
284
285 Raw(Cow<'a, str>),
289
290 Number(usize),
295
296 Param(Param<'a, V>),
299
300 Table(TableRef),
304
305 Column(ColumnRef),
308}
309
310impl<'a, V: SQLParam> SQLChunk<'a, V> {
311 #[inline]
315 #[must_use]
316 pub const fn token(t: Token) -> Self {
317 Self::Token(t)
318 }
319
320 #[inline]
322 #[must_use]
323 pub const fn ident_static(name: &'static str) -> Self {
324 Self::Ident(Cow::Borrowed(name))
325 }
326
327 #[inline]
329 #[must_use]
330 pub const fn raw_static(text: &'static str) -> Self {
331 Self::Raw(Cow::Borrowed(text))
332 }
333
334 #[inline]
336 #[must_use]
337 pub const fn table(table: TableRef) -> Self {
338 Self::Table(table)
339 }
340
341 #[inline]
343 #[must_use]
344 pub const fn column(column: ColumnRef) -> Self {
345 Self::Column(column)
346 }
347
348 #[inline]
350 pub const fn param_borrowed(value: &'a V, placeholder: Placeholder) -> Self {
351 Self::Param(Param {
352 value: Some(Cow::Borrowed(value)),
353 placeholder,
354 })
355 }
356
357 #[inline]
361 pub fn ident(name: impl Into<Cow<'a, str>>) -> Self {
362 Self::Ident(name.into())
363 }
364
365 #[inline]
367 pub fn raw(text: impl Into<Cow<'a, str>>) -> Self {
368 Self::Raw(text.into())
369 }
370
371 #[inline]
373 #[must_use]
374 pub const fn number(value: usize) -> Self {
375 Self::Number(value)
376 }
377
378 #[inline]
380 pub fn param(value: impl Into<Cow<'a, V>>, placeholder: Placeholder) -> Self {
381 Self::Param(Param {
382 value: Some(value.into()),
383 placeholder,
384 })
385 }
386
387 pub(crate) fn write(&self, buf: &mut impl core::fmt::Write) {
391 match self {
392 SQLChunk::Token(token) => {
393 let _ = buf.write_str(token.as_str());
394 }
395 SQLChunk::Ident(name) => {
396 write_quoted_ident(buf, name);
397 }
398 SQLChunk::Raw(text) => {
399 let _ = buf.write_str(text);
400 }
401 SQLChunk::Number(value) => {
402 let _ = write!(buf, "{value}");
403 }
404 SQLChunk::Param(Param { placeholder, .. }) => {
405 let _ = write!(buf, "{placeholder}");
406 }
407 SQLChunk::Table(t) => {
408 write_quoted_ident(buf, t.name);
409 }
410 SQLChunk::Column(c) => {
411 write_quoted_ident(buf, c.table);
412 let _ = buf.write_char('.');
413 write_quoted_ident(buf, c.name);
414 }
415 }
416 }
417
418 #[inline]
420 pub(crate) const fn is_word_like(&self) -> bool {
421 match self {
422 SQLChunk::Token(t) => !t.is_punctuation() && !t.is_operator(),
423 SQLChunk::Ident(_)
424 | SQLChunk::Raw(_)
425 | SQLChunk::Number(_)
426 | SQLChunk::Param(_)
427 | SQLChunk::Table(_)
428 | SQLChunk::Column(_) => true,
429 }
430 }
431}
432
433impl<V: SQLParam + core::fmt::Debug> core::fmt::Debug for SQLChunk<'_, V> {
434 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
435 match self {
436 SQLChunk::Token(token) => f.debug_tuple("Token").field(token).finish(),
437 SQLChunk::Ident(name) => f.debug_tuple("Ident").field(name).finish(),
438 SQLChunk::Raw(text) => f.debug_tuple("Raw").field(text).finish(),
439 SQLChunk::Number(value) => f.debug_tuple("Number").field(value).finish(),
440 SQLChunk::Param(param) => f.debug_tuple("Param").field(param).finish(),
441 SQLChunk::Table(t) => f.debug_tuple("Table").field(&t.name).finish(),
442 SQLChunk::Column(c) => f
443 .debug_tuple("Column")
444 .field(&format!("{}.{}", c.table, c.name))
445 .finish(),
446 }
447 }
448}
449
450impl<V: SQLParam> From<Token> for SQLChunk<'_, V> {
453 #[inline]
454 fn from(value: Token) -> Self {
455 Self::Token(value)
456 }
457}
458
459impl<V: SQLParam> From<TableRef> for SQLChunk<'_, V> {
460 #[inline]
461 fn from(value: TableRef) -> Self {
462 Self::Table(value)
463 }
464}
465
466impl<V: SQLParam> From<ColumnRef> for SQLChunk<'_, V> {
467 #[inline]
468 fn from(value: ColumnRef) -> Self {
469 Self::Column(value)
470 }
471}
472
473impl<'a, V: SQLParam> From<Param<'a, V>> for SQLChunk<'a, V> {
474 #[inline]
475 fn from(value: Param<'a, V>) -> Self {
476 Self::Param(value)
477 }
478}