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 },
21}
22
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
25pub enum TableDialect {
26 #[default]
27 PostgreSQL,
28 SQLite {
29 without_rowid: bool,
30 strict: bool,
31 },
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub struct ForeignKeyRef {
39 pub target_table: &'static str,
40 pub source_columns: &'static [&'static str],
41 pub target_columns: &'static [&'static str],
42}
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub struct PrimaryKeyRef {
47 pub columns: &'static [&'static str],
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq)]
52pub struct ConstraintRef {
53 pub name: Option<&'static str>,
54 pub kind: SQLConstraintKind,
55 pub columns: &'static [&'static str],
56 pub check_expression: Option<&'static str>,
57}
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub struct TableRef {
68 pub name: &'static str,
70 pub column_names: &'static [&'static str],
71
72 pub schema: Option<&'static str>,
74 pub qualified_name: &'static str,
75 pub columns: &'static [ColumnRef],
76 pub primary_key: Option<PrimaryKeyRef>,
77 pub foreign_keys: &'static [ForeignKeyRef],
78 pub constraints: &'static [ConstraintRef],
79 pub dependency_names: &'static [&'static str],
80
81 pub dialect: TableDialect,
83}
84
85impl TableRef {
86 #[must_use]
91 pub const fn sql(name: &'static str, column_names: &'static [&'static str]) -> Self {
92 Self {
93 name,
94 column_names,
95 schema: None,
96 qualified_name: "",
97 columns: &[],
98 primary_key: None,
99 foreign_keys: &[],
100 constraints: &[],
101 dependency_names: &[],
102 dialect: TableDialect::PostgreSQL,
103 }
104 }
105}
106
107#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
113pub struct ColumnFlags(u8);
114
115impl ColumnFlags {
116 pub const NOT_NULL: Self = Self(1 << 0);
118 pub const PRIMARY_KEY: Self = Self(1 << 1);
120 pub const UNIQUE: Self = Self(1 << 2);
122 pub const HAS_DEFAULT: Self = Self(1 << 3);
124
125 #[must_use]
127 pub const fn empty() -> Self {
128 Self(0)
129 }
130
131 #[must_use]
133 pub const fn from_bits(bits: u8) -> Self {
134 Self(bits)
135 }
136
137 #[must_use]
139 pub const fn bits(self) -> u8 {
140 self.0
141 }
142
143 #[must_use]
145 pub const fn contains(self, other: Self) -> bool {
146 (self.0 & other.0) == other.0
147 }
148
149 #[must_use]
151 pub const fn union(self, other: Self) -> Self {
152 Self(self.0 | other.0)
153 }
154}
155
156impl core::ops::BitOr for ColumnFlags {
157 type Output = Self;
158 fn bitor(self, rhs: Self) -> Self {
159 self.union(rhs)
160 }
161}
162
163impl core::ops::BitOrAssign for ColumnFlags {
164 fn bitor_assign(&mut self, rhs: Self) {
165 *self = self.union(rhs);
166 }
167}
168
169#[derive(Clone, Copy, Debug, PartialEq, Eq)]
175pub struct ColumnRef {
176 pub table: &'static str,
178 pub name: &'static str,
179
180 pub sql_type: &'static str,
182 pub flags: ColumnFlags,
183
184 pub dialect: ColumnDialect,
186}
187
188impl ColumnRef {
189 #[must_use]
194 pub const fn sql(table: &'static str, name: &'static str) -> Self {
195 Self {
196 table,
197 name,
198 sql_type: "",
199 flags: ColumnFlags::empty(),
200 dialect: ColumnDialect::SQLite {
201 autoincrement: false,
202 },
203 }
204 }
205
206 #[must_use]
208 pub const fn not_null(&self) -> bool {
209 self.flags.contains(ColumnFlags::NOT_NULL)
210 }
211
212 #[must_use]
214 pub const fn primary_key(&self) -> bool {
215 self.flags.contains(ColumnFlags::PRIMARY_KEY)
216 }
217
218 #[must_use]
220 pub const fn unique(&self) -> bool {
221 self.flags.contains(ColumnFlags::UNIQUE)
222 }
223
224 #[must_use]
226 pub const fn has_default(&self) -> bool {
227 self.flags.contains(ColumnFlags::HAS_DEFAULT)
228 }
229}
230
231#[inline]
244pub fn write_quoted_ident(buf: &mut impl core::fmt::Write, name: &str) {
245 let _ = buf.write_char('"');
246 if name.contains('"') {
247 for ch in name.chars() {
248 if ch == '"' {
249 let _ = buf.write_str("\"\"");
250 } else {
251 let _ = buf.write_char(ch);
252 }
253 }
254 } else {
255 let _ = buf.write_str(name);
256 }
257 let _ = buf.write_char('"');
258}
259
260#[derive(Clone)]
272pub enum SQLChunk<'a, V: SQLParam> {
273 Token(Token),
276
277 Ident(Cow<'a, str>),
281
282 Raw(Cow<'a, str>),
286
287 Number(usize),
292
293 Param(Param<'a, V>),
296
297 Table(TableRef),
301
302 Column(ColumnRef),
305}
306
307impl<'a, V: SQLParam> SQLChunk<'a, V> {
308 #[inline]
312 #[must_use]
313 pub const fn token(t: Token) -> Self {
314 Self::Token(t)
315 }
316
317 #[inline]
319 #[must_use]
320 pub const fn ident_static(name: &'static str) -> Self {
321 Self::Ident(Cow::Borrowed(name))
322 }
323
324 #[inline]
326 #[must_use]
327 pub const fn raw_static(text: &'static str) -> Self {
328 Self::Raw(Cow::Borrowed(text))
329 }
330
331 #[inline]
333 #[must_use]
334 pub const fn table(table: TableRef) -> Self {
335 Self::Table(table)
336 }
337
338 #[inline]
340 #[must_use]
341 pub const fn column(column: ColumnRef) -> Self {
342 Self::Column(column)
343 }
344
345 #[inline]
347 pub const fn param_borrowed(value: &'a V, placeholder: Placeholder) -> Self {
348 Self::Param(Param {
349 value: Some(Cow::Borrowed(value)),
350 placeholder,
351 })
352 }
353
354 #[inline]
358 pub fn ident(name: impl Into<Cow<'a, str>>) -> Self {
359 Self::Ident(name.into())
360 }
361
362 #[inline]
364 pub fn raw(text: impl Into<Cow<'a, str>>) -> Self {
365 Self::Raw(text.into())
366 }
367
368 #[inline]
370 #[must_use]
371 pub const fn number(value: usize) -> Self {
372 Self::Number(value)
373 }
374
375 #[inline]
377 pub fn param(value: impl Into<Cow<'a, V>>, placeholder: Placeholder) -> Self {
378 Self::Param(Param {
379 value: Some(value.into()),
380 placeholder,
381 })
382 }
383
384 pub(crate) fn write(&self, buf: &mut impl core::fmt::Write) {
388 match self {
389 SQLChunk::Token(token) => {
390 let _ = buf.write_str(token.as_str());
391 }
392 SQLChunk::Ident(name) => {
393 write_quoted_ident(buf, name);
394 }
395 SQLChunk::Raw(text) => {
396 let _ = buf.write_str(text);
397 }
398 SQLChunk::Number(value) => {
399 let _ = write!(buf, "{value}");
400 }
401 SQLChunk::Param(Param { placeholder, .. }) => {
402 let _ = write!(buf, "{placeholder}");
403 }
404 SQLChunk::Table(t) => {
405 write_quoted_ident(buf, t.name);
406 }
407 SQLChunk::Column(c) => {
408 write_quoted_ident(buf, c.table);
409 let _ = buf.write_char('.');
410 write_quoted_ident(buf, c.name);
411 }
412 }
413 }
414
415 #[inline]
417 pub(crate) const fn is_word_like(&self) -> bool {
418 match self {
419 SQLChunk::Token(t) => !t.is_punctuation() && !t.is_operator(),
420 SQLChunk::Ident(_)
421 | SQLChunk::Raw(_)
422 | SQLChunk::Number(_)
423 | SQLChunk::Param(_)
424 | SQLChunk::Table(_)
425 | SQLChunk::Column(_) => true,
426 }
427 }
428}
429
430impl<V: SQLParam + core::fmt::Debug> core::fmt::Debug for SQLChunk<'_, V> {
431 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
432 match self {
433 SQLChunk::Token(token) => f.debug_tuple("Token").field(token).finish(),
434 SQLChunk::Ident(name) => f.debug_tuple("Ident").field(name).finish(),
435 SQLChunk::Raw(text) => f.debug_tuple("Raw").field(text).finish(),
436 SQLChunk::Number(value) => f.debug_tuple("Number").field(value).finish(),
437 SQLChunk::Param(param) => f.debug_tuple("Param").field(param).finish(),
438 SQLChunk::Table(t) => f.debug_tuple("Table").field(&t.name).finish(),
439 SQLChunk::Column(c) => f
440 .debug_tuple("Column")
441 .field(&format!("{}.{}", c.table, c.name))
442 .finish(),
443 }
444 }
445}
446
447impl<V: SQLParam> From<Token> for SQLChunk<'_, V> {
450 #[inline]
451 fn from(value: Token) -> Self {
452 Self::Token(value)
453 }
454}
455
456impl<V: SQLParam> From<TableRef> for SQLChunk<'_, V> {
457 #[inline]
458 fn from(value: TableRef) -> Self {
459 Self::Table(value)
460 }
461}
462
463impl<V: SQLParam> From<ColumnRef> for SQLChunk<'_, V> {
464 #[inline]
465 fn from(value: ColumnRef) -> Self {
466 Self::Column(value)
467 }
468}
469
470impl<'a, V: SQLParam> From<Param<'a, V>> for SQLChunk<'a, V> {
471 #[inline]
472 fn from(value: Param<'a, V>) -> Self {
473 Self::Param(value)
474 }
475}