Skip to main content

sql_orm/
query_alias.rs

1use sql_orm_core::{Entity, EntityColumn};
2use sql_orm_query::{AggregateProjection, ColumnRef, Expr, SelectProjection, TableRef};
3use std::marker::PhantomData;
4
5/// Public column reference bound to a SQL table alias.
6#[derive(Debug)]
7pub struct AliasedEntityColumn<E: Entity> {
8    rust_field: &'static str,
9    column_name: &'static str,
10    alias: &'static str,
11    _entity: PhantomData<fn() -> E>,
12}
13
14impl<E: Entity> Copy for AliasedEntityColumn<E> {}
15
16impl<E: Entity> Clone for AliasedEntityColumn<E> {
17    fn clone(&self) -> Self {
18        *self
19    }
20}
21
22impl<E: Entity> PartialEq for AliasedEntityColumn<E> {
23    fn eq(&self, other: &Self) -> bool {
24        self.rust_field == other.rust_field
25            && self.column_name == other.column_name
26            && self.alias == other.alias
27    }
28}
29
30impl<E: Entity> Eq for AliasedEntityColumn<E> {}
31
32impl<E: Entity> AliasedEntityColumn<E> {
33    pub const fn new(column: EntityColumn<E>, alias: &'static str) -> Self {
34        Self {
35            rust_field: column.rust_field(),
36            column_name: column.column_name(),
37            alias,
38            _entity: PhantomData,
39        }
40    }
41
42    pub const fn column(&self) -> EntityColumn<E> {
43        EntityColumn::new(self.rust_field, self.column_name)
44    }
45
46    pub const fn alias(&self) -> &'static str {
47        self.alias
48    }
49
50    pub fn column_ref(self) -> ColumnRef {
51        ColumnRef::new(
52            TableRef::for_entity_as::<E>(self.alias),
53            self.rust_field,
54            self.column_name,
55        )
56    }
57
58    pub fn expr(self) -> Expr {
59        Expr::Column(self.column_ref())
60    }
61
62    pub fn table_ref(self) -> TableRef {
63        TableRef::for_entity_as::<E>(self.alias)
64    }
65}
66
67impl<E: Entity> From<AliasedEntityColumn<E>> for ColumnRef {
68    fn from(value: AliasedEntityColumn<E>) -> Self {
69        value.column_ref()
70    }
71}
72
73impl<E: Entity> From<AliasedEntityColumn<E>> for Expr {
74    fn from(value: AliasedEntityColumn<E>) -> Self {
75        value.expr()
76    }
77}
78
79impl<E: Entity> From<AliasedEntityColumn<E>> for SelectProjection {
80    fn from(value: AliasedEntityColumn<E>) -> Self {
81        let column_name = value.column_name;
82        SelectProjection::expr_as(value.expr(), column_name)
83    }
84}
85
86impl<E: Entity> From<AliasedEntityColumn<E>> for AggregateProjection {
87    fn from(value: AliasedEntityColumn<E>) -> Self {
88        let column_name = value.column_name;
89        AggregateProjection::group_key_as(value.expr(), column_name)
90    }
91}
92
93pub trait EntityColumnAliasExt<E: Entity> {
94    fn aliased(self, alias: &'static str) -> AliasedEntityColumn<E>;
95}
96
97impl<E: Entity> EntityColumnAliasExt<E> for EntityColumn<E> {
98    fn aliased(self, alias: &'static str) -> AliasedEntityColumn<E> {
99        AliasedEntityColumn::new(self, alias)
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::{AliasedEntityColumn, EntityColumnAliasExt};
106    use sql_orm_core::{
107        ColumnMetadata, Entity, EntityColumn, EntityMetadata, PrimaryKeyMetadata, SqlServerType,
108    };
109    use sql_orm_query::{AggregateProjection, ColumnRef, Expr, SelectProjection, TableRef};
110
111    struct TestEntity;
112
113    static TEST_ENTITY_COLUMNS: [ColumnMetadata; 1] = [ColumnMetadata {
114        rust_field: "name",
115        column_name: "name",
116        renamed_from: None,
117        sql_type: SqlServerType::NVarChar,
118        nullable: false,
119        primary_key: true,
120        identity: None,
121        default_sql: None,
122        computed_sql: None,
123        rowversion: false,
124        insertable: false,
125        updatable: false,
126        max_length: Some(120),
127        precision: None,
128        scale: None,
129    }];
130
131    static TEST_ENTITY_METADATA: EntityMetadata = EntityMetadata {
132        rust_name: "TestEntity",
133        schema: "dbo",
134        table: "test_entities",
135        renamed_from: None,
136        columns: &TEST_ENTITY_COLUMNS,
137        primary_key: PrimaryKeyMetadata {
138            name: None,
139            columns: &["name"],
140        },
141        indexes: &[],
142        foreign_keys: &[],
143        navigations: &[],
144    };
145
146    impl Entity for TestEntity {
147        fn metadata() -> &'static EntityMetadata {
148            &TEST_ENTITY_METADATA
149        }
150    }
151
152    #[allow(non_upper_case_globals)]
153    impl TestEntity {
154        const name: EntityColumn<TestEntity> = EntityColumn::new("name", "name");
155    }
156
157    #[test]
158    fn entity_column_can_be_bound_to_table_alias() {
159        let aliased: AliasedEntityColumn<TestEntity> = TestEntity::name.aliased("t");
160
161        assert_eq!(aliased.alias(), "t");
162        assert_eq!(aliased.column().rust_field(), TestEntity::name.rust_field());
163        assert_eq!(
164            aliased.column().column_name(),
165            TestEntity::name.column_name()
166        );
167        assert_eq!(
168            aliased.table_ref(),
169            TableRef::with_alias("dbo", "test_entities", "t")
170        );
171        assert_eq!(
172            aliased.column_ref(),
173            ColumnRef::new(
174                TableRef::with_alias("dbo", "test_entities", "t"),
175                "name",
176                "name"
177            )
178        );
179        assert_eq!(Expr::from(aliased), Expr::Column(aliased.column_ref()));
180        assert_eq!(
181            SelectProjection::from(aliased),
182            SelectProjection::expr_as(Expr::Column(aliased.column_ref()), "name")
183        );
184        assert_eq!(
185            AggregateProjection::from(aliased),
186            AggregateProjection::group_key_as(Expr::Column(aliased.column_ref()), "name")
187        );
188    }
189}