@@ -712,11 +712,15 @@ public SelectExpressionVerifyingExpressionVisitor(IEnumerable<TableReferenceExpr
712712 Visit ( childIdentifier . Column ) ;
713713 }
714714
715- break ;
715+ return selectExpression ;
716716
717717 case ConcreteColumnExpression concreteColumnExpression :
718718 concreteColumnExpression . Verify ( _tableReferencesInScope ) ;
719- break ;
719+ return concreteColumnExpression ;
720+
721+ case ShapedQueryExpression shapedQueryExpression :
722+ Verify ( shapedQueryExpression . QueryExpression , _tableReferencesInScope ) ;
723+ return shapedQueryExpression ;
720724 }
721725
722726 return base . Visit ( expression ) ;
@@ -727,5 +731,95 @@ public static void Verify(Expression expression, IEnumerable<TableReferenceExpre
727731 => new SelectExpressionVerifyingExpressionVisitor ( tableReferencesInScope )
728732 . Visit ( expression ) ;
729733 }
734+
735+ private sealed class CloningExpressionVisitor : ExpressionVisitor
736+ {
737+ [ return : NotNullIfNotNull ( "expression" ) ]
738+ public override Expression ? Visit ( Expression ? expression )
739+ {
740+ if ( expression is SelectExpression selectExpression )
741+ {
742+ // We ignore projection binding related elements as we don't want to copy them over for top level
743+ // Nested level will have _projection populated and no binding elements
744+ var newProjections = selectExpression . _projection . Select ( Visit ) . ToList < ProjectionExpression > ( ) ;
745+
746+ var newTables = selectExpression . _tables . Select ( Visit ) . ToList < TableExpressionBase > ( ) ;
747+ // Since we are cloning we need to generate new table references
748+ // In other cases (like VisitChildren), we just reuse the same table references and update the SelectExpression inside it.
749+ // We initially assign old SelectExpression in table references and later update it once we construct clone
750+ var newTableReferences = selectExpression . _tableReferences
751+ . Select ( e => new TableReferenceExpression ( selectExpression , e . Alias ) ) . ToList ( ) ;
752+ Check . DebugAssert (
753+ newTables . Select ( e => GetAliasFromTableExpressionBase ( e ) ) . SequenceEqual ( newTableReferences . Select ( e => e . Alias ) ) ,
754+ "Alias of updated tables must match the old tables." ) ;
755+
756+ var predicate = ( SqlExpression ? ) Visit ( selectExpression . Predicate ) ;
757+ var newGroupBy = selectExpression . _groupBy . Select ( Visit )
758+ . Where ( e => ! ( e is SqlConstantExpression || e is SqlParameterExpression ) )
759+ . ToList < SqlExpression > ( ) ;
760+ var havingExpression = ( SqlExpression ? ) Visit ( selectExpression . Having ) ;
761+ var newOrderings = selectExpression . _orderings . Select ( Visit ) . ToList < OrderingExpression > ( ) ;
762+ var offset = ( SqlExpression ? ) Visit ( selectExpression . Offset ) ;
763+ var limit = ( SqlExpression ? ) Visit ( selectExpression . Limit ) ;
764+
765+ var newSelectExpression = new SelectExpression ( selectExpression . Alias , newProjections , newTables , newTableReferences , newGroupBy , newOrderings )
766+ {
767+ Predicate = predicate ,
768+ Having = havingExpression ,
769+ Offset = offset ,
770+ Limit = limit ,
771+ IsDistinct = selectExpression . IsDistinct ,
772+ Tags = selectExpression . Tags ,
773+ _usedAliases = selectExpression . _usedAliases . ToHashSet ( )
774+ } ;
775+
776+ newSelectExpression . _tptLeftJoinTables . AddRange ( selectExpression . _tptLeftJoinTables ) ;
777+ // Since identifiers are ColumnExpression, they are not visited since they don't contain SelectExpression inside it.
778+ newSelectExpression . _identifier . AddRange ( selectExpression . _identifier ) ;
779+ newSelectExpression . _childIdentifiers . AddRange ( selectExpression . _childIdentifiers ) ;
780+
781+ // Remap tableReferences in new select expression
782+ foreach ( var tableReference in newTableReferences )
783+ {
784+ tableReference . UpdateTableReference ( selectExpression , newSelectExpression ) ;
785+ }
786+
787+ // Now that we have SelectExpression, we visit all components and update table references inside columns
788+ newSelectExpression = ( SelectExpression ) new ColumnExpressionReplacingExpressionVisitor ( selectExpression , newSelectExpression )
789+ . Visit ( newSelectExpression ) ;
790+
791+ return newSelectExpression ;
792+
793+ }
794+
795+ return expression is ICloneable cloneable ? ( Expression ) cloneable . Clone ( ) : base . Visit ( expression ) ;
796+ }
797+ }
798+
799+ private sealed class ColumnExpressionReplacingExpressionVisitor : ExpressionVisitor
800+ {
801+ private readonly SelectExpression _oldSelectExpression ;
802+ private readonly Dictionary < string , TableReferenceExpression > _newTableReferences ;
803+
804+ public ColumnExpressionReplacingExpressionVisitor ( SelectExpression oldSelectExpression , SelectExpression newSelectExpression )
805+ {
806+ _oldSelectExpression = oldSelectExpression ;
807+ _newTableReferences = newSelectExpression . _tableReferences . ToDictionary ( e => e . Alias ) ;
808+ }
809+
810+ [ return : NotNullIfNotNull ( "expression" ) ]
811+ public override Expression ? Visit ( Expression ? expression )
812+ {
813+ return expression is ConcreteColumnExpression concreteColumnExpression
814+ && _oldSelectExpression . ContainsTableReference ( concreteColumnExpression )
815+ ? new ConcreteColumnExpression (
816+ concreteColumnExpression . Name ,
817+ _newTableReferences [ concreteColumnExpression . TableAlias ] ,
818+ concreteColumnExpression . Type ,
819+ concreteColumnExpression . TypeMapping ! ,
820+ concreteColumnExpression . IsNullable )
821+ : base . Visit ( expression ) ;
822+ }
823+ }
730824 }
731825}
0 commit comments