@@ -76,17 +76,102 @@ impl ValidGrouping<()> for DummyExpression {
7676 type IsAggregate = is_aggregate:: No ;
7777}
7878
79+ /// A fixed size string for the table alias. We want to make sure that
80+ /// converting these to `&str` doesn't allocate and that they are small
81+ /// enough that the `Table` struct is only 16 bytes and can be `Copy`
82+ #[ derive( Debug , Clone , Copy ) ]
83+ pub struct ChildAliasStr {
84+ alias : [ u8 ; 4 ] ,
85+ }
86+
87+ impl ChildAliasStr {
88+ fn new ( idx : u8 ) -> Self {
89+ let c = 'i' as u8 ;
90+ let alias = if idx == 0 {
91+ [ c, 0 , 0 , 0 ]
92+ } else if idx < 10 {
93+ let ones = char:: from_digit ( idx as u32 , 10 ) . unwrap ( ) as u8 ;
94+ [ c, ones, 0 , 0 ]
95+ } else if idx < 100 {
96+ let tens = char:: from_digit ( ( idx / 10 ) as u32 , 10 ) . unwrap ( ) as u8 ;
97+ let ones = char:: from_digit ( ( idx % 10 ) as u32 , 10 ) . unwrap ( ) as u8 ;
98+ [ c, tens, ones, 0 ]
99+ } else {
100+ let hundreds = char:: from_digit ( ( idx / 100 ) as u32 , 10 ) . unwrap ( ) as u8 ;
101+ let idx = idx % 100 ;
102+ let tens = char:: from_digit ( ( idx / 10 ) as u32 , 10 ) . unwrap ( ) as u8 ;
103+ let ones = char:: from_digit ( ( idx % 10 ) as u32 , 10 ) . unwrap ( ) as u8 ;
104+ [ c, hundreds, tens, ones]
105+ } ;
106+ ChildAliasStr { alias }
107+ }
108+
109+ fn as_str ( & self ) -> & str {
110+ let alias = if self . alias [ 1 ] == 0 {
111+ return "i" ;
112+ } else if self . alias [ 2 ] == 0 {
113+ & self . alias [ ..2 ]
114+ } else if self . alias [ 3 ] == 0 {
115+ & self . alias [ ..3 ]
116+ } else {
117+ & self . alias
118+ } ;
119+ unsafe { std:: str:: from_utf8_unchecked ( alias) }
120+ }
121+ }
122+
123+ /// A table alias. We use `c` as the main table alias and `i`, `i1`, `i2`,
124+ /// ... for child tables. The fact that we use these specific letters is
125+ /// historical and doesn't have any meaning.
126+ #[ derive( Debug , Clone , Copy ) ]
127+ pub enum Alias {
128+ Main ,
129+ Child ( ChildAliasStr ) ,
130+ }
131+
132+ impl Alias {
133+ fn as_str ( & self ) -> & str {
134+ match self {
135+ Alias :: Main => "c" ,
136+ Alias :: Child ( idx) => idx. as_str ( ) ,
137+ }
138+ }
139+
140+ fn child ( idx : u8 ) -> Self {
141+ Alias :: Child ( ChildAliasStr :: new ( idx) )
142+ }
143+ }
144+
145+ #[ test]
146+ fn alias ( ) {
147+ assert_eq ! ( Alias :: Main . as_str( ) , "c" ) ;
148+ assert_eq ! ( Alias :: Child ( ChildAliasStr :: new( 0 ) ) . as_str( ) , "i" ) ;
149+ assert_eq ! ( Alias :: Child ( ChildAliasStr :: new( 1 ) ) . as_str( ) , "i1" ) ;
150+ assert_eq ! ( Alias :: Child ( ChildAliasStr :: new( 10 ) ) . as_str( ) , "i10" ) ;
151+ assert_eq ! ( Alias :: Child ( ChildAliasStr :: new( 100 ) ) . as_str( ) , "i100" ) ;
152+ assert_eq ! ( Alias :: Child ( ChildAliasStr :: new( 255 ) ) . as_str( ) , "i255" ) ;
153+ }
154+
79155#[ derive( Debug , Clone , Copy ) ]
80156/// A wrapper around the `super::Table` struct that provides helper
81157/// functions for generating SQL queries
82158pub struct Table < ' a > {
83159 /// The metadata for this table
84160 meta : & ' a super :: Table ,
161+ alias : Alias ,
85162}
86163
87164impl < ' a > Table < ' a > {
88165 pub ( crate ) fn new ( meta : & ' a super :: Table ) -> Self {
89- Self { meta }
166+ Self {
167+ meta,
168+ alias : Alias :: Main ,
169+ }
170+ }
171+
172+ pub fn child ( mut self , idx : u8 ) -> Self {
173+ self . alias = Alias :: child ( idx) ;
174+ self
90175 }
91176
92177 /// Reference a column in this table and use the correct SQL type `ST`
@@ -201,7 +286,7 @@ impl<'a> Table<'a> {
201286 table : & ' b Table < ' b > ,
202287 column : & ' b RelColumn ,
203288 ) {
204- let name = format ! ( "{}.{}::text" , table. meta . qualified_name , & column. name) ;
289+ let name = format ! ( "{}.{}::text" , table. alias . as_str ( ) , & column. name) ;
205290
206291 match ( column. is_list ( ) , column. is_nullable ( ) ) {
207292 ( true , true ) => select. add_field ( sql :: < Nullable < Array < Text > > > ( & name) ) ,
@@ -237,12 +322,30 @@ impl<'a> Table<'a> {
237322 }
238323}
239324
325+ pub struct FromTable < ' a > ( Table < ' a > ) ;
326+
327+ impl < ' a , DB > QueryFragment < DB > for FromTable < ' a >
328+ where
329+ DB : Backend ,
330+ {
331+ fn walk_ast < ' b > ( & ' b self , mut out : AstPass < ' _ , ' b , DB > ) -> QueryResult < ( ) > {
332+ out. unsafe_to_cache_prepared ( ) ;
333+
334+ out. push_identifier ( self . 0 . meta . nsp . as_str ( ) ) ?;
335+ out. push_sql ( "." ) ;
336+ out. push_identifier ( & self . 0 . meta . name ) ?;
337+ out. push_sql ( " as " ) ;
338+ out. push_sql ( self . 0 . alias . as_str ( ) ) ;
339+ Ok ( ( ) )
340+ }
341+ }
342+
240343impl < ' a > QuerySource for Table < ' a > {
241- type FromClause = Self ;
344+ type FromClause = FromTable < ' a > ;
242345 type DefaultSelection = DummyExpression ;
243346
244- fn from_clause ( & self ) -> Self {
245- self . clone ( )
347+ fn from_clause ( & self ) -> FromTable < ' a > {
348+ FromTable ( * self )
246349 }
247350
248351 fn default_selection ( & self ) -> Self :: DefaultSelection {
@@ -285,9 +388,7 @@ where
285388 fn walk_ast < ' b > ( & ' b self , mut out : AstPass < ' _ , ' b , DB > ) -> QueryResult < ( ) > {
286389 out. unsafe_to_cache_prepared ( ) ;
287390
288- out. push_identifier ( self . meta . nsp . as_str ( ) ) ?;
289- out. push_sql ( "." ) ;
290- out. push_identifier ( & self . meta . name ) ?;
391+ out. push_sql ( self . alias . as_str ( ) ) ;
291392 Ok ( ( ) )
292393 }
293394}
0 commit comments