Skip to main content

sql_orm/
query_alias.rs

1use sql_orm_core::{Entity, EntityColumn};
2use sql_orm_query::{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
86pub trait EntityColumnAliasExt<E: Entity> {
87    fn aliased(self, alias: &'static str) -> AliasedEntityColumn<E>;
88}
89
90impl<E: Entity> EntityColumnAliasExt<E> for EntityColumn<E> {
91    fn aliased(self, alias: &'static str) -> AliasedEntityColumn<E> {
92        AliasedEntityColumn::new(self, alias)
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::{AliasedEntityColumn, EntityColumnAliasExt};
99    use sql_orm_core::{
100        ColumnMetadata, Entity, EntityColumn, EntityMetadata, PrimaryKeyMetadata, SqlServerType,
101    };
102    use sql_orm_query::{ColumnRef, Expr, SelectProjection, TableRef};
103
104    struct TestEntity;
105
106    static TEST_ENTITY_COLUMNS: [ColumnMetadata; 1] = [ColumnMetadata {
107        rust_field: "name",
108        column_name: "name",
109        renamed_from: None,
110        sql_type: SqlServerType::NVarChar,
111        nullable: false,
112        primary_key: true,
113        identity: None,
114        default_sql: None,
115        computed_sql: None,
116        rowversion: false,
117        insertable: false,
118        updatable: false,
119        max_length: Some(120),
120        precision: None,
121        scale: None,
122    }];
123
124    static TEST_ENTITY_METADATA: EntityMetadata = EntityMetadata {
125        rust_name: "TestEntity",
126        schema: "dbo",
127        table: "test_entities",
128        renamed_from: None,
129        columns: &TEST_ENTITY_COLUMNS,
130        primary_key: PrimaryKeyMetadata {
131            name: None,
132            columns: &["name"],
133        },
134        indexes: &[],
135        foreign_keys: &[],
136        navigations: &[],
137    };
138
139    impl Entity for TestEntity {
140        fn metadata() -> &'static EntityMetadata {
141            &TEST_ENTITY_METADATA
142        }
143    }
144
145    #[allow(non_upper_case_globals)]
146    impl TestEntity {
147        const name: EntityColumn<TestEntity> = EntityColumn::new("name", "name");
148    }
149
150    #[test]
151    fn entity_column_can_be_bound_to_table_alias() {
152        let aliased: AliasedEntityColumn<TestEntity> = TestEntity::name.aliased("t");
153
154        assert_eq!(aliased.alias(), "t");
155        assert_eq!(aliased.column().rust_field(), TestEntity::name.rust_field());
156        assert_eq!(
157            aliased.column().column_name(),
158            TestEntity::name.column_name()
159        );
160        assert_eq!(
161            aliased.table_ref(),
162            TableRef::with_alias("dbo", "test_entities", "t")
163        );
164        assert_eq!(
165            aliased.column_ref(),
166            ColumnRef::new(
167                TableRef::with_alias("dbo", "test_entities", "t"),
168                "name",
169                "name"
170            )
171        );
172        assert_eq!(Expr::from(aliased), Expr::Column(aliased.column_ref()));
173        assert_eq!(
174            SelectProjection::from(aliased),
175            SelectProjection::expr_as(Expr::Column(aliased.column_ref()), "name")
176        );
177    }
178}