class CteScope extends AnyRef
The CteScope is responsible for keeping track of visible and known CTE definitions at a given stage of a SQL query/DataFrame program resolution. These scopes are stacked and the stack is managed by the CteRegistry. The scope is created per single WITH clause.
The CTE operators are:
- UnresolvedWith. This is a
hostoperator that contains a list of unresolved CTE definitions from the WITH clause and a single child operator, which is the actual unresolved SELECT query. - UnresolvedRelation. This is a generic unresolved relation operator that will sometimes be resolved to a CTE definition and later replaced with a CTERelationRef. The CTE takes precedence over a regular table or a view when resolving this identifier.
- CTERelationDef. This is a reusable logical plan, which will later be referenced by the lower CTE definitions and UnresolvedWith child.
- CTERelationRef. This is a leaf node similar to a relation operator that references a certain CTERelationDef by its ID. It has a name (unique locally for a WITH clause list) and an ID (unique for all the CTEs in a query).
- WithCTE. This is a
hostoperator that contains a list of resolved CTE definitions from the WITH clause and a single child operator, which is the actual resolved SELECT query.
The task of the Resolver is to correctly place WithCTE with CTERelationDefs inside and make sure that CTERelationRefs correctly reference CTERelationDefs with their IDs. The decision whether to inline those CTE subtrees or not is made by the Optimizer, unlike what Spark does for the Views (always inline during the analysis).
There are some caveats in how Spark places those operators and resolves their names:
- Ambiguous CTE definition names are disallowed only within a single WITH clause, and this is validated by the Parser in AstBuilder using QueryParsingErrors.duplicateCteDefinitionNamesError:
-- This is disallowed. WITH cte AS (SELECT 1), cte AS (SELECT 2) SELECT * FROM cte;
- When UnresolvedRelation identifier is resolved to a CTERelationDef and there is a name conflict on several layers of CTE definitions, the lower definitions take precedence:
-- The result is `3`, lower [[CTERelationDef]] takes precedence. WITH cte AS ( SELECT 1 ) SELECT * FROM ( WITH cte AS ( SELECT 2 ) SELECT * FROM ( WITH cte AS ( SELECT 3 ) SELECT * FROM cte ) )
- Any subquery can contain UnresolvedWith on top of it, but WithCTE is not gonna be 1 to 1 to its unresolved counterpart. For example, if we are dealing with simple subqueries, CTERelationDefs will be merged together under a single WithCTE. The previous example would produce the following resolved plan:
WithCTE :- CTERelationDef 18, false : +- ... :- CTERelationDef 19, false : +- ... :- CTERelationDef 20, false : +- ... +- Project [3#1203] : +- ...
- However, if we have any expression subquery (scalar/IN/EXISTS...), the top CTERelationDefs and subquery's CTERelationDef won't be merged together (as they are separated by an expression tree):
WITH cte AS ( SELECT 1 AS col1 ) SELECT * FROM cte WHERE col1 IN ( WITH cte AS ( SELECT 2 ) SELECT * FROM cte )
->
WithCTE :- CTERelationDef 21, false : +- ... +- Project [col1#1223] +- Filter col1#1223 IN (list#1222 []) : +- WithCTE : :- CTERelationDef 22, false : : +- ... : +- Project [2#1241] : +- ... +- ...
- Upper CTEs are visible through subqueries and can be referenced by lower operators, but not through the View boundary:
CREATE VIEW v1 AS SELECT 1; CREATE VIEW v2 AS SELECT * FROM v1; -- The result is 1. -- The `v2` body will be inlined in the main query tree during the analysis, but upper `v1` -- CTE definition _won't_ take precedence over the lower `v1` view. WITH v1 AS ( SELECT 2 ) SELECT * FROM v2;
- Alphabetic
- By Inheritance
- CteScope
- AnyRef
- Any
- Hide All
- Show All
- Public
- Protected
Instance Constructors
- new CteScope(isRoot: Boolean, isOpaque: Boolean)
- isRoot
This marks the place where WithCTE has to be placed with all the merged CTERelationDef that were collected under it. It will be true for root query, Views and expression subqueries.
- isOpaque
This flag makes this CteScope opaque for CTERelationDef lookups. It will be true for root query and Views.
Value Members
- final def !=(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def ##: Int
- Definition Classes
- AnyRef → Any
- final def ==(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def asInstanceOf[T0]: T0
- Definition Classes
- Any
- def clone(): AnyRef
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.CloneNotSupportedException]) @IntrinsicCandidate() @native()
- final def eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- def equals(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef → Any
- final def getClass(): Class[_ <: AnyRef]
- Definition Classes
- AnyRef → Any
- Annotations
- @IntrinsicCandidate() @native()
- def getCte(name: String): Option[CTERelationDef]
Get a visible CTE definition by its name.
- def getKnownCtes: Seq[CTERelationDef]
Get all known (from this and child scopes) CTERelationDefs.
Get all known (from this and child scopes) CTERelationDefs. This is used to construct WithCTE from a root scope.
- def hashCode(): Int
- Definition Classes
- AnyRef → Any
- Annotations
- @IntrinsicCandidate() @native()
- final def isInstanceOf[T0]: Boolean
- Definition Classes
- Any
- val isOpaque: Boolean
- val isRoot: Boolean
- def mergeChildScope(childScope: CteScope): Unit
Merge the state from a child scope.
Merge the state from a child scope. We transfer all the known CTE definitions to later merge them in one WithCTE. Root scopes terminate this chain, since they have their own WithCTE.
- final def ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- final def notify(): Unit
- Definition Classes
- AnyRef
- Annotations
- @IntrinsicCandidate() @native()
- final def notifyAll(): Unit
- Definition Classes
- AnyRef
- Annotations
- @IntrinsicCandidate() @native()
- def registerCte(name: String, cteDef: CTERelationDef): Unit
Register a new CTE definition in this scope.
Register a new CTE definition in this scope. Since the scope is created per single WITH clause, there can be no name conflicts, but this is validated by the Parser in AstBuilder using QueryParsingErrors.duplicateCteDefinitionNamesError. This definition will be both known and visible.
- final def synchronized[T0](arg0: => T0): T0
- Definition Classes
- AnyRef
- def toString(): String
- Definition Classes
- AnyRef → Any
- final def wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException]) @native()
- final def wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
Deprecated Value Members
- def finalize(): Unit
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.Throwable]) @Deprecated
- Deprecated
(Since version 9)