diff --git a/CHANGELOG.md b/CHANGELOG.md index 35ea058ec..074f5430f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ pub struct SeaRc; // new ``` * `impl From for Condition`. Now you can use that instead of `ConditionExpression`, which has been removed. +* Addded `DatabaseName`, `SchemaName`, `TableName`, `ColumnName` types. ### Breaking Changes @@ -120,6 +121,9 @@ impl Iden for Glyph { } } ``` +* Reworked `TableRef` and `ColumnRef` variants. +* Turned `SchemaTable` into a type alias of `TableName`. Code that accesses the + fields inside may not compile. Other existing code should still compile. * Removed `ConditionExpression` from the public API. Instead, just convert between `Condition` and `Expr` using `From`/`Into`. * Blanket-implemented `SqliteExpr` and `PgExpr` for `T where T: ExprTrait`. diff --git a/src/audit/common.rs b/src/audit/common.rs index d4ec8a8e2..e3d8ad0e9 100644 --- a/src/audit/common.rs +++ b/src/audit/common.rs @@ -5,13 +5,7 @@ pub(super) fn parse_audit_table(table_ref: &TableRef) -> Option { match table_ref { TableRef::SubQuery(_, _) => None, TableRef::FunctionCall(_, _) => None, - TableRef::Table(tbl) | TableRef::TableAlias(tbl, _) => Some(SchemaTable(None, tbl.clone())), - TableRef::SchemaTable(sch, tbl) - | TableRef::DatabaseSchemaTable(_, sch, tbl) - | TableRef::SchemaTableAlias(sch, tbl, _) - | TableRef::DatabaseSchemaTableAlias(_, sch, tbl, _) => { - Some(SchemaTable(Some(sch.clone()), tbl.clone())) - } + TableRef::Table(tbl, _) => Some(tbl.clone()), TableRef::ValuesList(_, _) => None, } } diff --git a/src/audit/mod.rs b/src/audit/mod.rs index 9a74f7518..44e427789 100644 --- a/src/audit/mod.rs +++ b/src/audit/mod.rs @@ -4,7 +4,7 @@ mod insert; mod select; mod update; -use crate::DynIden; +use crate::{DynIden, TableName}; pub trait AuditTrait { fn audit(&self) -> Result; @@ -25,7 +25,10 @@ pub struct QueryAccessAudit { #[non_exhaustive] pub struct QueryAccessRequest { pub access_type: AccessType, - pub schema_table: SchemaTable, + /// Legacy naming, kept for compatibility. It should be `table_name`. + /// + /// The table name can be qualified as `(database.)(schema.)table`. + pub schema_table: TableName, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -47,8 +50,11 @@ pub enum SchemaOper { Truncate, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct SchemaTable(pub Option, pub DynIden); +/// A table name, optionally qualified as `(database.)(schema.)table`. +/// +/// This is a legacy type alias, to preserve some compatibility. +/// It's going to be deprecated in the future. +pub type SchemaTable = TableName; impl QueryAccessAudit { /// This filters the selects from access requests. diff --git a/src/audit/select.rs b/src/audit/select.rs index 25a1d8b31..681d7a3ea 100644 --- a/src/audit/select.rs +++ b/src/audit/select.rs @@ -80,19 +80,10 @@ impl Walker { match table_ref { TableRef::SubQuery(select, _) => self.recurse_audit_select(select)?, TableRef::FunctionCall(function, _) => self.recurse_audit_function(function)?, - TableRef::Table(tbl) | TableRef::TableAlias(tbl, _) => { + TableRef::Table(table_name, _) => { self.access.push(QueryAccessRequest { access_type: AccessType::Select, - schema_table: SchemaTable(None, tbl.clone()), - }); - } - TableRef::SchemaTable(sch, tbl) - | TableRef::DatabaseSchemaTable(_, sch, tbl) - | TableRef::SchemaTableAlias(sch, tbl, _) - | TableRef::DatabaseSchemaTableAlias(_, sch, tbl, _) => { - self.access.push(QueryAccessRequest { - access_type: AccessType::Select, - schema_table: SchemaTable(Some(sch.clone()), tbl.clone()), + schema_table: table_name.clone(), }); } TableRef::ValuesList(_, _) => (), @@ -179,7 +170,7 @@ impl Walker { // remove cte alias for cte in &with_clause.cte_expressions { if let Some(table_name) = &cte.table_name { - self.remove_item(AccessType::Select, &SchemaTable(None, table_name.clone())); + self.remove_item(AccessType::Select, &TableName(None, table_name.clone())); } } } diff --git a/src/backend/mysql/foreign_key.rs b/src/backend/mysql/foreign_key.rs index 4edbe4032..a3edf1f05 100644 --- a/src/backend/mysql/foreign_key.rs +++ b/src/backend/mysql/foreign_key.rs @@ -3,7 +3,10 @@ use super::*; impl ForeignKeyBuilder for MysqlQueryBuilder { fn prepare_table_ref_fk_stmt(&self, table_ref: &TableRef, sql: &mut dyn SqlWriter) { match table_ref { - TableRef::Table(_) => self.prepare_table_ref_iden(table_ref, sql), + // Support only "naked" table names with no schema or alias. + TableRef::Table(TableName(None, _), None) => { + self.prepare_table_ref_iden(table_ref, sql) + } _ => panic!("Not supported"), } } diff --git a/src/backend/mysql/index.rs b/src/backend/mysql/index.rs index c46dd49a0..e96344d71 100644 --- a/src/backend/mysql/index.rs +++ b/src/backend/mysql/index.rs @@ -60,7 +60,10 @@ impl IndexBuilder for MysqlQueryBuilder { fn prepare_table_ref_index_stmt(&self, table_ref: &TableRef, sql: &mut dyn SqlWriter) { match table_ref { - TableRef::Table(_) => self.prepare_table_ref_iden(table_ref, sql), + // Support only "naked" table names with no schema or alias. + TableRef::Table(TableName(None, _), None) => { + self.prepare_table_ref_iden(table_ref, sql) + } _ => panic!("Not supported"), } } diff --git a/src/backend/mysql/query.rs b/src/backend/mysql/query.rs index f931a042d..02fae0df5 100644 --- a/src/backend/mysql/query.rs +++ b/src/backend/mysql/query.rs @@ -96,11 +96,10 @@ impl QueryBuilder for MysqlQueryBuilder { self.prepare_iden(column, sql); } else { if let Some(table) = table { - if let TableRef::Table(table) = table.deref() { - self.prepare_column_ref( - &ColumnRef::TableColumn(table.clone(), column.clone()), - sql, - ); + // Support only "naked" table names with no schema or alias. + if let TableRef::Table(TableName(None, table), None) = table.deref() { + let column_name = ColumnName::from((table.clone(), column.clone())); + self.prepare_column_ref(&ColumnRef::Column(column_name), sql); return; } } diff --git a/src/backend/postgres/foreign_key.rs b/src/backend/postgres/foreign_key.rs index 0058167b8..7055a1086 100644 --- a/src/backend/postgres/foreign_key.rs +++ b/src/backend/postgres/foreign_key.rs @@ -101,9 +101,8 @@ impl ForeignKeyBuilder for PostgresQueryBuilder { fn prepare_table_ref_fk_stmt(&self, table_ref: &TableRef, sql: &mut dyn SqlWriter) { match table_ref { - TableRef::Table(_) - | TableRef::SchemaTable(_, _) - | TableRef::DatabaseSchemaTable(_, _, _) => self.prepare_table_ref_iden(table_ref, sql), + // Support only unaliased (but potentialy qualified) table names. + TableRef::Table(.., None) => self.prepare_table_ref_iden(table_ref, sql), _ => panic!("Not supported"), } } diff --git a/src/backend/postgres/index.rs b/src/backend/postgres/index.rs index 020e2a070..ac79ed780 100644 --- a/src/backend/postgres/index.rs +++ b/src/backend/postgres/index.rs @@ -78,11 +78,14 @@ impl IndexBuilder for PostgresQueryBuilder { } fn prepare_table_ref_index_stmt(&self, table_ref: &TableRef, sql: &mut dyn SqlWriter) { - match table_ref { - TableRef::Table(_) | TableRef::SchemaTable(_, _) => { - self.prepare_table_ref_iden(table_ref, sql) - } - _ => panic!("Not supported"), + // Support only `table` and `schema.table` forms. + // No `database.schema.table` or aliases. + let TableRef::Table(table_name, None) = table_ref else { + panic!("Not supported"); + }; + match table_name.as_iden_tuple() { + (Some(_db), _schema, _table) => panic!("Not supported"), + (None, _schema, _table) => self.prepare_table_ref_iden(table_ref, sql), } } @@ -94,13 +97,18 @@ impl IndexBuilder for PostgresQueryBuilder { } if let Some(table) = &drop.table { - match table { - TableRef::Table(_) => {} - TableRef::SchemaTable(schema, _) => { + // Support only `table` and `schema.table` forms. + // No `database.schema.table` or aliases. + let TableRef::Table(table_name, None) = table else { + panic!("Not supported"); + }; + match table_name.as_iden_tuple() { + (None, None, _table) => {} + (None, Some(schema), _table) => { self.prepare_iden(schema, sql); write!(sql, ".").unwrap(); } - _ => panic!("Not supported"), + (Some(_db), _schema, _table) => panic!("Not supported"), } } if let Some(name) = &drop.index.name { diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index b2b587cfe..702b1ad2b 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -595,27 +595,21 @@ pub trait QueryBuilder: fn prepare_column_ref(&self, column_ref: &ColumnRef, sql: &mut dyn SqlWriter) { match column_ref { - ColumnRef::Column(column) => self.prepare_iden(column, sql), - ColumnRef::TableColumn(table, column) => { - self.prepare_iden(table, sql); - write!(sql, ".").unwrap(); - self.prepare_iden(column, sql); - } - ColumnRef::SchemaTableColumn(schema, table, column) => { - self.prepare_iden(schema, sql); - write!(sql, ".").unwrap(); - self.prepare_iden(table, sql); - write!(sql, ".").unwrap(); + ColumnRef::Column(ColumnName(table_name, column)) => { + if let Some(table_name) = table_name { + self.prepare_table_name(table_name, sql); + write!(sql, ".").unwrap(); + } self.prepare_iden(column, sql); } - ColumnRef::Asterisk => { + ColumnRef::Asterisk(table_name) => { + if let Some(table_name) = table_name { + self.prepare_table_name(table_name, sql); + write!(sql, ".").unwrap(); + } write!(sql, "*").unwrap(); } - ColumnRef::TableAsterisk(table) => { - self.prepare_iden(table, sql); - write!(sql, ".*").unwrap(); - } - }; + } } /// Translate [`UnOper`] into SQL statement. diff --git a/src/backend/sqlite/foreign_key.rs b/src/backend/sqlite/foreign_key.rs index 49daab942..9c4368a7d 100644 --- a/src/backend/sqlite/foreign_key.rs +++ b/src/backend/sqlite/foreign_key.rs @@ -3,7 +3,10 @@ use super::*; impl ForeignKeyBuilder for SqliteQueryBuilder { fn prepare_table_ref_fk_stmt(&self, table_ref: &TableRef, sql: &mut dyn SqlWriter) { match table_ref { - TableRef::Table(_) => self.prepare_table_ref_iden(table_ref, sql), + // Support only "naked" table names with no schema or alias. + TableRef::Table(TableName(None, _), None) => { + self.prepare_table_ref_iden(table_ref, sql) + } _ => panic!("Not supported"), } } diff --git a/src/backend/sqlite/index.rs b/src/backend/sqlite/index.rs index b549b0dd0..7adbb803b 100644 --- a/src/backend/sqlite/index.rs +++ b/src/backend/sqlite/index.rs @@ -37,7 +37,10 @@ impl IndexBuilder for SqliteQueryBuilder { fn prepare_table_ref_index_stmt(&self, table_ref: &TableRef, sql: &mut dyn SqlWriter) { match table_ref { - TableRef::Table(_) => self.prepare_table_ref_iden(table_ref, sql), + // Support only "naked" table names with no schema or alias. + TableRef::Table(TableName(None, _), None) => { + self.prepare_table_ref_iden(table_ref, sql) + } _ => panic!("Not supported"), } } diff --git a/src/backend/table_builder.rs b/src/backend/table_builder.rs index ae49c4bb1..9388455f2 100644 --- a/src/backend/table_builder.rs +++ b/src/backend/table_builder.rs @@ -68,9 +68,8 @@ pub trait TableBuilder: /// Translate [`TableRef`] into SQL statement. fn prepare_table_ref_table_stmt(&self, table_ref: &TableRef, sql: &mut dyn SqlWriter) { match table_ref { - TableRef::Table(_) - | TableRef::SchemaTable(_, _) - | TableRef::DatabaseSchemaTable(_, _, _) => self.prepare_table_ref_iden(table_ref, sql), + // Support only unaliased (but potentialy qualified) table names. + TableRef::Table(.., None) => self.prepare_table_ref_iden(table_ref, sql), _ => panic!("Not supported"), } } diff --git a/src/backend/table_ref_builder.rs b/src/backend/table_ref_builder.rs index 0bbfc9570..57f64e681 100644 --- a/src/backend/table_ref_builder.rs +++ b/src/backend/table_ref_builder.rs @@ -3,48 +3,30 @@ use crate::*; pub trait TableRefBuilder: QuotedBuilder { /// Translate [`TableRef`] that without values into SQL statement. fn prepare_table_ref_iden(&self, table_ref: &TableRef, sql: &mut dyn SqlWriter) { - match table_ref { - TableRef::Table(iden) => { - self.prepare_iden(iden, sql); - } - TableRef::SchemaTable(schema, table) => { - self.prepare_iden(schema, sql); - write!(sql, ".").unwrap(); - self.prepare_iden(table, sql); - } - TableRef::DatabaseSchemaTable(database, schema, table) => { - self.prepare_iden(database, sql); - write!(sql, ".").unwrap(); - self.prepare_iden(schema, sql); - write!(sql, ".").unwrap(); - self.prepare_iden(table, sql); - } - TableRef::TableAlias(iden, alias) => { - self.prepare_iden(iden, sql); - write!(sql, " AS ").unwrap(); - self.prepare_iden(alias, sql); - } - TableRef::SchemaTableAlias(schema, table, alias) => { - self.prepare_iden(schema, sql); - write!(sql, ".").unwrap(); - self.prepare_iden(table, sql); - write!(sql, " AS ").unwrap(); - self.prepare_iden(alias, sql); - } - TableRef::DatabaseSchemaTableAlias(database, schema, table, alias) => { - self.prepare_iden(database, sql); - write!(sql, ".").unwrap(); - self.prepare_iden(schema, sql); - write!(sql, ".").unwrap(); - self.prepare_iden(table, sql); - write!(sql, " AS ").unwrap(); - self.prepare_iden(alias, sql); - } + let (table_name, alias) = match table_ref { + TableRef::Table(table_name, alias) => (table_name, alias), TableRef::SubQuery(_, _) | TableRef::ValuesList(_, _) - | TableRef::FunctionCall(_, _) => { - panic!("TableRef with values is not support") + | TableRef::FunctionCall(_, _) => panic!("TableRef with values is not support"), + }; + self.prepare_table_name(table_name, sql); + if let Some(alias) = alias { + write!(sql, " AS ").unwrap(); + self.prepare_iden(alias, sql); + } + } + + /// Translate [`TableName`] into an SQL statement. + fn prepare_table_name(&self, table_name: &TableName, sql: &mut dyn SqlWriter) { + let TableName(schema_name, table) = table_name; + if let Some(SchemaName(database_name, schema)) = schema_name { + if let Some(DatabaseName(database)) = database_name { + self.prepare_iden(database, sql); + write!(sql, ".").unwrap(); } + self.prepare_iden(schema, sql); + write!(sql, ".").unwrap(); } + self.prepare_iden(table, sql); } } diff --git a/src/query/select.rs b/src/query/select.rs index 01f06a7d2..5fbe0667b 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -987,10 +987,7 @@ impl SelectStatement { /// ); /// assert_eq!( /// query.audit().unwrap().selects(), - /// [SchemaTable( - /// Some(Font::Table.into_iden()), - /// Char::Table.into_iden() - /// )] + /// [TableName(Some(Font::Table.into()), Char::Table.into_iden())] /// ); /// ``` pub fn from_as(&mut self, tbl_ref: R, alias: A) -> &mut Self @@ -1051,7 +1048,7 @@ impl SelectStatement { /// use sea_query::{tests_cfg::*, *}; /// /// let query = Query::select() - /// .column(ColumnRef::Asterisk) + /// .column(Asterisk) /// .from_function(Func::random(), "func") /// .to_owned(); /// @@ -1083,7 +1080,7 @@ impl SelectStatement { /// use sea_query::{tests_cfg::*, *}; /// /// let query = Query::select() - /// .column(ColumnRef::Asterisk) + /// .column(Asterisk) /// .from(Char::Table) /// .from_clear() /// .from(Font::Table) @@ -2365,14 +2362,14 @@ impl SelectStatement { /// .to_owned(); /// /// let select = SelectStatement::new() - /// .column(ColumnRef::Asterisk) + /// .column(Asterisk) /// .from("cte_traversal") /// .to_owned(); /// /// let with_clause = WithClause::new() /// .recursive(true) /// .cte(common_table_expression) - /// .cycle(Cycle::new_from_expr_set_using(Expr::Column(ColumnRef::Column("id".into_iden())), "looped", "traversal_path")) + /// .cycle(Cycle::new_from_expr_set_using(Expr::Column("id".into_column_ref()), "looped", "traversal_path")) /// .to_owned(); /// /// let query = select.with(with_clause).to_owned(); @@ -2437,11 +2434,11 @@ impl SelectStatement { /// let with_clause = WithClause::new() /// .recursive(true) /// .cte(common_table_expression) - /// .cycle(Cycle::new_from_expr_set_using(Expr::Column(ColumnRef::Column("id".into_iden())), "looped", "traversal_path")) + /// .cycle(Cycle::new_from_expr_set_using(Expr::Column("id".into_column_ref()), "looped", "traversal_path")) /// .to_owned(); /// /// let query = SelectStatement::new() - /// .column(ColumnRef::Asterisk) + /// .column(Asterisk) /// .from("cte_traversal") /// .with_cte(with_clause) /// .to_owned(); diff --git a/src/query/with.rs b/src/query/with.rs index 2da068298..b1e41ba78 100644 --- a/src/query/with.rs +++ b/src/query/with.rs @@ -1,6 +1,7 @@ use crate::{ - ColumnRef, DynIden, Expr, IntoIden, QueryBuilder, QueryStatementBuilder, QueryStatementWriter, - SelectExpr, SelectStatement, SqlWriter, SubQueryStatement, TableRef, Values, + ColumnRef, DynIden, Expr, IdenList, IntoIden, QueryBuilder, QueryStatementBuilder, + QueryStatementWriter, SelectExpr, SelectStatement, SqlWriter, SubQueryStatement, TableName, + TableRef, Values, }; use inherent::inherent; @@ -115,14 +116,8 @@ impl CommonTableExpression { cte.try_set_cols_from_selects(&select.selects); if let Some(from) = select.from.first() { match from { - TableRef::Table(iden) => cte.set_table_name_from_select(iden), - TableRef::SchemaTable(_, iden) => cte.set_table_name_from_select(iden), - TableRef::DatabaseSchemaTable(_, _, iden) => cte.set_table_name_from_select(iden), - TableRef::TableAlias(_, iden) => cte.set_table_name_from_select(iden), - TableRef::SchemaTableAlias(_, _, iden) => cte.set_table_name_from_select(iden), - TableRef::DatabaseSchemaTableAlias(_, _, _, iden) => { - cte.set_table_name_from_select(iden) - } + TableRef::Table(_, Some(alias)) => cte.set_table_name_from_select(alias), + TableRef::Table(TableName(_, tbl), None) => cte.set_table_name_from_select(tbl), _ => {} } } @@ -151,16 +146,17 @@ impl CommonTableExpression { Some(ident.clone()) } else { match &select.expr { - Expr::Column(column) => match column { - ColumnRef::Column(iden) => Some(iden.clone()), - ColumnRef::TableColumn(table, column) => { - Some(format!("{table}_{column}").into_iden()) - } - ColumnRef::SchemaTableColumn(schema, table, column) => { - Some(format!("{schema}_{table}_{column}").into_iden()) + Expr::Column(ColumnRef::Column(column_name)) => { + // We could depend on `itertools` instead of joining manually. + let mut joined_column_name = String::new(); + for part in column_name.clone().into_iter() { + joined_column_name.push_str(&part.0); + joined_column_name.push('_'); } - _ => None, - }, + // Remove the trailing underscore after the column name. + joined_column_name.pop(); + Some(joined_column_name.into_iden()) + } _ => None, } } @@ -394,14 +390,14 @@ impl Cycle { /// .to_owned(); /// /// let select = SelectStatement::new() -/// .column(ColumnRef::Asterisk) +/// .column(Asterisk) /// .from("cte_traversal") /// .to_owned(); /// /// let with_clause = WithClause::new() /// .recursive(true) /// .cte(common_table_expression) -/// .cycle(Cycle::new_from_expr_set_using(Expr::Column(ColumnRef::Column("id".into_iden())), "looped", "traversal_path")) +/// .cycle(Cycle::new_from_expr_set_using(Expr::Column("id".into_column_ref()), "looped", "traversal_path")) /// .to_owned(); /// /// let query = select.with(with_clause).to_owned(); diff --git a/src/types.rs b/src/types.rs index 770d4addc..5ac4d7e33 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,7 @@ //! Base types used throughout sea-query. use crate::{FunctionCall, ValueTuple, Values, expr::*, query::*}; -use std::borrow::Cow; +use std::{borrow::Cow, fmt::Debug, iter::Flatten}; #[cfg(feature = "backend-postgres")] use crate::extension::postgres::PgBinOper; @@ -128,27 +128,77 @@ pub trait IdenList { fn into_iter(self) -> Self::IntoIter; } -/// Column references +/// An identifier that represents a database name. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DatabaseName(pub DynIden); + +/// A schema name, potentially qualified as `(database.)schema`. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SchemaName(pub Option, pub DynIden); + +/// A table name, potentially qualified as `(database.)(schema.)table`. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TableName(pub Option, pub DynIden); + +impl TableName { + /// A flat `(db?, schema?, table)` tuple view, for quick pattern matching. + /// + /// Don't use this if you need exhaustiveness. + /// The return type is too lax and allows invalid shapes like `(Some(_), None, _)`. + pub(crate) fn as_iden_tuple(&self) -> (Option<&DynIden>, Option<&DynIden>, &DynIden) { + let TableName(schema_name, table) = self; + match schema_name { + None => (None, None, table), + Some(SchemaName(db_name, schema)) => match db_name { + None => (None, Some(schema), table), + Some(DatabaseName(db)) => (Some(db), Some(schema), table), + }, + } + } +} + +/// A column name, potentially qualified as `(database.)(schema.)(table.)column`. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ColumnName(pub Option, pub DynIden); + +/// Iteration over `[db?, schema?, table?, column]` identifiers. +impl IdenList for ColumnName { + type IntoIter = Flatten, 4>>; + + /// Iteration over `[db?, schema?, table?, column]` identifiers. + fn into_iter(self) -> Self::IntoIter { + let ColumnName(table_name, column) = self; + let arr = match table_name { + None => [None, None, None, Some(column)], + Some(TableName(schema_name, table)) => match schema_name { + None => [None, None, Some(table), Some(column)], + Some(SchemaName(db_name, schema)) => { + let db = db_name.map(|db| db.0); + [db, Some(schema), Some(table), Some(column)] + } + }, + }; + arr.into_iter().flatten() + } +} + +/// Column references. #[derive(Debug, Clone, PartialEq)] #[non_exhaustive] pub enum ColumnRef { - Column(DynIden), - TableColumn(DynIden, DynIden), - SchemaTableColumn(DynIden, DynIden, DynIden), - Asterisk, - TableAsterisk(DynIden), + /// A column name, potentially qualified as `(database.)(schema.)(table.)column`. + Column(ColumnName), + /// An `*` expression, potentially qualified as `(database.)(schema.)(table.)*`. + Asterisk(Option), } impl ColumnRef { #[doc(hidden)] - /// Returns the column name if it's not an asterisk. + /// Returns the unqualified column name if it's not an asterisk. pub fn column(&self) -> Option<&DynIden> { match self { - ColumnRef::Column(column) => Some(column), - ColumnRef::TableColumn(_, column) => Some(column), - ColumnRef::SchemaTableColumn(_, _, column) => Some(column), - ColumnRef::Asterisk => None, - ColumnRef::TableAsterisk(_) => None, + ColumnRef::Column(ColumnName(_table_ref, column_itself)) => Some(column_itself), + ColumnRef::Asterisk(..) => None, } } } @@ -161,18 +211,8 @@ pub trait IntoColumnRef { #[derive(Debug, Clone, PartialEq)] #[non_exhaustive] pub enum TableRef { - /// Table identifier without any schema / database prefix - Table(DynIden), - /// Table identifier with schema prefix - SchemaTable(DynIden, DynIden), - /// Table identifier with database and schema prefix - DatabaseSchemaTable(DynIden, DynIden, DynIden), - /// Table identifier with alias - TableAlias(DynIden, DynIden), - /// Table identifier with schema prefix and alias - SchemaTableAlias(DynIden, DynIden, DynIden), - /// Table identifier with database and schema prefix and alias - DatabaseSchemaTableAlias(DynIden, DynIden, DynIden, DynIden), + /// A table identifier. Potentially qualified. Potentially remaned using an alias + Table(TableName, Option), /// Subquery with alias SubQuery(Box, DynIden), /// Values list with alias @@ -185,12 +225,7 @@ impl TableRef { #[doc(hidden)] pub fn sea_orm_table(&self) -> &DynIden { match self { - TableRef::Table(tbl) - | TableRef::SchemaTable(_, tbl) - | TableRef::DatabaseSchemaTable(_, _, tbl) - | TableRef::TableAlias(tbl, _) - | TableRef::SchemaTableAlias(_, tbl, _) - | TableRef::DatabaseSchemaTableAlias(_, _, tbl, _) + TableRef::Table(TableName(_, tbl), _) | TableRef::SubQuery(_, tbl) | TableRef::ValuesList(_, tbl) | TableRef::FunctionCall(_, tbl) => tbl, @@ -200,15 +235,10 @@ impl TableRef { #[doc(hidden)] pub fn sea_orm_table_alias(&self) -> Option<&DynIden> { match self { - TableRef::Table(_) - | TableRef::SchemaTable(_, _) - | TableRef::DatabaseSchemaTable(_, _, _) - | TableRef::SubQuery(_, _) - | TableRef::ValuesList(_, _) => None, - TableRef::TableAlias(_, alias) - | TableRef::SchemaTableAlias(_, _, alias) - | TableRef::DatabaseSchemaTableAlias(_, _, _, alias) - | TableRef::FunctionCall(_, alias) => Some(alias), + TableRef::Table(_, None) | TableRef::SubQuery(_, _) | TableRef::ValuesList(_, _) => { + None + } + TableRef::Table(_, Some(alias)) | TableRef::FunctionCall(_, alias) => Some(alias), } } } @@ -511,84 +541,130 @@ impl IntoColumnRef for ColumnRef { } } -impl IntoColumnRef for T +impl From for DatabaseName where T: IntoIden, { - fn into_column_ref(self) -> ColumnRef { - ColumnRef::Column(self.into_iden()) + fn from(iden: T) -> Self { + DatabaseName(iden.into_iden()) } } -impl IntoColumnRef for Asterisk { - fn into_column_ref(self) -> ColumnRef { - ColumnRef::Asterisk +impl From for SchemaName +where + T: IntoIden, +{ + fn from(iden: T) -> Self { + SchemaName(None, iden.into_iden()) } } -impl IntoColumnRef for (S, T) +impl From<(S, T)> for SchemaName where S: IntoIden, T: IntoIden, { - fn into_column_ref(self) -> ColumnRef { - ColumnRef::TableColumn(self.0.into_iden(), self.1.into_iden()) + fn from((db, schema): (S, T)) -> Self { + SchemaName(Some(db.into()), schema.into_iden()) } } -impl IntoColumnRef for (T, Asterisk) +impl From for TableName where T: IntoIden, { - fn into_column_ref(self) -> ColumnRef { - ColumnRef::TableAsterisk(self.0.into_iden()) + fn from(iden: T) -> Self { + TableName(None, iden.into_iden()) } } -impl IntoColumnRef for (S, T, U) +impl From<(S, T)> for TableName where S: IntoIden, T: IntoIden, - U: IntoIden, { - fn into_column_ref(self) -> ColumnRef { - ColumnRef::SchemaTableColumn(self.0.into_iden(), self.1.into_iden(), self.2.into_iden()) + fn from((schema, table): (S, T)) -> Self { + TableName(Some(schema.into()), table.into_iden()) } } -impl IntoTableRef for TableRef { - fn into_table_ref(self) -> TableRef { - self +impl From<(S, T, U)> for TableName +where + S: IntoIden, + T: IntoIden, + U: IntoIden, +{ + fn from((db, schema, table): (S, T, U)) -> Self { + TableName(Some((db, schema).into()), table.into_iden()) } } -impl IntoTableRef for T +impl From for ColumnName where T: IntoIden, { - fn into_table_ref(self) -> TableRef { - TableRef::Table(self.into_iden()) + fn from(iden: T) -> Self { + ColumnName(None, iden.into_iden()) } } -impl IntoTableRef for (S, T) +impl From<(S, T)> for ColumnName where S: IntoIden, T: IntoIden, { - fn into_table_ref(self) -> TableRef { - TableRef::SchemaTable(self.0.into_iden(), self.1.into_iden()) + fn from((table, column): (S, T)) -> Self { + ColumnName(Some(table.into()), column.into_iden()) } } -impl IntoTableRef for (S, T, U) +impl From<(S, T, U)> for ColumnName where S: IntoIden, T: IntoIden, U: IntoIden, +{ + fn from((schema, table, column): (S, T, U)) -> Self { + ColumnName(Some((schema, table).into()), column.into_iden()) + } +} + +impl IntoColumnRef for T +where + T: Into, +{ + fn into_column_ref(self) -> ColumnRef { + ColumnRef::Column(self.into()) + } +} + +impl IntoColumnRef for Asterisk { + fn into_column_ref(self) -> ColumnRef { + ColumnRef::Asterisk(None) + } +} + +impl IntoColumnRef for (T, Asterisk) +where + T: IntoIden, +{ + fn into_column_ref(self) -> ColumnRef { + ColumnRef::Asterisk(Some(self.0.into())) + } +} + +impl IntoTableRef for TableRef { + fn into_table_ref(self) -> TableRef { + self + } +} + +impl IntoTableRef for T +where + T: Into, { fn into_table_ref(self) -> TableRef { - TableRef::DatabaseSchemaTable(self.0.into_iden(), self.1.into_iden(), self.2.into_iden()) + TableRef::Table(self.into(), None) } } @@ -599,20 +675,7 @@ impl TableRef { A: IntoIden, { match self { - Self::Table(table) => Self::TableAlias(table, alias.into_iden()), - Self::TableAlias(table, _) => Self::TableAlias(table, alias.into_iden()), - Self::SchemaTable(schema, table) => { - Self::SchemaTableAlias(schema, table, alias.into_iden()) - } - Self::DatabaseSchemaTable(database, schema, table) => { - Self::DatabaseSchemaTableAlias(database, schema, table, alias.into_iden()) - } - Self::SchemaTableAlias(schema, table, _) => { - Self::SchemaTableAlias(schema, table, alias.into_iden()) - } - Self::DatabaseSchemaTableAlias(database, schema, table, _) => { - Self::DatabaseSchemaTableAlias(database, schema, table, alias.into_iden()) - } + Self::Table(table, _) => Self::Table(table, Some(alias.into_iden())), Self::SubQuery(statement, _) => Self::SubQuery(statement, alias.into_iden()), Self::ValuesList(values, _) => Self::ValuesList(values, alias.into_iden()), Self::FunctionCall(func, _) => Self::FunctionCall(func, alias.into_iden()), @@ -819,40 +882,40 @@ mod tests { type CharLocal = Character; assert_eq!( - ColumnRef::Column(Character::Id.into_iden()), - ColumnRef::Column(Character::Id.into_iden()) + ColumnRef::Column(Character::Id.into()), + ColumnRef::Column(Character::Id.into()) ); assert_eq!( - ColumnRef::Column(Character::Id.into_iden()), - ColumnRef::Column(Char::Id.into_iden()) + ColumnRef::Column(Character::Id.into()), + ColumnRef::Column(Char::Id.into()) ); assert_eq!( - ColumnRef::Column(Character::Id.into_iden()), - ColumnRef::Column(CharLocal::Id.into_iden()) + ColumnRef::Column(Character::Id.into()), + ColumnRef::Column(CharLocal::Id.into()) ); assert_eq!( - ColumnRef::Column(Character::Id.into_iden()), - ColumnRef::Column(CharReexport::Id.into_iden()) + ColumnRef::Column(Character::Id.into()), + ColumnRef::Column(CharReexport::Id.into()) ); assert_eq!( - ColumnRef::Column("id".into_iden()), - ColumnRef::Column("id".into_iden()) + ColumnRef::Column("id".into()), + ColumnRef::Column("id".into()) ); assert_ne!( - ColumnRef::Column("id".into_iden()), - ColumnRef::Column("id_".into_iden()) + ColumnRef::Column("id".into()), + ColumnRef::Column("id_".into()) ); assert_eq!( - ColumnRef::Column(Character::Id.into_iden()), - ColumnRef::Column("id".into_iden()) + ColumnRef::Column(Character::Id.into()), + ColumnRef::Column("id".into()) ); assert_ne!( - ColumnRef::Column(Character::Id.into_iden()), - ColumnRef::Column(Character::Table.into_iden()) + ColumnRef::Column(Character::Id.into()), + ColumnRef::Column(Character::Table.into()) ); assert_eq!( - ColumnRef::Column(Character::Id.into_iden()), - ColumnRef::Column(Font::Id.into_iden()) + ColumnRef::Column(Character::Id.into()), + ColumnRef::Column(Font::Id.into()) ); } } diff --git a/tests/sqlite/query.rs b/tests/sqlite/query.rs index 3f6af32c7..09452fb11 100644 --- a/tests/sqlite/query.rs +++ b/tests/sqlite/query.rs @@ -1818,13 +1818,11 @@ fn recursive_with_multiple_ctes() { .cte(sub_select2_cte); let mut main_sel2 = Query::select(); - main_sel2 - .expr(Expr::col(Asterisk)) - .from(TableRef::Table(sub_select2_name.into_iden())); + main_sel2.expr(Expr::col(Asterisk)).from(sub_select2_name); let mut main_sel1 = Query::select(); main_sel1 .expr(Expr::col(Asterisk)) - .from(TableRef::Table(sub_select1_name.into_iden())) + .from(sub_select1_name) .union(UnionType::All, main_sel2); let query = with.query(main_sel1);