Packages

class NameScopeStack extends SQLConfHelper

The NameScopeStack is a stack of NameScopes managed by the Resolver. Usually the current scope is used for name resolution, but in case of correlated subqueries we can lookup names in the parent scopes. Low-level scope creation is managed internally, and only high-level api like pushScope and popScope is available to the resolvers. Freshly-created NameScopeStack contains an empty root NameScope, which in the context of Resolver corresponds to the query output.

Linear Supertypes
SQLConfHelper, AnyRef, Any
Ordering
  1. Alphabetic
  2. By Inheritance
Inherited
  1. NameScopeStack
  2. SQLConfHelper
  3. AnyRef
  4. Any
  1. Hide All
  2. Show All
Visibility
  1. Public
  2. Protected

Instance Constructors

  1. new NameScopeStack(planLogger: PlanLogger = new PlanLogger)

Value Members

  1. final def !=(arg0: Any): Boolean
    Definition Classes
    AnyRef → Any
  2. final def ##: Int
    Definition Classes
    AnyRef → Any
  3. final def ==(arg0: Any): Boolean
    Definition Classes
    AnyRef → Any
  4. final def asInstanceOf[T0]: T0
    Definition Classes
    Any
  5. def clone(): AnyRef
    Attributes
    protected[lang]
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.CloneNotSupportedException]) @IntrinsicCandidate() @native()
  6. def conf: SQLConf

    The active config object within the current scope.

    The active config object within the current scope. See SQLConf.get for more information.

    Definition Classes
    SQLConfHelper
  7. def current: NameScope

    Get the current scope, which is a default choice for name resolution.

  8. final def eq(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef
  9. def equals(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef → Any
  10. final def getClass(): Class[_ <: AnyRef]
    Definition Classes
    AnyRef → Any
    Annotations
    @IntrinsicCandidate() @native()
  11. def hashCode(): Int
    Definition Classes
    AnyRef → Any
    Annotations
    @IntrinsicCandidate() @native()
  12. final def isInstanceOf[T0]: Boolean
    Definition Classes
    Any
  13. final def ne(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef
  14. final def notify(): Unit
    Definition Classes
    AnyRef
    Annotations
    @IntrinsicCandidate() @native()
  15. final def notifyAll(): Unit
    Definition Classes
    AnyRef
    Annotations
    @IntrinsicCandidate() @native()
  16. def overwriteCurrent(output: Option[Seq[Attribute]] = None, hiddenOutput: Option[Seq[Attribute]] = None, availableAliases: Option[HashSet[ExprId]] = None, aggregateListAliases: Seq[Alias] = Seq.empty, baseAggregate: Option[Aggregate] = None): Unit

    Completely overwrite the current scope state with operator output, hiddenOutput, availableAliases, aggregateListAliases and baseAggregate.

    Completely overwrite the current scope state with operator output, hiddenOutput, availableAliases, aggregateListAliases and baseAggregate. If hiddenOutput, availableAliases or baseAggregate are not provided, preserve the previous values. Additionally, update nullabilities of attributes in hidden output from new output, so that if attribute was nullable in either old hidden output or new output, it must stay nullable in new hidden output as well.

    This method is called by the Resolver when we've calculated the output of an operator that is being resolved. The new output is calculated based on the outputs of operator's children.

    Example for SubqueryAlias, here we rewrite the current NameScope's attributes to prepend subquery qualifier to their names:

    val qualifier = sa.identifier.qualifier :+ sa.alias
    scopes.overwriteCurrent(scope.output.map(attribute => attribute.withQualifier(qualifier)))

    Trivially, we would call this method for every operator in the query plan, however some operators just propagate the output of their children without any changes, so we can omit this call for them (e.g. Filter).

    This method should be preferred over pushScope.

  17. def overwriteOutputAndExtendHiddenOutput(output: Seq[Attribute], groupingAttributeIds: Option[HashSet[ExprId]] = None, aggregateListAliases: Seq[Alias] = Seq.empty, baseAggregate: Option[Aggregate] = None): Unit

    Overwrites output, groupingAttributeIds, aggregateListAliases and baseAggregate of the current NameScope entry and:

    Overwrites output, groupingAttributeIds, aggregateListAliases and baseAggregate of the current NameScope entry and:

    1. extends hidden output with the provided output (only attributes that are not in the hidden output are added). This is done because resolution of arguments can be done through certain operators by hidden output. This use case is specific to Dataframe programs. Example:
    val df = (1 to 100).map { i => (i, i % 10, i % 2 == 0) }.toDF("a", "b", "c")
    df.select($"a", $"b").filter($"c")

    Unresolved tree would be:

    Filter 'c +- 'Project ['a, 'b] +- Project [_1 AS a, _2 AS b, _3 AS c] +- LocalRelation [_1, _2, _3]

    As it can be seen in the example above, c from the Filter condition should be resolved using the hiddenOutput (because its child output doesn't contain c). That's why in hidden output we have to have both hidden output from the previous scope and the provided output. This is done for Project and Aggregate operators.

    2. updates properties of attributes in hidden output. This includes nullabilities and access modes. See updateHiddenOutputProperties for more details.

  18. def popScope(): Unit

    After finishing the resolution after pushScope, the caller needs to call popScope to clear the stack.

    After finishing the resolution after pushScope, the caller needs to call popScope to clear the stack. We propagate hiddenOutput, availableAliases upwards because of name resolution by overwriting their current values with the popped ones. This is not done in case pushScope and popScope were called in the context of subquery resolution (which is indicated by isSubqueryRoot flag), because we don't want to overwrite the existing hiddenOutput of the main plan.

  19. def pushScope(isSubqueryRoot: Boolean = false): Unit

    Push a fresh scope to the stack.

    Push a fresh scope to the stack.

    This method is called by the Resolver before recursing into the operator's child resolution _only_ in cases where a fresh scope is required.

    For example, Project or Aggregate introduce their own scopes semantically, so that a lower resolution can lookup correlated names:

    CREATE TABLE IF NOT EXISTS t1 (col1 INT, col2 STRING);
    CREATE TABLE IF NOT EXISTS t2 (col1 INT, col2 STRING);
    
    -- Here we need a scope for the upper [[Project]], and a separate scope for the correlated
    -- subquery, because its [[Filter]] need to lookup `t1.col1` from the upper scope.
    -- Those scopes have to be independent to avoid polluting each other's attributes.
    SELECT col1, (SELECT col2 FROM t2 WHERE t2.col1 == t1.col1 LIMIT 1) FROM t1;

    Also, we need separate scopes for the operators with multiple children, so that the next child's resolution wouldn't try to work with the data from it's sibling's scope, to avoid all kinds of undefined behavior:

    pushScope()
    val resolvedLeftChild = try {
       resolve(unresolvedExcept.left)
    } finally {
       popScope()
    }
    
    // Right child should not see the left child's resolution data to avoid subtle bugs, so we
    // create a fresh scope here.
    
    pushScope()
    val resolvedRightChild = try {
       resolve(unresolvedExcept.right)
    } finally {
       popScope()
    }
    isSubqueryRoot

    Indicates that the current scope is a root of a subquery. This is used by NameScopeStack.resolveMultipartName to detect the nearest outer scope.

  20. def resolveMultipartName(multipartName: Seq[String], canLaterallyReferenceColumn: Boolean = false, canReferenceAggregateExpressionAliases: Boolean = false, canResolveNameByHiddenOutput: Boolean = false, canResolveNameByHiddenOutputInSubquery: Boolean = false, shouldPreferTableColumnsOverAliases: Boolean = false, shouldPreferHiddenOutput: Boolean = false, canReferenceAggregatedAccessOnlyAttributes: Boolean = false): NameTarget

    Resolve multipart name into a NameTarget from current or outer scopes.

    Resolve multipart name into a NameTarget from current or outer scopes. Currently we only support one level of correlation, so we look up multipartName in current scope, and if the name was not found, we look it up in the nearest outer scope:

    -- 'a' is a simple lookup from the current scope.
    SELECT a FROM (SELECT col1 AS a FROM VALUES (1));
    -- `a` in `(SELECT a + 1)` will be wrapped in [[OuterReference]].
    SELECT a, (SELECT a + 1) AS b FROM (SELECT col1 AS a FROM VALUES (1));

    The ambiguity between local and outer references is resolved in favour of current:

    -- There's no correlation here, subquery references its column from the current scope.
    -- This returns [1, 2].
    SELECT col1, (SELECT col1 FROM VALUES (2)) AS b FROM VALUES (1)

    Correlations beyond one level are not supported:

    -- 3 levels, fails with `UNRESOLVED_COLUMN`.
    SELECT (
      SELECT (
        SELECT t1.col1 FROM VALUES (3) AS t3
      ) FROM VALUES (2) AS t2
    ) FROM VALUES (1) AS t1;

    Correlated references are accessible from lower subquery operators:

    -- Returns [1, 1]
     SELECT
      col1, (SELECT * FROM (SELECT t1.col1 FROM VALUES (2) AS t2))
    FROM
      VALUES (1) AS t1;

    We cannot reference LCA or aggregate expression by alias in the outer scope:

    -- These examples fail with `UNRESOLVED_COLUMN`.
    -- LCA in outer scope.
    SELECT col1 AS a, (SELECT a + 1) AS b FROM VALUES (1);
    -- Aliased aggerate expression in outer scope.
    SELECT col1 AS a FROM VALUES (1) GROUP BY a, (SELECT a + 1);

    Only Attributes are wrapped in OuterReference:

    -- The subquery's [[Project]] list will contain outer(col1#0).f1.f2.
    SELECT
      col1, (SELECT col1.f1.f2 + 1) AS b
    FROM
      VALUES (named_struct('f1', named_struct('f2', 1)));
  21. final def synchronized[T0](arg0: => T0): T0
    Definition Classes
    AnyRef
  22. def toString(): String
    Definition Classes
    AnyRef → Any
  23. final def wait(arg0: Long, arg1: Int): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException])
  24. final def wait(arg0: Long): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException]) @native()
  25. final def wait(): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException])
  26. def withSQLConf[T](pairs: (String, String)*)(f: => T): T

    Sets all SQL configurations specified in pairs, calls f, and then restores all SQL configurations.

    Sets all SQL configurations specified in pairs, calls f, and then restores all SQL configurations.

    Attributes
    protected
    Definition Classes
    SQLConfHelper

Deprecated Value Members

  1. def finalize(): Unit
    Attributes
    protected[lang]
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.Throwable]) @Deprecated
    Deprecated

    (Since version 9)

Inherited from SQLConfHelper

Inherited from AnyRef

Inherited from Any

Ungrouped