8282import com .starrocks .sql .ast .PartitionKeyDesc ;
8383import com .starrocks .sql .ast .PartitionValue ;
8484import com .starrocks .sql .ast .QualifiedName ;
85+ import com .starrocks .sql .ast .QueryRelation ;
8586import com .starrocks .sql .ast .QueryStatement ;
8687import com .starrocks .sql .ast .RangePartitionDesc ;
8788import com .starrocks .sql .ast .Relation ;
140141
141142import java .time .LocalDateTime ;
142143import java .time .format .DateTimeFormatter ;
144+ import java .util .ArrayDeque ;
143145import java .util .ArrayList ;
144146import java .util .Arrays ;
145147import java .util .Collections ;
148+ import java .util .Deque ;
146149import java .util .HashMap ;
147150import java .util .HashSet ;
148151import java .util .Iterator ;
@@ -738,6 +741,8 @@ public static Map<TableName, Table> collectAllTable(StatementBase statementBase)
738741
739742 private static class TableCollector extends AstTraverser <Void , Void > {
740743 protected Map <TableName , Table > tables ;
744+ private final Deque <Set <String >> cteNameScopes = new ArrayDeque <>();
745+ private final Deque <Boolean > recursiveCteScopes = new ArrayDeque <>();
741746
742747 public TableCollector () {
743748 this .tables = Maps .newHashMap ();
@@ -754,6 +759,36 @@ public Void visitQueryStatement(QueryStatement statement, Void context) {
754759 return visit (statement .getQueryRelation ());
755760 }
756761
762+ @ Override
763+ public Void visitSelect (SelectRelation node , Void context ) {
764+ if (!node .hasWithClause ()) {
765+ return super .visitSelect (node , context );
766+ }
767+ cteNameScopes .push (new HashSet <>());
768+ recursiveCteScopes .push (node .isHasRecursiveCTE ());
769+ try {
770+ return super .visitSelect (node , context );
771+ } finally {
772+ recursiveCteScopes .pop ();
773+ cteNameScopes .pop ();
774+ }
775+ }
776+
777+ @ Override
778+ public Void visitSetOp (SetOperationRelation node , Void context ) {
779+ if (!node .hasWithClause ()) {
780+ return super .visitSetOp (node , context );
781+ }
782+ cteNameScopes .push (new HashSet <>());
783+ recursiveCteScopes .push (node .isHasRecursiveCTE ());
784+ try {
785+ return super .visitSetOp (node , context );
786+ } finally {
787+ recursiveCteScopes .pop ();
788+ cteNameScopes .pop ();
789+ }
790+ }
791+
757792 // ------------------------------------------- DML Statement -------------------------------------------------------
758793
759794 @ Override
@@ -790,10 +825,79 @@ public Void visitMergeIntoStatement(MergeIntoStmt node, Void context) {
790825
791826 @ Override
792827 public Void visitTable (TableRelation node , Void context ) {
828+ if (isUnresolvedCteReference (node )) {
829+ return null ;
830+ }
793831 Table table = node .getTable ();
794832 tables .put (node .getName (), table );
795833 return null ;
796834 }
835+
836+ @ Override
837+ public Void visitCTE (CTERelation node , Void context ) {
838+ if (node .isRecursive () && !node .isAnchor ()) {
839+ return null ;
840+ }
841+ if (cteNameScopes .isEmpty ()) {
842+ return super .visitCTE (node , context );
843+ }
844+ if (isInRecursiveCteScope () && node .getCteQueryStatement ().getQueryRelation ()
845+ instanceof SetOperationRelation setOperationRelation ) {
846+ visitRecursiveCteDefinition (node , setOperationRelation , context );
847+ } else {
848+ visit (node .getCteQueryStatement (), context );
849+ }
850+ addCteName (cteNameScopes .peek (), node );
851+ return null ;
852+ }
853+
854+ private void visitRecursiveCteDefinition (CTERelation cteRelation , SetOperationRelation setOperationRelation ,
855+ Void context ) {
856+ List <QueryRelation > relations = setOperationRelation .getRelations ();
857+ if (relations .isEmpty ()) {
858+ return ;
859+ }
860+ visit (relations .get (0 ), context );
861+
862+ Set <String > recursiveNames = new HashSet <>();
863+ addCteName (recursiveNames , cteRelation );
864+ cteNameScopes .push (recursiveNames );
865+ try {
866+ for (int i = 1 ; i < relations .size (); i ++) {
867+ visit (relations .get (i ), context );
868+ }
869+ } finally {
870+ cteNameScopes .pop ();
871+ }
872+ }
873+
874+ private boolean isInRecursiveCteScope () {
875+ return !recursiveCteScopes .isEmpty () && recursiveCteScopes .peek ();
876+ }
877+
878+ private void addCteName (Set <String > names , CTERelation cteRelation ) {
879+ if (!Strings .isNullOrEmpty (cteRelation .getName ())) {
880+ names .add (cteRelation .getName ());
881+ }
882+ }
883+
884+ private boolean isUnresolvedCteReference (TableRelation node ) {
885+ if (node .getTable () != null || cteNameScopes .isEmpty ()) {
886+ return false ;
887+ }
888+ TableName tableName = node .getName ();
889+ if (tableName == null || !Strings .isNullOrEmpty (tableName .getCatalog ()) ||
890+ !Strings .isNullOrEmpty (tableName .getDb ()) || Strings .isNullOrEmpty (tableName .getTbl ())) {
891+ return false ;
892+ }
893+ String tableNameWithoutDb = tableName .getTbl ();
894+ for (Set <String > cteNames : cteNameScopes ) {
895+ if (cteNames .contains (tableNameWithoutDb )) {
896+ return true ;
897+ }
898+ }
899+ return false ;
900+ }
797901 }
798902
799903 public static Map <TableName , Table > collectAllTableWithAlias (StatementBase statementBase ) {
0 commit comments