1mod ddl;
20mod identifiers;
21mod kind;
22mod reflection;
23mod transactions;
24
25pub use kind::{normalize_dialect_name, DialectKind};
26pub use reflection::{ReflectionQuery, ReflectionQueryKind, ReflectionScope};
27
28use ddl::{
29 compile_add_column, compile_add_constraint, compile_alter_column, compile_column_comment,
30 compile_create_index, compile_create_table, compile_drop_column, compile_drop_index,
31 compile_table_comment, compile_table_mysql_options, compile_table_postgres_inherits,
32 compile_table_postgres_using, compile_table_postgres_with, compile_table_tablespace,
33 MysqlTableOptionsRef,
34};
35use identifiers::{quote_backtick, quote_double};
36use ormdantic_core::{
37 BackendFeature, DeferrableMode, FeatureSet, IsolationLevel, OrmdanticError, OrmdanticResult,
38 SavepointName, TransactionAccessMode, TransactionOptions,
39};
40use ormdantic_schema::{ColumnDef, FieldKind, NamespaceDef, SchemaOperation};
41use reflection::scope_predicate;
42use transactions::render_isolation_level;
43
44pub trait Dialect {
45 fn kind(&self) -> DialectKind;
46 fn name(&self) -> &'static str;
47 fn quote_ident(&self, ident: &str) -> String;
48 fn placeholder(&self, index: usize) -> String;
49 fn max_bind_parameters(&self) -> Option<usize> {
50 Some(match self.kind() {
51 DialectKind::Sqlite => 32_766,
52 DialectKind::Postgres
53 | DialectKind::MySql
54 | DialectKind::MariaDb
55 | DialectKind::Oracle => 65_535,
56 DialectKind::MsSql => 2_100,
57 })
58 }
59 fn supports_returning(&self) -> bool;
60 fn supports_native_uuid(&self) -> bool;
61 fn supports_json(&self) -> bool;
62
63 fn feature_set(&self) -> FeatureSet {
64 let mut features = FeatureSet::new([
65 BackendFeature::Ctes,
66 BackendFeature::Savepoints,
67 BackendFeature::Windows,
68 ]);
69 if self.supports_returning() {
70 features.insert(BackendFeature::Returning);
71 }
72 if self.supports_json() {
73 features.insert(BackendFeature::NativeJson);
74 }
75 if self.supports_native_uuid() {
76 features.insert(BackendFeature::NativeUuid);
77 }
78 features
79 }
80
81 fn supports_feature(&self, feature: BackendFeature) -> bool {
82 self.feature_set().contains(feature)
83 }
84
85 fn render_column_type(&self, column: &ColumnDef) -> String {
86 match column.kind() {
87 FieldKind::String => {
88 render_string_type(self.kind(), column.max_length(), column.is_primary_key())
89 }
90 FieldKind::Enum { name, schema } => match (self.kind(), name) {
91 (DialectKind::Postgres, Some(name)) => match schema {
92 Some(schema) => {
93 format!("{}.{}", self.quote_ident(schema), self.quote_ident(name))
94 }
95 None => self.quote_ident(name),
96 },
97 _ => "TEXT".to_string(),
98 },
99 FieldKind::Integer => "INTEGER".to_string(),
100 FieldKind::Float => "REAL".to_string(),
101 FieldKind::Boolean => "BOOLEAN".to_string(),
102 FieldKind::Uuid if self.supports_native_uuid() => "UUID".to_string(),
103 FieldKind::Uuid => render_uuid_type(self.kind(), column.is_primary_key()),
104 FieldKind::Date => "DATE".to_string(),
105 FieldKind::DateTime => "TIMESTAMP".to_string(),
106 FieldKind::Json | FieldKind::ModelJson if self.supports_json() => "JSON".to_string(),
107 FieldKind::Json | FieldKind::ModelJson => "TEXT".to_string(),
108 FieldKind::Decimal if self.kind() == DialectKind::Sqlite => {
109 match (column.precision(), column.scale()) {
110 (Some(precision), Some(scale)) => {
111 format!("DECIMAL_TEXT({precision}, {scale})")
112 }
113 _ => "DECIMAL_TEXT".to_string(),
114 }
115 }
116 FieldKind::Decimal => match (column.precision(), column.scale()) {
117 (Some(precision), Some(scale)) => format!("NUMERIC({precision}, {scale})"),
118 _ => "NUMERIC".to_string(),
119 },
120 FieldKind::Binary => "BLOB".to_string(),
121 FieldKind::ForeignKey { .. } => {
122 render_string_type(self.kind(), column.max_length(), true)
123 }
124 FieldKind::Unknown => "TEXT".to_string(),
125 }
126 }
127
128 fn compile_schema_operation(
129 &self,
130 operation: &SchemaOperation,
131 ) -> OrmdanticResult<Vec<String>> {
132 Ok(match operation {
133 SchemaOperation::CreateNamespace(namespace) => {
134 compile_create_namespace(self, namespace)?
135 }
136 SchemaOperation::DropNamespace { name } => compile_drop_namespace(self, name)?,
137 SchemaOperation::SetNamespaceComment { name, comment } => {
138 compile_namespace_comment(self, name, comment.as_deref())?
139 }
140 SchemaOperation::CreateTable(table) => compile_create_table(self, table)?,
141 SchemaOperation::DropTable { name } => vec![drop_table_sql(self, name)],
142 SchemaOperation::RecreateTable(table) => {
143 let mut statements =
144 vec![drop_table_sql(self, &table.qualified_name().to_string())];
145 statements.extend(compile_create_table(self, table)?);
146 statements
147 }
148 SchemaOperation::AddColumn { table, column } => {
149 compile_add_column(self, table, column)?
150 }
151 SchemaOperation::DropColumn { table, column } => {
152 vec![compile_drop_column(self, table, column)]
153 }
154 SchemaOperation::AlterColumn { table, column } => {
155 vec![compile_alter_column(self, table, column)?]
156 }
157 SchemaOperation::SetColumnComment { table, column } => {
158 compile_column_comment(self, table, column)?
159 .into_iter()
160 .collect()
161 }
162 SchemaOperation::CreateIndex { table, index } => {
163 vec![compile_create_index(self, table, index)?]
164 }
165 SchemaOperation::DropIndex { table, name } => {
166 vec![compile_drop_index(self, table, name)]
167 }
168 SchemaOperation::AddConstraint { table, constraint } => {
169 vec![compile_add_constraint(self, table, constraint)?]
170 }
171 SchemaOperation::DropConstraint { table, name } => vec![format!(
172 "ALTER TABLE {} DROP CONSTRAINT {}",
173 quote_qualified_name(self, table),
174 self.quote_ident(name)
175 )],
176 SchemaOperation::SetTableComment { table, comment } => {
177 compile_table_comment(self, table, comment.as_deref())?
178 .into_iter()
179 .collect()
180 }
181 SchemaOperation::SetTableTablespace { table, tablespace } => {
182 compile_table_tablespace(self, table, tablespace.as_deref())?
183 .into_iter()
184 .collect()
185 }
186 SchemaOperation::SetTableMysqlOptions {
187 table,
188 engine,
189 charset,
190 collation,
191 row_format,
192 key_block_size,
193 pack_keys,
194 checksum,
195 delay_key_write,
196 stats_persistent,
197 stats_auto_recalc,
198 stats_sample_pages,
199 avg_row_length,
200 max_rows,
201 min_rows,
202 insert_method,
203 data_directory,
204 index_directory,
205 connection,
206 union,
207 partition_by,
208 partitions,
209 subpartition_by,
210 subpartitions,
211 auto_increment,
212 } => compile_table_mysql_options(
213 self,
214 table,
215 MysqlTableOptionsRef {
216 engine: engine.as_deref(),
217 charset: charset.as_deref(),
218 collation: collation.as_deref(),
219 row_format: row_format.as_deref(),
220 key_block_size: *key_block_size,
221 pack_keys: *pack_keys,
222 checksum: *checksum,
223 delay_key_write: *delay_key_write,
224 stats_persistent: *stats_persistent,
225 stats_auto_recalc: *stats_auto_recalc,
226 stats_sample_pages: *stats_sample_pages,
227 avg_row_length: *avg_row_length,
228 max_rows: *max_rows,
229 min_rows: *min_rows,
230 insert_method: insert_method.as_deref(),
231 data_directory: data_directory.as_deref(),
232 index_directory: index_directory.as_deref(),
233 connection: connection.as_deref(),
234 union,
235 partition_by: partition_by.as_deref(),
236 partitions: *partitions,
237 subpartition_by: subpartition_by.as_deref(),
238 subpartitions: *subpartitions,
239 auto_increment: *auto_increment,
240 },
241 )?
242 .into_iter()
243 .collect(),
244 SchemaOperation::SetTablePostgresInherits { table, add, drop } => {
245 compile_table_postgres_inherits(self, table, add, drop)?
246 }
247 SchemaOperation::SetTablePostgresWith { table, set, reset } => {
248 compile_table_postgres_with(self, table, set, reset)?
249 }
250 SchemaOperation::SetTablePostgresUsing { table, using } => {
251 compile_table_postgres_using(self, table, using.as_deref())?
252 .into_iter()
253 .collect()
254 }
255 SchemaOperation::SetTablePostgresUnlogged { table, unlogged } => {
256 if self.kind() != DialectKind::Postgres {
257 return Err(ormdantic_core::OrmdanticError::UnsupportedFeature {
258 feature: "PostgreSQL unlogged tables".to_string(),
259 dialect: self.name().to_string(),
260 });
261 }
262 vec![format!(
263 "ALTER TABLE {} SET {}",
264 quote_qualified_name(self, table),
265 if *unlogged { "UNLOGGED" } else { "LOGGED" }
266 )]
267 }
268 SchemaOperation::AttachPostgresPartition {
269 table,
270 parent,
271 bound,
272 } => {
273 if self.kind() != DialectKind::Postgres {
274 return Err(ormdantic_core::OrmdanticError::UnsupportedFeature {
275 feature: "PostgreSQL table partitions".to_string(),
276 dialect: self.name().to_string(),
277 });
278 }
279 vec![format!(
280 "ALTER TABLE {} ATTACH PARTITION {} {}",
281 quote_qualified_name(self, parent),
282 quote_qualified_name(self, table),
283 bound
284 )]
285 }
286 SchemaOperation::DetachPostgresPartition { table, parent } => {
287 if self.kind() != DialectKind::Postgres {
288 return Err(ormdantic_core::OrmdanticError::UnsupportedFeature {
289 feature: "PostgreSQL table partitions".to_string(),
290 dialect: self.name().to_string(),
291 });
292 }
293 vec![format!(
294 "ALTER TABLE {} DETACH PARTITION {}",
295 quote_qualified_name(self, parent),
296 quote_qualified_name(self, table)
297 )]
298 }
299 })
300 }
301
302 fn begin_transaction_sql(&self, options: &TransactionOptions) -> Vec<String> {
303 match self.kind() {
304 DialectKind::Postgres => postgres_begin_transaction_sql(self, options),
305 DialectKind::MySql | DialectKind::MariaDb => mysql_begin_transaction_sql(self, options),
306 DialectKind::MsSql => mssql_begin_transaction_sql(self, options),
307 DialectKind::Oracle => oracle_begin_transaction_sql(self, options),
308 DialectKind::Sqlite => vec!["BEGIN".to_string()],
309 }
310 }
311
312 fn set_isolation_sql(&self, isolation_level: IsolationLevel) -> String {
313 format!(
314 "SET TRANSACTION ISOLATION LEVEL {}",
315 render_isolation_level(isolation_level)
316 )
317 }
318
319 fn savepoint_sql(&self, name: &SavepointName) -> String {
320 format!("SAVEPOINT {}", self.quote_ident(name.as_str()))
321 }
322
323 fn rollback_to_savepoint_sql(&self, name: &SavepointName) -> String {
324 format!("ROLLBACK TO SAVEPOINT {}", self.quote_ident(name.as_str()))
325 }
326
327 fn release_savepoint_sql(&self, name: &SavepointName) -> String {
328 format!("RELEASE SAVEPOINT {}", self.quote_ident(name.as_str()))
329 }
330
331 fn reflection_queries(&self, scope: &ReflectionScope) -> Vec<ReflectionQuery> {
332 vec![
333 ReflectionQuery::new(
334 ReflectionQueryKind::Tables,
335 format!("SELECT table_name FROM information_schema.tables{}", scope_predicate(scope)),
336 ),
337 ReflectionQuery::new(
338 ReflectionQueryKind::Columns,
339 format!("SELECT table_name, column_name, data_type, is_nullable FROM information_schema.columns{}", scope_predicate(scope)),
340 ),
341 ReflectionQuery::new(
342 ReflectionQueryKind::Constraints,
343 format!("SELECT table_name, constraint_name, constraint_type FROM information_schema.table_constraints{}", scope_predicate(scope)),
344 ),
345 ]
346 }
347
348 fn upsert_conflict_clause(
349 &self,
350 conflict_column: &str,
351 update_columns: &[String],
352 ) -> OrmdanticResult<String> {
353 let target = self.quote_ident(conflict_column);
354 if update_columns.is_empty() {
355 return Ok(format!("ON CONFLICT ({target}) DO NOTHING"));
356 }
357
358 let assignments = update_columns
359 .iter()
360 .map(|column| {
361 format!(
362 "{} = excluded.{}",
363 self.quote_ident(column),
364 self.quote_ident(column)
365 )
366 })
367 .collect::<Vec<_>>()
368 .join(", ");
369 Ok(format!(
370 "ON CONFLICT ({target}) DO UPDATE SET {assignments}"
371 ))
372 }
373}
374
375const DEFAULT_BOUNDED_STRING_LENGTH: u32 = 255;
376
377fn render_string_type(kind: DialectKind, max_length: Option<u32>, keyable: bool) -> String {
378 let max_length = max_length.filter(|length| *length > 0);
379 match kind {
380 DialectKind::Sqlite => "TEXT".to_string(),
381 DialectKind::Postgres => max_length
382 .map(|length| format!("VARCHAR({length})"))
383 .unwrap_or_else(|| "TEXT".to_string()),
384 DialectKind::MsSql => match max_length {
385 Some(length) => format!("NVARCHAR({length})"),
386 None => format!("NVARCHAR({DEFAULT_BOUNDED_STRING_LENGTH})"),
387 },
388 DialectKind::Oracle => match max_length {
389 Some(length) => format!("VARCHAR2({length})"),
390 None => format!("VARCHAR2({DEFAULT_BOUNDED_STRING_LENGTH})"),
391 },
392 DialectKind::MySql | DialectKind::MariaDb => match max_length {
393 Some(length) => format!("VARCHAR({length})"),
394 None if keyable => format!("VARCHAR({DEFAULT_BOUNDED_STRING_LENGTH})"),
395 None => "TEXT".to_string(),
396 },
397 }
398}
399
400fn render_uuid_type(kind: DialectKind, keyable: bool) -> String {
401 match kind {
402 DialectKind::Oracle => "VARCHAR2(36)".to_string(),
403 DialectKind::MySql | DialectKind::MariaDb if keyable => "VARCHAR(36)".to_string(),
404 _ => "TEXT".to_string(),
405 }
406}
407
408fn postgres_begin_transaction_sql(
409 dialect: &(impl Dialect + ?Sized),
410 options: &TransactionOptions,
411) -> Vec<String> {
412 let mut statements = Vec::new();
413 if let Some(isolation_level) = options.isolation_level() {
414 statements.push(dialect.set_isolation_sql(isolation_level));
415 }
416 let mut begin = "BEGIN".to_string();
417 if options.access_mode() == TransactionAccessMode::ReadOnly {
418 begin.push_str(" READ ONLY");
419 }
420 match options.deferrable_mode() {
421 Some(DeferrableMode::Deferrable) => begin.push_str(" DEFERRABLE"),
422 Some(DeferrableMode::NotDeferrable) => begin.push_str(" NOT DEFERRABLE"),
423 None => {}
424 }
425 statements.push(begin);
426 statements
427}
428
429fn mysql_begin_transaction_sql(
430 dialect: &(impl Dialect + ?Sized),
431 options: &TransactionOptions,
432) -> Vec<String> {
433 let mut statements = Vec::new();
434 if let Some(isolation_level) = options.isolation_level() {
435 statements.push(dialect.set_isolation_sql(isolation_level));
436 }
437 let mut begin = "START TRANSACTION".to_string();
438 if options.access_mode() == TransactionAccessMode::ReadOnly {
439 begin.push_str(" READ ONLY");
440 }
441 statements.push(begin);
442 statements
443}
444
445fn mssql_begin_transaction_sql(
446 dialect: &(impl Dialect + ?Sized),
447 options: &TransactionOptions,
448) -> Vec<String> {
449 let mut statements = Vec::new();
450 if let Some(isolation_level) = options.isolation_level() {
451 statements.push(dialect.set_isolation_sql(isolation_level));
452 }
453 statements.push("BEGIN TRANSACTION".to_string());
454 statements
455}
456
457fn oracle_begin_transaction_sql(
458 dialect: &(impl Dialect + ?Sized),
459 options: &TransactionOptions,
460) -> Vec<String> {
461 if options.access_mode() == TransactionAccessMode::ReadOnly {
462 return vec!["SET TRANSACTION READ ONLY".to_string()];
463 }
464 if let Some(isolation_level) = options.isolation_level() {
465 return vec![dialect.set_isolation_sql(isolation_level)];
466 }
467 Vec::new()
468}
469
470fn quote_qualified_name(dialect: &(impl Dialect + ?Sized), name: &str) -> String {
471 name.split('.')
472 .map(|part| dialect.quote_ident(part))
473 .collect::<Vec<_>>()
474 .join(".")
475}
476
477fn drop_table_sql(dialect: &(impl Dialect + ?Sized), name: &str) -> String {
478 let qualified = quote_qualified_name(dialect, name);
479 if dialect.kind() == DialectKind::Oracle {
480 return format!("DROP TABLE {qualified}");
481 }
482 format!("DROP TABLE IF EXISTS {qualified}")
483}
484
485fn reflection_where(
486 scope: &ReflectionScope,
487 schema_column: Option<&str>,
488 table_column: &str,
489 default_schema_expr: Option<&str>,
490 mut predicates: Vec<String>,
491) -> String {
492 if let Some(schema_column) = schema_column {
493 match scope.schema_name() {
494 Some(schema) => {
495 predicates.push(format!("{schema_column} = {}", sql_string_literal(schema)));
496 }
497 None => {
498 if let Some(default_schema_expr) = default_schema_expr {
499 predicates.push(format!("{schema_column} = {default_schema_expr}"));
500 }
501 }
502 }
503 }
504 if !scope.table_names().is_empty() {
505 predicates.push(format!(
506 "{table_column} IN ({})",
507 scope
508 .table_names()
509 .iter()
510 .map(|table| sql_string_literal(table))
511 .collect::<Vec<_>>()
512 .join(", ")
513 ));
514 }
515 if predicates.is_empty() {
516 String::new()
517 } else {
518 format!(" WHERE {}", predicates.join(" AND "))
519 }
520}
521
522fn oracle_reflection_where(
523 scope: &ReflectionScope,
524 schema_column: Option<&str>,
525 table_column: &str,
526 mut predicates: Vec<String>,
527) -> String {
528 if let (Some(schema_column), Some(schema)) = (schema_column, scope.schema_name()) {
529 predicates.push(format!(
530 "{schema_column} = {}",
531 sql_string_literal(&schema.to_uppercase())
532 ));
533 }
534 if !scope.table_names().is_empty() {
535 predicates.push(format!(
536 "{table_column} IN ({})",
537 scope
538 .table_names()
539 .iter()
540 .map(|table| sql_string_literal(&table.to_uppercase()))
541 .collect::<Vec<_>>()
542 .join(", ")
543 ));
544 }
545 if predicates.is_empty() {
546 String::new()
547 } else {
548 format!(" WHERE {}", predicates.join(" AND "))
549 }
550}
551
552fn compile_create_namespace(
553 dialect: &(impl Dialect + ?Sized),
554 namespace: &NamespaceDef,
555) -> OrmdanticResult<Vec<String>> {
556 let name = namespace.name();
557 let mut statements = match dialect.kind() {
558 DialectKind::Postgres | DialectKind::MySql | DialectKind::MariaDb => vec![format!(
559 "CREATE SCHEMA IF NOT EXISTS {}",
560 dialect.quote_ident(name)
561 )],
562 DialectKind::MsSql => {
563 let create_sql = format!("CREATE SCHEMA {}", dialect.quote_ident(name));
564 vec![format!(
565 "IF SCHEMA_ID({}) IS NULL EXEC({})",
566 mssql_unicode_literal(name),
567 mssql_unicode_literal(&create_sql)
568 )]
569 }
570 DialectKind::Sqlite | DialectKind::Oracle => {
571 return Err(OrmdanticError::UnsupportedFeature {
572 feature: "namespaces/schemas".to_string(),
573 dialect: dialect.name().to_string(),
574 });
575 }
576 };
577 if let Some(comment) = namespace.comment() {
578 statements.extend(compile_namespace_comment(dialect, name, Some(comment))?);
579 }
580 Ok(statements)
581}
582
583fn compile_drop_namespace(
584 dialect: &(impl Dialect + ?Sized),
585 name: &str,
586) -> OrmdanticResult<Vec<String>> {
587 Ok(match dialect.kind() {
588 DialectKind::Postgres | DialectKind::MySql | DialectKind::MariaDb | DialectKind::MsSql => {
589 vec![format!(
590 "DROP SCHEMA IF EXISTS {}",
591 dialect.quote_ident(name)
592 )]
593 }
594 DialectKind::Sqlite | DialectKind::Oracle => {
595 return Err(OrmdanticError::UnsupportedFeature {
596 feature: "namespaces/schemas".to_string(),
597 dialect: dialect.name().to_string(),
598 });
599 }
600 })
601}
602
603fn compile_namespace_comment(
604 dialect: &(impl Dialect + ?Sized),
605 name: &str,
606 comment: Option<&str>,
607) -> OrmdanticResult<Vec<String>> {
608 Ok(match dialect.kind() {
609 DialectKind::Postgres => vec![format!(
610 "COMMENT ON SCHEMA {} IS {}",
611 dialect.quote_ident(name),
612 comment
613 .map(sql_string_literal)
614 .unwrap_or_else(|| "NULL".to_string())
615 )],
616 DialectKind::MsSql => vec![compile_mssql_namespace_comment(name, comment)],
617 _ => {
618 return Err(OrmdanticError::UnsupportedFeature {
619 feature: "namespace comments".to_string(),
620 dialect: dialect.name().to_string(),
621 });
622 }
623 })
624}
625
626fn compile_mssql_namespace_comment(name: &str, comment: Option<&str>) -> String {
627 let schema_literal = mssql_unicode_literal(name);
628 let exists_predicate = format!(
629 "EXISTS (SELECT 1 FROM sys.extended_properties ep \
630 JOIN sys.schemas s ON ep.major_id = s.schema_id \
631 WHERE ep.class = 3 AND ep.minor_id = 0 \
632 AND ep.name = N'MS_Description' \
633 AND s.name = {schema_literal})"
634 );
635 let level_args = format!("@level0type = N'SCHEMA', @level0name = {schema_literal}");
636 match comment {
637 Some(comment) => {
638 let comment_literal = mssql_unicode_literal(comment);
639 format!(
640 "IF {exists_predicate} \
641 EXEC sys.sp_updateextendedproperty @name = N'MS_Description', \
642 @value = {comment_literal}, {level_args}; \
643 ELSE EXEC sys.sp_addextendedproperty @name = N'MS_Description', \
644 @value = {comment_literal}, {level_args}"
645 )
646 }
647 None => format!(
648 "IF {exists_predicate} \
649 EXEC sys.sp_dropextendedproperty @name = N'MS_Description', {level_args}"
650 ),
651 }
652}
653
654fn sql_string_literal(value: &str) -> String {
655 format!("'{}'", value.replace('\'', "''"))
656}
657
658fn mssql_unicode_literal(value: &str) -> String {
659 format!("N'{}'", value.replace('\'', "''"))
660}
661
662#[derive(Debug, Clone, Copy, PartialEq, Eq)]
663pub struct SqliteDialect;
664
665impl Dialect for SqliteDialect {
666 fn kind(&self) -> DialectKind {
667 DialectKind::Sqlite
668 }
669
670 fn name(&self) -> &'static str {
671 "sqlite"
672 }
673
674 fn quote_ident(&self, ident: &str) -> String {
675 quote_double(ident)
676 }
677
678 fn placeholder(&self, _index: usize) -> String {
679 "?".to_string()
680 }
681
682 fn supports_returning(&self) -> bool {
683 true
684 }
685
686 fn supports_native_uuid(&self) -> bool {
687 false
688 }
689
690 fn supports_json(&self) -> bool {
691 true
692 }
693
694 fn reflection_queries(&self, scope: &ReflectionScope) -> Vec<ReflectionQuery> {
695 let table_where = reflection_where(
696 scope,
697 None,
698 "m.name",
699 None,
700 vec!["m.type = 'table'".to_string()],
701 );
702 vec![
703 ReflectionQuery::new(
704 ReflectionQueryKind::Tables,
705 format!("SELECT m.name AS table_name FROM sqlite_master AS m{table_where} ORDER BY m.name"),
706 ),
707 ReflectionQuery::new(
708 ReflectionQueryKind::Columns,
709 format!(
710 "SELECT m.name AS table_name, x.name AS column_name, x.type AS data_type, \
711 NOT x.[notnull] AS is_nullable, x.dflt_value AS column_default, x.pk AS primary_key \
712 FROM sqlite_master AS m, pragma_table_xinfo(m.name) AS x{table_where} \
713 AND x.hidden <> 1 ORDER BY m.name, x.cid"
714 ),
715 ),
716 ReflectionQuery::new(
717 ReflectionQueryKind::Indexes,
718 format!(
719 "SELECT m.name AS table_name, il.name AS index_name, il.[unique] AS is_unique, il.origin \
720 FROM sqlite_master AS m, pragma_index_list(m.name) AS il{table_where} \
721 ORDER BY m.name, il.seq"
722 ),
723 ),
724 ReflectionQuery::new(
725 ReflectionQueryKind::ForeignKeys,
726 format!(
727 "SELECT m.name AS table_name, fk.[table] AS foreign_table, fk.[from] AS column_name, \
728 fk.[to] AS foreign_column, fk.on_update, fk.on_delete, fk.id AS constraint_id \
729 FROM sqlite_master AS m, pragma_foreign_key_list(m.name) AS fk{table_where} \
730 ORDER BY m.name, fk.id, fk.seq"
731 ),
732 ),
733 ReflectionQuery::new(
734 ReflectionQueryKind::Constraints,
735 format!(
736 "SELECT m.name AS table_name, m.sql AS table_sql FROM sqlite_master AS m{table_where} \
737 ORDER BY m.name"
738 ),
739 ),
740 ]
741 }
742}
743
744#[derive(Debug, Clone, Copy, PartialEq, Eq)]
745pub struct PostgresDialect;
746
747impl Dialect for PostgresDialect {
748 fn kind(&self) -> DialectKind {
749 DialectKind::Postgres
750 }
751
752 fn name(&self) -> &'static str {
753 "postgresql"
754 }
755
756 fn quote_ident(&self, ident: &str) -> String {
757 quote_double(ident)
758 }
759
760 fn placeholder(&self, index: usize) -> String {
761 format!("${index}")
762 }
763
764 fn supports_returning(&self) -> bool {
765 true
766 }
767
768 fn supports_native_uuid(&self) -> bool {
769 true
770 }
771
772 fn supports_json(&self) -> bool {
773 true
774 }
775
776 fn reflection_queries(&self, scope: &ReflectionScope) -> Vec<ReflectionQuery> {
777 let table_where = reflection_where(
778 scope,
779 Some("table_schema"),
780 "table_name",
781 None,
782 vec!["table_type = 'BASE TABLE'".to_string()],
783 );
784 let column_where =
785 reflection_where(scope, Some("table_schema"), "table_name", None, vec![]);
786 let constraint_where =
787 reflection_where(scope, Some("table_schema"), "table_name", None, vec![]);
788 let index_where = reflection_where(scope, Some("schemaname"), "tablename", None, vec![]);
789 let foreign_key_where = reflection_where(
790 scope,
791 Some("tc.table_schema"),
792 "tc.table_name",
793 None,
794 vec!["tc.constraint_type = 'FOREIGN KEY'".to_string()],
795 );
796 vec![
797 ReflectionQuery::new(
798 ReflectionQueryKind::Tables,
799 format!("SELECT table_schema, table_name FROM information_schema.tables{table_where} ORDER BY table_schema, table_name"),
800 ),
801 ReflectionQuery::new(
802 ReflectionQueryKind::Columns,
803 format!(
804 "SELECT table_schema, table_name, column_name, data_type, is_nullable, column_default, ordinal_position \
805 FROM information_schema.columns{column_where} ORDER BY table_schema, table_name, ordinal_position"
806 ),
807 ),
808 ReflectionQuery::new(
809 ReflectionQueryKind::Indexes,
810 format!(
811 "SELECT schemaname AS table_schema, tablename AS table_name, indexname AS index_name, indexdef \
812 FROM pg_indexes{index_where} ORDER BY schemaname, tablename, indexname"
813 ),
814 ),
815 ReflectionQuery::new(
816 ReflectionQueryKind::ForeignKeys,
817 format!(
818 "SELECT tc.table_schema, tc.table_name, tc.constraint_name, kcu.column_name, \
819 ccu.table_name AS foreign_table, ccu.column_name AS foreign_column \
820 FROM information_schema.table_constraints AS tc \
821 JOIN information_schema.key_column_usage AS kcu \
822 ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema \
823 JOIN information_schema.constraint_column_usage AS ccu \
824 ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema{foreign_key_where} \
825 ORDER BY tc.table_schema, tc.table_name, tc.constraint_name, kcu.ordinal_position"
826 ),
827 ),
828 ReflectionQuery::new(
829 ReflectionQueryKind::Constraints,
830 format!(
831 "SELECT table_schema, table_name, constraint_name, constraint_type \
832 FROM information_schema.table_constraints{constraint_where} \
833 ORDER BY table_schema, table_name, constraint_name"
834 ),
835 ),
836 ]
837 }
838}
839
840#[derive(Debug, Clone, Copy, PartialEq, Eq)]
841pub struct MySqlDialect;
842
843impl Dialect for MySqlDialect {
844 fn kind(&self) -> DialectKind {
845 DialectKind::MySql
846 }
847
848 fn name(&self) -> &'static str {
849 "mysql"
850 }
851
852 fn quote_ident(&self, ident: &str) -> String {
853 quote_backtick(ident)
854 }
855
856 fn placeholder(&self, _index: usize) -> String {
857 "?".to_string()
858 }
859
860 fn supports_returning(&self) -> bool {
861 false
862 }
863
864 fn supports_native_uuid(&self) -> bool {
865 false
866 }
867
868 fn supports_json(&self) -> bool {
869 true
870 }
871
872 fn release_savepoint_sql(&self, name: &SavepointName) -> String {
873 format!("RELEASE SAVEPOINT {}", self.quote_ident(name.as_str()))
874 }
875
876 fn reflection_queries(&self, scope: &ReflectionScope) -> Vec<ReflectionQuery> {
877 let table_where = reflection_where(
878 scope,
879 Some("table_schema"),
880 "table_name",
881 Some("DATABASE()"),
882 vec!["table_type = 'BASE TABLE'".to_string()],
883 );
884 let column_where = reflection_where(
885 scope,
886 Some("table_schema"),
887 "table_name",
888 Some("DATABASE()"),
889 vec![],
890 );
891 let index_where = reflection_where(
892 scope,
893 Some("table_schema"),
894 "table_name",
895 Some("DATABASE()"),
896 vec!["index_name <> 'PRIMARY'".to_string()],
897 );
898 let foreign_key_where = reflection_where(
899 scope,
900 Some("kcu.table_schema"),
901 "kcu.table_name",
902 Some("DATABASE()"),
903 vec!["kcu.referenced_table_name IS NOT NULL".to_string()],
904 );
905 let constraint_where = reflection_where(
906 scope,
907 Some("table_schema"),
908 "table_name",
909 Some("DATABASE()"),
910 vec![],
911 );
912 vec![
913 ReflectionQuery::new(
914 ReflectionQueryKind::Tables,
915 format!("SELECT table_schema, table_name FROM information_schema.tables{table_where} ORDER BY table_schema, table_name"),
916 ),
917 ReflectionQuery::new(
918 ReflectionQueryKind::Columns,
919 format!(
920 "SELECT table_schema, table_name, column_name, data_type, is_nullable, column_default, ordinal_position \
921 FROM information_schema.columns{column_where} ORDER BY table_schema, table_name, ordinal_position"
922 ),
923 ),
924 ReflectionQuery::new(
925 ReflectionQueryKind::Indexes,
926 format!(
927 "SELECT table_schema, table_name, index_name, non_unique, seq_in_index, column_name \
928 FROM information_schema.statistics{index_where} ORDER BY table_schema, table_name, index_name, seq_in_index"
929 ),
930 ),
931 ReflectionQuery::new(
932 ReflectionQueryKind::ForeignKeys,
933 format!(
934 "SELECT kcu.table_schema, kcu.table_name, kcu.constraint_name, kcu.column_name, \
935 kcu.referenced_table_name AS foreign_table, kcu.referenced_column_name AS foreign_column, \
936 rc.update_rule, rc.delete_rule \
937 FROM information_schema.key_column_usage AS kcu \
938 LEFT JOIN information_schema.referential_constraints AS rc \
939 ON rc.constraint_schema = kcu.constraint_schema \
940 AND rc.constraint_name = kcu.constraint_name \
941 AND rc.table_name = kcu.table_name{foreign_key_where} \
942 ORDER BY kcu.table_schema, kcu.table_name, kcu.constraint_name, kcu.ordinal_position"
943 ),
944 ),
945 ReflectionQuery::new(
946 ReflectionQueryKind::Constraints,
947 format!(
948 "SELECT table_schema, table_name, constraint_name, constraint_type \
949 FROM information_schema.table_constraints{constraint_where} \
950 ORDER BY table_schema, table_name, constraint_name"
951 ),
952 ),
953 ]
954 }
955
956 fn upsert_conflict_clause(
957 &self,
958 _conflict_column: &str,
959 update_columns: &[String],
960 ) -> OrmdanticResult<String> {
961 if update_columns.is_empty() {
962 return Ok("ON DUPLICATE KEY UPDATE 1 = 1".to_string());
963 }
964 let assignments = update_columns
965 .iter()
966 .map(|column| {
967 format!(
968 "{} = VALUES({})",
969 self.quote_ident(column),
970 self.quote_ident(column)
971 )
972 })
973 .collect::<Vec<_>>()
974 .join(", ");
975 Ok(format!("ON DUPLICATE KEY UPDATE {assignments}"))
976 }
977}
978
979#[derive(Debug, Clone, Copy, PartialEq, Eq)]
980pub struct MariaDbDialect;
981
982impl Dialect for MariaDbDialect {
983 fn kind(&self) -> DialectKind {
984 DialectKind::MariaDb
985 }
986
987 fn name(&self) -> &'static str {
988 "mariadb"
989 }
990
991 fn quote_ident(&self, ident: &str) -> String {
992 quote_backtick(ident)
993 }
994
995 fn placeholder(&self, _index: usize) -> String {
996 "?".to_string()
997 }
998
999 fn supports_returning(&self) -> bool {
1000 true
1001 }
1002
1003 fn supports_native_uuid(&self) -> bool {
1004 false
1005 }
1006
1007 fn supports_json(&self) -> bool {
1008 true
1009 }
1010
1011 fn reflection_queries(&self, scope: &ReflectionScope) -> Vec<ReflectionQuery> {
1012 MySqlDialect.reflection_queries(scope)
1013 }
1014
1015 fn upsert_conflict_clause(
1016 &self,
1017 conflict_column: &str,
1018 update_columns: &[String],
1019 ) -> OrmdanticResult<String> {
1020 MySqlDialect.upsert_conflict_clause(conflict_column, update_columns)
1021 }
1022}
1023
1024#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1025pub struct MsSqlDialect;
1026
1027impl Dialect for MsSqlDialect {
1028 fn kind(&self) -> DialectKind {
1029 DialectKind::MsSql
1030 }
1031
1032 fn name(&self) -> &'static str {
1033 "mssql"
1034 }
1035
1036 fn quote_ident(&self, ident: &str) -> String {
1037 format!("[{}]", ident.replace(']', "]]"))
1038 }
1039
1040 fn placeholder(&self, index: usize) -> String {
1041 format!("@P{index}")
1042 }
1043
1044 fn supports_returning(&self) -> bool {
1045 false
1046 }
1047
1048 fn supports_native_uuid(&self) -> bool {
1049 true
1050 }
1051
1052 fn supports_json(&self) -> bool {
1053 false
1054 }
1055
1056 fn savepoint_sql(&self, name: &SavepointName) -> String {
1057 format!("SAVE TRANSACTION {}", self.quote_ident(name.as_str()))
1058 }
1059
1060 fn rollback_to_savepoint_sql(&self, name: &SavepointName) -> String {
1061 format!("ROLLBACK TRANSACTION {}", self.quote_ident(name.as_str()))
1062 }
1063
1064 fn release_savepoint_sql(&self, _name: &SavepointName) -> String {
1065 String::new()
1066 }
1067
1068 fn reflection_queries(&self, scope: &ReflectionScope) -> Vec<ReflectionQuery> {
1069 let table_where = reflection_where(
1070 scope,
1071 Some("TABLE_SCHEMA"),
1072 "TABLE_NAME",
1073 None,
1074 vec!["TABLE_TYPE = 'BASE TABLE'".to_string()],
1075 );
1076 let column_where =
1077 reflection_where(scope, Some("TABLE_SCHEMA"), "TABLE_NAME", None, vec![]);
1078 let index_where = reflection_where(
1079 scope,
1080 Some("s.name"),
1081 "t.name",
1082 None,
1083 vec![
1084 "i.is_primary_key = 0".to_string(),
1085 "i.name IS NOT NULL".to_string(),
1086 ],
1087 );
1088 let foreign_key_where = reflection_where(
1089 scope,
1090 Some("kcu.TABLE_SCHEMA"),
1091 "kcu.TABLE_NAME",
1092 None,
1093 vec![],
1094 );
1095 let constraint_where =
1096 reflection_where(scope, Some("TABLE_SCHEMA"), "TABLE_NAME", None, vec![]);
1097 vec![
1098 ReflectionQuery::new(
1099 ReflectionQueryKind::Tables,
1100 format!("SELECT TABLE_SCHEMA AS table_schema, TABLE_NAME AS table_name FROM INFORMATION_SCHEMA.TABLES{table_where} ORDER BY TABLE_SCHEMA, TABLE_NAME"),
1101 ),
1102 ReflectionQuery::new(
1103 ReflectionQueryKind::Columns,
1104 format!(
1105 "SELECT TABLE_SCHEMA AS table_schema, TABLE_NAME AS table_name, COLUMN_NAME AS column_name, \
1106 DATA_TYPE AS data_type, IS_NULLABLE AS is_nullable, COLUMN_DEFAULT AS column_default, ORDINAL_POSITION AS ordinal_position \
1107 FROM INFORMATION_SCHEMA.COLUMNS{column_where} ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION"
1108 ),
1109 ),
1110 ReflectionQuery::new(
1111 ReflectionQueryKind::Indexes,
1112 format!(
1113 "SELECT s.name AS table_schema, t.name AS table_name, i.name AS index_name, i.is_unique, ic.key_ordinal, c.name AS column_name \
1114 FROM sys.indexes AS i \
1115 JOIN sys.tables AS t ON t.object_id = i.object_id \
1116 JOIN sys.schemas AS s ON s.schema_id = t.schema_id \
1117 LEFT JOIN sys.index_columns AS ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id \
1118 LEFT JOIN sys.columns AS c ON c.object_id = t.object_id AND c.column_id = ic.column_id{index_where} \
1119 ORDER BY s.name, t.name, i.name, ic.key_ordinal"
1120 ),
1121 ),
1122 ReflectionQuery::new(
1123 ReflectionQueryKind::ForeignKeys,
1124 format!(
1125 "SELECT kcu.TABLE_SCHEMA AS table_schema, kcu.TABLE_NAME AS table_name, kcu.CONSTRAINT_NAME AS constraint_name, \
1126 kcu.COLUMN_NAME AS column_name, ccu.TABLE_NAME AS foreign_table, ccu.COLUMN_NAME AS foreign_column \
1127 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu \
1128 JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc \
1129 ON rc.CONSTRAINT_SCHEMA = kcu.CONSTRAINT_SCHEMA AND rc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME \
1130 JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS ccu \
1131 ON ccu.CONSTRAINT_SCHEMA = rc.UNIQUE_CONSTRAINT_SCHEMA AND ccu.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME{foreign_key_where} \
1132 ORDER BY kcu.TABLE_SCHEMA, kcu.TABLE_NAME, kcu.CONSTRAINT_NAME, kcu.ORDINAL_POSITION"
1133 ),
1134 ),
1135 ReflectionQuery::new(
1136 ReflectionQueryKind::Constraints,
1137 format!(
1138 "SELECT TABLE_SCHEMA AS table_schema, TABLE_NAME AS table_name, CONSTRAINT_NAME AS constraint_name, CONSTRAINT_TYPE AS constraint_type \
1139 FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS{constraint_where} ORDER BY TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME"
1140 ),
1141 ),
1142 ]
1143 }
1144
1145 fn upsert_conflict_clause(
1146 &self,
1147 _conflict_column: &str,
1148 _update_columns: &[String],
1149 ) -> OrmdanticResult<String> {
1150 Err(OrmdanticError::UnsupportedFeature {
1151 feature: "INSERT conflict-clause upsert".to_string(),
1152 dialect: self.name().to_string(),
1153 })
1154 }
1155}
1156
1157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1158pub struct OracleDialect;
1159
1160impl Dialect for OracleDialect {
1161 fn kind(&self) -> DialectKind {
1162 DialectKind::Oracle
1163 }
1164
1165 fn name(&self) -> &'static str {
1166 "oracle"
1167 }
1168
1169 fn quote_ident(&self, ident: &str) -> String {
1170 quote_double(ident)
1171 }
1172
1173 fn placeholder(&self, index: usize) -> String {
1174 format!(":{index}")
1175 }
1176
1177 fn supports_returning(&self) -> bool {
1178 false
1179 }
1180
1181 fn supports_native_uuid(&self) -> bool {
1182 false
1183 }
1184
1185 fn supports_json(&self) -> bool {
1186 true
1187 }
1188
1189 fn rollback_to_savepoint_sql(&self, name: &SavepointName) -> String {
1190 format!("ROLLBACK TO SAVEPOINT {}", self.quote_ident(name.as_str()))
1191 }
1192
1193 fn release_savepoint_sql(&self, _name: &SavepointName) -> String {
1194 String::new()
1195 }
1196
1197 fn reflection_queries(&self, scope: &ReflectionScope) -> Vec<ReflectionQuery> {
1198 let scoped = scope.schema_name().is_some();
1199 let table_view = if scoped { "all_tables" } else { "user_tables" };
1200 let columns_view = if scoped {
1201 "all_tab_columns"
1202 } else {
1203 "user_tab_columns"
1204 };
1205 let indexes_view = if scoped {
1206 "all_indexes"
1207 } else {
1208 "user_indexes"
1209 };
1210 let constraints_view = if scoped {
1211 "all_constraints"
1212 } else {
1213 "user_constraints"
1214 };
1215 let cons_columns_view = if scoped {
1216 "all_cons_columns"
1217 } else {
1218 "user_cons_columns"
1219 };
1220 let owner_select = if scoped {
1221 "owner"
1222 } else {
1223 "CAST(NULL AS VARCHAR2(1))"
1224 };
1225 let constraint_owner_select = if scoped {
1226 "c.owner"
1227 } else {
1228 "CAST(NULL AS VARCHAR2(1))"
1229 };
1230 let local_owner_join = if scoped {
1231 "AND c.owner = cc.owner "
1232 } else {
1233 ""
1234 };
1235 let referenced_owner_join = if scoped {
1236 "AND r.owner = rcc.owner "
1237 } else {
1238 ""
1239 };
1240 let referenced_constraint_join = if scoped {
1241 "c.r_constraint_name = r.constraint_name AND c.r_owner = r.owner"
1242 } else {
1243 "c.r_constraint_name = r.constraint_name"
1244 };
1245 let owner_column = scoped.then_some("owner");
1246 let table_where = oracle_reflection_where(scope, owner_column, "table_name", vec![]);
1247 let column_where = oracle_reflection_where(scope, owner_column, "table_name", vec![]);
1248 let index_where = oracle_reflection_where(scope, owner_column, "table_name", vec![]);
1249 let constraint_where = oracle_reflection_where(scope, owner_column, "table_name", vec![]);
1250 let foreign_key_where = oracle_reflection_where(
1251 scope,
1252 scoped.then_some("c.owner"),
1253 "c.table_name",
1254 vec!["c.constraint_type = 'R'".to_string()],
1255 );
1256 vec![
1257 ReflectionQuery::new(
1258 ReflectionQueryKind::Tables,
1259 format!(
1260 "SELECT {owner_select} AS table_schema, table_name FROM {table_view}{table_where} \
1261 ORDER BY table_name"
1262 ),
1263 ),
1264 ReflectionQuery::new(
1265 ReflectionQueryKind::Columns,
1266 format!(
1267 "SELECT {owner_select} AS table_schema, table_name, column_name, data_type, nullable AS is_nullable, data_default AS column_default, column_id AS ordinal_position \
1268 FROM {columns_view}{column_where} ORDER BY table_name, column_id"
1269 ),
1270 ),
1271 ReflectionQuery::new(
1272 ReflectionQueryKind::Indexes,
1273 format!(
1274 "SELECT {owner_select} AS table_schema, table_name, index_name, uniqueness \
1275 FROM {indexes_view}{index_where} ORDER BY table_name, index_name"
1276 ),
1277 ),
1278 ReflectionQuery::new(
1279 ReflectionQueryKind::ForeignKeys,
1280 format!(
1281 "SELECT {constraint_owner_select} AS table_schema, c.table_name, c.constraint_name, cc.column_name, \
1282 r.table_name AS foreign_table, rcc.column_name AS foreign_column, c.delete_rule \
1283 FROM {constraints_view} c \
1284 JOIN {cons_columns_view} cc ON c.constraint_name = cc.constraint_name {local_owner_join}AND c.table_name = cc.table_name \
1285 JOIN {constraints_view} r ON {referenced_constraint_join} \
1286 JOIN {cons_columns_view} rcc ON r.constraint_name = rcc.constraint_name {referenced_owner_join}AND r.table_name = rcc.table_name AND cc.position = rcc.position{foreign_key_where} \
1287 ORDER BY c.table_name, c.constraint_name, cc.position"
1288 ),
1289 ),
1290 ReflectionQuery::new(
1291 ReflectionQueryKind::Constraints,
1292 format!(
1293 "SELECT {owner_select} AS table_schema, table_name, constraint_name, constraint_type \
1294 FROM {constraints_view}{constraint_where} ORDER BY table_name, constraint_name"
1295 ),
1296 ),
1297 ]
1298 }
1299
1300 fn upsert_conflict_clause(
1301 &self,
1302 _conflict_column: &str,
1303 _update_columns: &[String],
1304 ) -> OrmdanticResult<String> {
1305 Err(OrmdanticError::UnsupportedFeature {
1306 feature: "INSERT conflict-clause upsert".to_string(),
1307 dialect: self.name().to_string(),
1308 })
1309 }
1310}
1311
1312#[derive(Debug, Clone, Copy)]
1313pub enum AnyDialect {
1314 Sqlite(SqliteDialect),
1315 Postgres(PostgresDialect),
1316 MySql(MySqlDialect),
1317 MariaDb(MariaDbDialect),
1318 MsSql(MsSqlDialect),
1319 Oracle(OracleDialect),
1320}
1321
1322impl AnyDialect {
1323 pub fn parse(name: &str) -> OrmdanticResult<Self> {
1324 Ok(match DialectKind::parse(name)? {
1325 DialectKind::Sqlite => Self::Sqlite(SqliteDialect),
1326 DialectKind::Postgres => Self::Postgres(PostgresDialect),
1327 DialectKind::MySql => Self::MySql(MySqlDialect),
1328 DialectKind::MariaDb => Self::MariaDb(MariaDbDialect),
1329 DialectKind::MsSql => Self::MsSql(MsSqlDialect),
1330 DialectKind::Oracle => Self::Oracle(OracleDialect),
1331 })
1332 }
1333}
1334
1335impl Dialect for AnyDialect {
1336 fn kind(&self) -> DialectKind {
1337 match self {
1338 Self::Sqlite(dialect) => dialect.kind(),
1339 Self::Postgres(dialect) => dialect.kind(),
1340 Self::MySql(dialect) => dialect.kind(),
1341 Self::MariaDb(dialect) => dialect.kind(),
1342 Self::MsSql(dialect) => dialect.kind(),
1343 Self::Oracle(dialect) => dialect.kind(),
1344 }
1345 }
1346
1347 fn name(&self) -> &'static str {
1348 match self {
1349 Self::Sqlite(dialect) => dialect.name(),
1350 Self::Postgres(dialect) => dialect.name(),
1351 Self::MySql(dialect) => dialect.name(),
1352 Self::MariaDb(dialect) => dialect.name(),
1353 Self::MsSql(dialect) => dialect.name(),
1354 Self::Oracle(dialect) => dialect.name(),
1355 }
1356 }
1357
1358 fn quote_ident(&self, ident: &str) -> String {
1359 match self {
1360 Self::Sqlite(dialect) => dialect.quote_ident(ident),
1361 Self::Postgres(dialect) => dialect.quote_ident(ident),
1362 Self::MySql(dialect) => dialect.quote_ident(ident),
1363 Self::MariaDb(dialect) => dialect.quote_ident(ident),
1364 Self::MsSql(dialect) => dialect.quote_ident(ident),
1365 Self::Oracle(dialect) => dialect.quote_ident(ident),
1366 }
1367 }
1368
1369 fn placeholder(&self, index: usize) -> String {
1370 match self {
1371 Self::Sqlite(dialect) => dialect.placeholder(index),
1372 Self::Postgres(dialect) => dialect.placeholder(index),
1373 Self::MySql(dialect) => dialect.placeholder(index),
1374 Self::MariaDb(dialect) => dialect.placeholder(index),
1375 Self::MsSql(dialect) => dialect.placeholder(index),
1376 Self::Oracle(dialect) => dialect.placeholder(index),
1377 }
1378 }
1379
1380 fn supports_returning(&self) -> bool {
1381 match self {
1382 Self::Sqlite(dialect) => dialect.supports_returning(),
1383 Self::Postgres(dialect) => dialect.supports_returning(),
1384 Self::MySql(dialect) => dialect.supports_returning(),
1385 Self::MariaDb(dialect) => dialect.supports_returning(),
1386 Self::MsSql(dialect) => dialect.supports_returning(),
1387 Self::Oracle(dialect) => dialect.supports_returning(),
1388 }
1389 }
1390
1391 fn supports_native_uuid(&self) -> bool {
1392 match self {
1393 Self::Sqlite(dialect) => dialect.supports_native_uuid(),
1394 Self::Postgres(dialect) => dialect.supports_native_uuid(),
1395 Self::MySql(dialect) => dialect.supports_native_uuid(),
1396 Self::MariaDb(dialect) => dialect.supports_native_uuid(),
1397 Self::MsSql(dialect) => dialect.supports_native_uuid(),
1398 Self::Oracle(dialect) => dialect.supports_native_uuid(),
1399 }
1400 }
1401
1402 fn supports_json(&self) -> bool {
1403 match self {
1404 Self::Sqlite(dialect) => dialect.supports_json(),
1405 Self::Postgres(dialect) => dialect.supports_json(),
1406 Self::MySql(dialect) => dialect.supports_json(),
1407 Self::MariaDb(dialect) => dialect.supports_json(),
1408 Self::MsSql(dialect) => dialect.supports_json(),
1409 Self::Oracle(dialect) => dialect.supports_json(),
1410 }
1411 }
1412
1413 fn feature_set(&self) -> FeatureSet {
1414 match self {
1415 Self::Sqlite(dialect) => dialect.feature_set(),
1416 Self::Postgres(dialect) => dialect.feature_set(),
1417 Self::MySql(dialect) => dialect.feature_set(),
1418 Self::MariaDb(dialect) => dialect.feature_set(),
1419 Self::MsSql(dialect) => dialect.feature_set(),
1420 Self::Oracle(dialect) => dialect.feature_set(),
1421 }
1422 }
1423
1424 fn render_column_type(&self, column: &ColumnDef) -> String {
1425 match self {
1426 Self::Sqlite(dialect) => dialect.render_column_type(column),
1427 Self::Postgres(dialect) => dialect.render_column_type(column),
1428 Self::MySql(dialect) => dialect.render_column_type(column),
1429 Self::MariaDb(dialect) => dialect.render_column_type(column),
1430 Self::MsSql(dialect) => dialect.render_column_type(column),
1431 Self::Oracle(dialect) => dialect.render_column_type(column),
1432 }
1433 }
1434
1435 fn compile_schema_operation(
1436 &self,
1437 operation: &SchemaOperation,
1438 ) -> OrmdanticResult<Vec<String>> {
1439 match self {
1440 Self::Sqlite(dialect) => dialect.compile_schema_operation(operation),
1441 Self::Postgres(dialect) => dialect.compile_schema_operation(operation),
1442 Self::MySql(dialect) => dialect.compile_schema_operation(operation),
1443 Self::MariaDb(dialect) => dialect.compile_schema_operation(operation),
1444 Self::MsSql(dialect) => dialect.compile_schema_operation(operation),
1445 Self::Oracle(dialect) => dialect.compile_schema_operation(operation),
1446 }
1447 }
1448
1449 fn begin_transaction_sql(&self, options: &TransactionOptions) -> Vec<String> {
1450 match self {
1451 Self::Sqlite(dialect) => dialect.begin_transaction_sql(options),
1452 Self::Postgres(dialect) => dialect.begin_transaction_sql(options),
1453 Self::MySql(dialect) => dialect.begin_transaction_sql(options),
1454 Self::MariaDb(dialect) => dialect.begin_transaction_sql(options),
1455 Self::MsSql(dialect) => dialect.begin_transaction_sql(options),
1456 Self::Oracle(dialect) => dialect.begin_transaction_sql(options),
1457 }
1458 }
1459
1460 fn set_isolation_sql(&self, isolation_level: IsolationLevel) -> String {
1461 match self {
1462 Self::Sqlite(dialect) => dialect.set_isolation_sql(isolation_level),
1463 Self::Postgres(dialect) => dialect.set_isolation_sql(isolation_level),
1464 Self::MySql(dialect) => dialect.set_isolation_sql(isolation_level),
1465 Self::MariaDb(dialect) => dialect.set_isolation_sql(isolation_level),
1466 Self::MsSql(dialect) => dialect.set_isolation_sql(isolation_level),
1467 Self::Oracle(dialect) => dialect.set_isolation_sql(isolation_level),
1468 }
1469 }
1470
1471 fn savepoint_sql(&self, name: &SavepointName) -> String {
1472 match self {
1473 Self::Sqlite(dialect) => dialect.savepoint_sql(name),
1474 Self::Postgres(dialect) => dialect.savepoint_sql(name),
1475 Self::MySql(dialect) => dialect.savepoint_sql(name),
1476 Self::MariaDb(dialect) => dialect.savepoint_sql(name),
1477 Self::MsSql(dialect) => dialect.savepoint_sql(name),
1478 Self::Oracle(dialect) => dialect.savepoint_sql(name),
1479 }
1480 }
1481
1482 fn rollback_to_savepoint_sql(&self, name: &SavepointName) -> String {
1483 match self {
1484 Self::Sqlite(dialect) => dialect.rollback_to_savepoint_sql(name),
1485 Self::Postgres(dialect) => dialect.rollback_to_savepoint_sql(name),
1486 Self::MySql(dialect) => dialect.rollback_to_savepoint_sql(name),
1487 Self::MariaDb(dialect) => dialect.rollback_to_savepoint_sql(name),
1488 Self::MsSql(dialect) => dialect.rollback_to_savepoint_sql(name),
1489 Self::Oracle(dialect) => dialect.rollback_to_savepoint_sql(name),
1490 }
1491 }
1492
1493 fn release_savepoint_sql(&self, name: &SavepointName) -> String {
1494 match self {
1495 Self::Sqlite(dialect) => dialect.release_savepoint_sql(name),
1496 Self::Postgres(dialect) => dialect.release_savepoint_sql(name),
1497 Self::MySql(dialect) => dialect.release_savepoint_sql(name),
1498 Self::MariaDb(dialect) => dialect.release_savepoint_sql(name),
1499 Self::MsSql(dialect) => dialect.release_savepoint_sql(name),
1500 Self::Oracle(dialect) => dialect.release_savepoint_sql(name),
1501 }
1502 }
1503
1504 fn reflection_queries(&self, scope: &ReflectionScope) -> Vec<ReflectionQuery> {
1505 match self {
1506 Self::Sqlite(dialect) => dialect.reflection_queries(scope),
1507 Self::Postgres(dialect) => dialect.reflection_queries(scope),
1508 Self::MySql(dialect) => dialect.reflection_queries(scope),
1509 Self::MariaDb(dialect) => dialect.reflection_queries(scope),
1510 Self::MsSql(dialect) => dialect.reflection_queries(scope),
1511 Self::Oracle(dialect) => dialect.reflection_queries(scope),
1512 }
1513 }
1514
1515 fn upsert_conflict_clause(
1516 &self,
1517 conflict_column: &str,
1518 update_columns: &[String],
1519 ) -> OrmdanticResult<String> {
1520 match self {
1521 Self::Sqlite(dialect) => {
1522 dialect.upsert_conflict_clause(conflict_column, update_columns)
1523 }
1524 Self::Postgres(dialect) => {
1525 dialect.upsert_conflict_clause(conflict_column, update_columns)
1526 }
1527 Self::MySql(dialect) => dialect.upsert_conflict_clause(conflict_column, update_columns),
1528 Self::MariaDb(dialect) => {
1529 dialect.upsert_conflict_clause(conflict_column, update_columns)
1530 }
1531 Self::MsSql(dialect) => dialect.upsert_conflict_clause(conflict_column, update_columns),
1532 Self::Oracle(dialect) => {
1533 dialect.upsert_conflict_clause(conflict_column, update_columns)
1534 }
1535 }
1536 }
1537}