@@ -75,6 +75,8 @@ pub enum ASTNode {
7575 projection : Vec < ASTNode > ,
7676 /// FROM
7777 relation : Option < Box < ASTNode > > ,
78+ // JOIN
79+ joins : Vec < Join > ,
7880 /// WHERE
7981 selection : Option < Box < ASTNode > > ,
8082 /// ORDER BY
@@ -167,10 +169,16 @@ impl ToString for ASTNode {
167169 . collect:: <Vec <String >>( )
168170 . join( ", " )
169171 ) ,
170- ASTNode :: SQLCase { conditions, results, else_result } => {
172+ ASTNode :: SQLCase {
173+ conditions,
174+ results,
175+ else_result,
176+ } => {
171177 let mut s = format ! (
172178 "CASE {}" ,
173- conditions. iter( ) . zip( results)
179+ conditions
180+ . iter( )
181+ . zip( results)
174182 . map( |( c, r) | format!( "WHEN {} THEN {}" , c. to_string( ) , r. to_string( ) ) )
175183 . collect:: <Vec <String >>( )
176184 . join( " " )
@@ -179,10 +187,11 @@ impl ToString for ASTNode {
179187 s += & format ! ( " ELSE {}" , else_result. to_string( ) )
180188 }
181189 s + " END"
182- } ,
190+ }
183191 ASTNode :: SQLSelect {
184192 projection,
185193 relation,
194+ joins,
186195 selection,
187196 order_by,
188197 group_by,
@@ -200,6 +209,9 @@ impl ToString for ASTNode {
200209 if let Some ( relation) = relation {
201210 s += & format ! ( " FROM {}" , relation. as_ref( ) . to_string( ) ) ;
202211 }
212+ for join in joins {
213+ s += & join. to_string ( ) ;
214+ }
203215 if let Some ( selection) = selection {
204216 s += & format ! ( " WHERE {}" , selection. as_ref( ) . to_string( ) ) ;
205217 }
@@ -402,3 +414,72 @@ impl ToString for SQLColumnDef {
402414 s
403415 }
404416}
417+
418+ #[ derive( Debug , Clone , PartialEq ) ]
419+ pub struct Join {
420+ pub relation : ASTNode ,
421+ pub join_operator : JoinOperator ,
422+ }
423+
424+ impl ToString for Join {
425+ fn to_string ( & self ) -> String {
426+ fn prefix ( constraint : & JoinConstraint ) -> String {
427+ match constraint {
428+ JoinConstraint :: Natural => "NATURAL " . to_string ( ) ,
429+ _ => "" . to_string ( ) ,
430+ }
431+ }
432+ fn suffix ( constraint : & JoinConstraint ) -> String {
433+ match constraint {
434+ JoinConstraint :: On ( expr) => format ! ( " ON({})" , expr. to_string( ) ) ,
435+ JoinConstraint :: Using ( attrs) => format ! ( " USING({})" , attrs. join( ", " ) ) ,
436+ _ => "" . to_string ( ) ,
437+ }
438+ }
439+ match & self . join_operator {
440+ JoinOperator :: Inner ( constraint) => format ! (
441+ "{}INNER JOIN {}{}" ,
442+ prefix( constraint) ,
443+ self . relation. to_string( ) ,
444+ prefix( constraint)
445+ ) ,
446+ JoinOperator :: Cross => format ! ( "CROSS JOIN {}" , self . relation. to_string( ) ) ,
447+ JoinOperator :: Implicit => format ! ( ", {}" , self . relation. to_string( ) ) ,
448+ JoinOperator :: LeftOuter ( constraint) => format ! (
449+ "{}LEFT OUTER JOIN {}{}" ,
450+ prefix( constraint) ,
451+ self . relation. to_string( ) ,
452+ suffix( constraint)
453+ ) ,
454+ JoinOperator :: RightOuter ( constraint) => format ! (
455+ "{}RIGHT OUTER JOIN {}{}" ,
456+ prefix( constraint) ,
457+ self . relation. to_string( ) ,
458+ suffix( constraint)
459+ ) ,
460+ JoinOperator :: FullOuter ( constraint) => format ! (
461+ "{}FULL OUTER JOIN {}{}" ,
462+ prefix( constraint) ,
463+ self . relation. to_string( ) ,
464+ suffix( constraint)
465+ ) ,
466+ }
467+ }
468+ }
469+
470+ #[ derive( Debug , Clone , PartialEq ) ]
471+ pub enum JoinOperator {
472+ Inner ( JoinConstraint ) ,
473+ LeftOuter ( JoinConstraint ) ,
474+ RightOuter ( JoinConstraint ) ,
475+ FullOuter ( JoinConstraint ) ,
476+ Implicit ,
477+ Cross ,
478+ }
479+
480+ #[ derive( Debug , Clone , PartialEq ) ]
481+ pub enum JoinConstraint {
482+ On ( ASTNode ) ,
483+ Using ( Vec < String > ) ,
484+ Natural ,
485+ }
0 commit comments