Class ExpressionEvaluatorVisitor

java.lang.Object
dev.frostlake.executor.expressions.ExpressionEvaluatorVisitor
All Implemented Interfaces:
ExpressionVisitor<Object>

public class ExpressionEvaluatorVisitor extends Object implements ExpressionVisitor<Object>
Evaluates expression AST using the visitor pattern
  • Constructor Details

  • Method Details

    • setRow

      public void setRow(Row row)
      Point this visitor at a new row — lets one visitor be reused across rows (see ExpressionEvaluator).
    • setQueryExecutor

      public void setQueryExecutor(QueryExecutor queryExecutor)
    • setScopeOpaqueToSubqueries

      public void setScopeOpaqueToSubqueries(boolean opaque)
      See scopeOpaqueToSubqueries.
    • setLateralContext

      public void setLateralContext(Map<String,Object> lateralContext)
    • setSubqueryMemo

      public void setSubqueryMemo(SubqueryMemo subqueryMemo)
    • setDeclaredTypeBase

      public void setDeclaredTypeBase(Table base, Map<String,Table> aliasToTable, List<Table> allTables)
      Install the relation a reference's DECLARED type falls back to. Only a name this evaluator's own table does not carry at all reaches it: a slot the projection typed answers for itself, and one it could not type stays undetermined rather than borrowing a same-named base column's type.
      Parameters:
      base - the base relation, or null to clear
      aliasToTable - its FROM-clause alias map, or null
      allTables - its joined relations, or null
    • setMultiTableContext

      public void setMultiTableContext(Map<String,Table> aliasToTable, List<Table> allTables)
    • setFromClauseKeys

      public void setFromClauseKeys(Collection<String> keys)
      The FROM clause's relation keys — each table's alias when one was written, else its name. Live, an alias REPLACES the table name: with FROM r AS x the reference r.t is "invalid identifier 'R.T'" in plain, JOIN and ASOF queries alike, so when these keys are known, a qualifier naming none of them is refused instead of falling through to the bare-name resolution. Null (the default) keeps the historical leniency for paths that do not thread their FROM shape.
    • setResultContext

      public void setResultContext(Map<String,Object> resultContext)
      Provide precomputed SELECT-list outputs (for HAVING / QUALIFY), keyed by the canonical AST form of each select item (via AstPrinterVisitor) and by its alias. A column reference or function call whose canonical form matches resolves to the precomputed value instead of being (re-)evaluated — so an aggregate or window function referenced in a HAVING/QUALIFY condition uses the already-computed result rather than re-aggregating.
    • visitDefaultMarker

      public Object visitDefaultMarker(DefaultMarkerExpression expr)
      The bare DML DEFAULT reached EVALUATION, which means it was not standing alone as a value — the write paths take the marker before this. Live agrees exactly here: `SET c = DEFAULT + 1` and `SELECT default FROM t` are both "invalid identifier 'DEFAULT'".
      Specified by:
      visitDefaultMarker in interface ExpressionVisitor<Object>
    • visitLiteral

      public Object visitLiteral(LiteralExpression expr)
      Specified by:
      visitLiteral in interface ExpressionVisitor<Object>
    • visitSystemStreamHasData

      public Object visitSystemStreamHasData(SystemStreamHasDataExpression expr)
      Specified by:
      visitSystemStreamHasData in interface ExpressionVisitor<Object>
    • visitSystemUserTaskCancel

      public Object visitSystemUserTaskCancel(SystemUserTaskCancelExpression expr)
      Specified by:
      visitSystemUserTaskCancel in interface ExpressionVisitor<Object>
    • visitSessionVar

      public Object visitSessionVar(SessionVarExpression expr)
      Specified by:
      visitSessionVar in interface ExpressionVisitor<Object>
    • visitBindVariable

      public Object visitBindVariable(BindVariableExpression expr)
      Specified by:
      visitBindVariable in interface ExpressionVisitor<Object>
    • visitColumnReference

      public Object visitColumnReference(ColumnReferenceExpression expr)
      Specified by:
      visitColumnReference in interface ExpressionVisitor<Object>
    • visitBinaryOperation

      public Object visitBinaryOperation(BinaryOperationExpression expr)
      Specified by:
      visitBinaryOperation in interface ExpressionVisitor<Object>
    • visitUnaryOperation

      public Object visitUnaryOperation(UnaryOperationExpression expr)
      Specified by:
      visitUnaryOperation in interface ExpressionVisitor<Object>
    • visitLambda

      public Object visitLambda(LambdaExpression expr)
      Specified by:
      visitLambda in interface ExpressionVisitor<Object>
    • visitFunctionCall

      public Object visitFunctionCall(FunctionCallExpression written)
      Specified by:
      visitFunctionCall in interface ExpressionVisitor<Object>
    • unknownFunctionSentence

      public String unknownFunctionSentence(List<FunctionCallExpression> calls)
      The same sentence for a whole statement's worth of unresolvable calls. Live names EVERY one of them in a single sentence, pluralising the noun and separating the names with ", " — Unknown functions FNC, FNA, FNB. — and it does NOT de-duplicate: two calls to the same missing name are listed twice.

      The two families are reported SEPARATELY and never mixed. When a statement carries both a bare unresolvable name and a qualified one, live names only the BARE list; the qualified sentence appears only when every unresolvable call is qualified (live-verified in both directions).

    • resolvesToAFunction

      public boolean resolvesToAFunction(FunctionCallExpression call)
      The predicate behind requireResolvableFunctionName(dev.frostlake.executor.expressions.FunctionCallExpression), as a question rather than a refusal — the statement-wide scan asks it of every call so it can name them ALL in one sentence, which is what live does.
      Parameters:
      call - the call to judge
      Returns:
      whether some path could resolve this name
    • visitCaseExpression

      public Object visitCaseExpression(CaseExpression expr)
      Specified by:
      visitCaseExpression in interface ExpressionVisitor<Object>
    • visitCast

      public Object visitCast(CastExpression expr)
      Specified by:
      visitCast in interface ExpressionVisitor<Object>
    • withNarrowingCastEqualitiesAnswered

      public Expression withNarrowingCastEqualitiesAnswered(Expression predicate)
      A WHERE's or ON's predicate with live's one exemption from the width check applied. An equality between a narrowing string cast of a text value and a constant that fits the width can only hold when the value fits too, so live answers it without converting (false for a longer value), where every other use of the cast refuses the value. The cast is dropped from such an equality, either way round, and from an IN list every item of which fits; the walk goes through AND and OR, never under NOT (live-verified).
      Parameters:
      predicate - the parsed predicate
      Returns:
      the predicate with those casts dropped, or the predicate itself when none applies
    • visitIsNull

      public Object visitIsNull(IsNullExpression expr)
      Specified by:
      visitIsNull in interface ExpressionVisitor<Object>
    • visitTupleIn

      public Object visitTupleIn(TupleInExpression expr)
      Specified by:
      visitTupleIn in interface ExpressionVisitor<Object>
    • visitSpread

      public Object visitSpread(SpreadExpression expr)
      Specified by:
      visitSpread in interface ExpressionVisitor<Object>
    • validateUdfArguments

      public void validateUdfArguments(Expression expr)
      The user-defined function argument check alone, for a FROM-less select item: that path computes the item's value without the strict walk, so the one plan-time refusal it must not miss is run here on its own, before the value is (see rejectUncoercibleUdfArguments(java.lang.String, dev.frostlake.executor.expressions.FunctionCallExpression)).
    • validateStrictArguments

      public void validateStrictArguments(Expression expr)
    • validateBooleanPositions

      public void validateBooleanPositions(Expression expr)
      The BOOLEAN positions inside a predicate — a searched CASE's WHEN conditions and the BOOLEAN_STRICT_ARGS calls — judged at plan time, so that WHERE CASE WHEN g THEN TRUE END over a VARCHAR refuses over an empty table exactly as it does in a SELECT list. Deliberately only those two rules: the predicate walk stays narrow (see ExpressionEvaluator.validatePredicate), and the full strict walk runs them on its own.
    • validatePredicateType

      public void validatePredicateType(Expression predicate)
      Predicate-position strictness: Snowflake rejects a WHERE, HAVING or QUALIFY condition whose static type is VARCHAR or NUMBER (live: "Invalid data type [VARCHAR(10)] for predicate [STRICT_T.S10]", "[NUMBER(38,0)] for predicate [T.N]", and "[VARCHAR(4)] for predicate ['true']" for a literal). BOOLEAN, VARIANT and undetermined types pass. The three clauses share the sentence, and the predicate is spelled from the plan: MAX(RT.G), COUNT(*), RT.N + 1, a window call in its canonical form, a SELECT alias bare (GG).
    • strictText

      public String strictText(Expression expr)
      The expression as a strictness message renders it: live prints the call from its analysed plan, so a column reference comes out qualified with its source relation (TO_VARCHAR(FK.F), CAST(STT.SO AS …)) — see StrictMessagePrinter.
    • strictPlanText

      public String strictPlanText(Expression expr)
      strictText(dev.frostlake.executor.expressions.Expression) in the plan-shaped mode — the aggregate, cast and scalar rewrites live's plan applies before it names an operand — for the sentences that re-print an operand from the plan, such as a conversion refusal's parameter.
    • strictPlanCallText

      public String strictPlanCallText(FunctionCallExpression call)
      An arity sentence's echo: the refused call under the name it was written with, its arguments re-printed from the plan as strictPlanText(dev.frostlake.executor.expressions.Expression) prints them — UPPER(CAST('2020-01-01' AS DATE), 1) for UPPER(TO_DATE('2020-01-01'), 1) and TO_DATE(DATE_ADDDAYSTODATE(1, FT.D), 'YYYY-MM-DD', 1) for a DATEADD beside a format.
    • strictConversionText

      public String strictConversionText(Expression expr)
      strictText(dev.frostlake.executor.expressions.Expression) as an invalid-type sentence quotes an operand: its conversions spelled as the functions the plan made of them, TO_VECTOR(ARRAY_CONSTRUCT(1, 2, 3)) and FIXED_TO_FIXED(1 AS NUMBER(10,2)[UNKNOWN]) — see StrictPrintMode.CONVERSION.
    • strictAggregateText

      public String strictAggregateText(Expression expr)
      strictText(dev.frostlake.executor.expressions.Expression) for one of the two calls a NESTING message brackets: a plain AVG at that root is named by its SUM half rather than its whole definition — see StrictMessagePrinter.averageSumHalf(dev.frostlake.executor.expressions.Expression).
    • setWindowedStrictWalk

      public void setWindowedStrictWalk(boolean windowedStrictWalk)
    • validateGeoComparisons

      public void validateGeoComparisons(Expression expr)
      The GEOSPATIAL comparison rules alone, walked over a PREDICATE. The strict-argument walk covers a projection; a WHERE clause is validated by validatePredicateType(dev.frostlake.executor.expressions.Expression) instead, and that is exactly where g = … gets written — so the two comparison rules are applied here as well, and nothing else is. The walk descends only through the containers a comparison nests in.
    • rejectInvalidTupleInShape

      public void rejectInvalidTupleInShape(TupleInExpression expr)
      A tuple IN right-hand list must be parenthesized ROWS of the left side's width. The FLAT spelling (a, b) IN (1, 2) is a type error naming the ROW and every scalar — live, "Invalid argument types for function 'IN': (ROW(NUMBER(1,0), NUMBER(1,0)), NUMBER(1,0), NUMBER(1,0))" — and a row of the wrong WIDTH is a conversion error naming both ROW types — live, "Can not convert parameter 'ROW(1, 2, 3)' of type [ROW(NUMBER(1,0), NUMBER(1,0), NUMBER(1,0))] into expected type [ROW(NUMBER(1,0), NUMBER(1,0))]".
    • validateOrderedValueExpression

      public void validateOrderedValueExpression(Expression expr)
      The value an ordering aggregate accumulates may not be semi-structured. This is the same rule SemiStructuredRejection.INCOMPATIBLE_TYPES covers for MEDIAN's argument, applied where the value arrives through a WITHIN GROUP (ORDER BY …) clause instead of the argument list: live, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY o) is "incompatible types: [OBJECT] and [NUMBER(9,0)]" (SQLSTATE 42846) — exactly what MEDIAN(o) says — while … (ORDER BY n) over a NUMBER is fine.

      It cannot be declared on the function like the argument positions are, because the clause is not an argument: the FRACTION is, and that position refuses a semi-structured value with the OTHER message ("Invalid argument types for function 'PERCENTILE_CONT': (OBJECT)", live-verified for PERCENTILE_CONT(o) WITHIN GROUP (ORDER BY n)). One call, two message shapes.

      A FILE arrives at the same sentence: live, both PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY f) and the PERCENTILE_DISC spelling are "incompatible types: [FILE] and [NUMBER(9,0)]" — the CLAUSE message, not the "cannot be used as ORDER BY keys" one a bare ORDER BY f gets, which is why this check runs before the key rule reaches the clause.

    • validateKeyExpression

      public void validateKeyExpression(Expression expr, SortKeyRole role)
      Plan-time rejection of a FILE or a GEOSPATIAL value in a GROUPING / SORTING key position. A FILE compares, groups as a DISTINCT value and joins perfectly well — live, f = f, SELECT DISTINCT f, COUNT(DISTINCT f), JOIN ON a.f = b.f and f IN (…) are all accepted — but it may not be a GROUP BY, ORDER BY or window PARTITION BY key. The check reads the key's INFERRED type, so it fires for a bare column, a positional ordinal, a SELECT alias and any expression that yields a FILE (GROUP BY IFF(TRUE, f, f)) alike.

      GEOGRAPHY and GEOMETRY are refused in the same three positions with the same sentence and the same SQLSTATE, measured on a table carrying both: GROUP BY g is "Expressions of type GEOGRAPHY cannot be used as GROUP BY keys" (42804, vendor 92102), ORDER BY g the ORDER BY variant (92103) and OVER (PARTITION BY g) the PARTITION BY one (92104) — the three codes FILE uses. Every spelling the FILE rule covers rejects too: the ordinal (GROUP BY 1 over a projected geo column), the SELECT alias, ROLLUP(g), CUBE(gm), GROUPING SETS ((g)), a multi-key list, IFF(TRUE, g, g), the ORDER BY inside an OVER spec, a derived table's ORDER BY and the CTE spelling — and all of them on an EMPTY input. Frostlake used to group and sort geo values by their GeoJSON text.

      This is where geo diverges MOST sharply from the semi-structured types the same machinery serves: live GROUPS, ORDERS and PARTITIONS an OBJECT, an ARRAY and every structured kind quite happily, which is why #143 correctly refused to route those through here. A geo value is nevertheless still a DISTINCT value — SELECT DISTINCT g, SELECT DISTINCT g, gm, COUNT(DISTINCT g), UNION, INTERSECT and EXCEPT all work live, and none of them routes through this check.

    • castMatrixRefuses

      public static boolean castMatrixRefuses(DataType source, DataType target)
      Whether the plain cast refuses outright to carry a source value into a SCALAR target: the matrix's conversion verdict, the pairs rejectUncastableSource(dev.frostlake.executor.expressions.CastExpression) refuses while a statement compiles. A target outside the matrix (a text, a VARIANT, a container) is never refused here.
      Parameters:
      source - the value's static type
      target - the type it would be cast to
      Returns:
      true when no cast from the one to the other exists
    • argumentTypeText

      public String argumentTypeText(Expression arg)
      The type name live prints when it refuses an argument, for callers outside the strict-argument walk — the table-function parameter checks name their offending argument the same way.
    • countsStoredColumn

      public boolean countsStoredColumn(Expression argument)
      Whether a COUNT over argument is answered from the statistics: over a constant, or over a column that is one of the catalog table's stored columns — a derived relation's too, where it passes one through unchanged. Live answers COUNT(c) and COUNT(1) from them and scans for COUNT(c + 1), COUNT(ABS(c)) and COUNT(c::NUMBER(10,1)); an IFF over a constant condition counts as the branch it folds to.
      Parameters:
      argument - the COUNT's one argument
      Returns:
      true when the statistics answer it
    • inferStaticRange

      public ValueRange inferStaticRange(Expression expr)
      The interval expr's values lie in, or null when none can be known. Never throws.
    • rejectArgumentFamilies

      public void rejectArgumentFamilies(Expression expr)
      Applies every function's argument-family refusals to the calls in expr from their DECLARED types, before any row is read — the check the row loop makes for each call, brought forward so that an aggregate over an empty table is refused as live refuses it: SELECT AVG(bo) FROM t WHERE FALSE is "Invalid argument types for function 'SUM': (BOOLEAN)" at the call on the account, not NULL. A call the registry does not know is left to its own refusal.
      Parameters:
      expr - the expression to walk; window calls are the window stage's own business
    • validateColumnScopeOnly

      public void validateColumnScopeOnly(Expression expression)
      PHASE ONE of the plan-time walk: every column reference in expression, nothing else.
    • validateFunctionNamesOnly

      public void validateFunctionNamesOnly(Expression expression)
      PHASE TWO: every call NAME in expression, nothing else.
    • setOutputScopeNames

      public void setOutputScopeNames(Set<String> names)
      Bare names exempt from the plan-time scope rejection — the enclosing query's SELECT output aliases / output column names, which the non-SELECT clauses may legally reference.
    • setOutputAliasTypes

      public void setOutputAliasTypes(Map<String,DataType> types)
    • setScopeExemptNames

      public void setScopeExemptNames(Set<String> names)
    • inferStaticType

      public DataType inferStaticType(Expression expr)
      The statically-known type of expr in this table context, or null when undetermined — the projection-time entry point that gives a derived relation's columns their declared types.

      Reads the semi-structured refinement where the general inference says only VARIANT, because that is what a derived column's type IS: live, SYSTEM$TYPEOF over (SELECT OBJECT_CONSTRUCT('a',1) AS x FROM t) reports OBJECT, over (SELECT ARRAY_AGG(n) AS x ...) reports ARRAY and over (SELECT CASE WHEN TRUE THEN o ELSE o END AS x ...) reports OBJECT — while PARSE_JSON, TO_VARIANT and colon-path access all report VARIANT and are left alone. So the OBJECT/ARRAY producers this refinement knows are exactly the ones live names.

    • spelledNumber

      public DataType spelledNumber(Expression expr)
      The NUMBER expr spells when it is a bare string literal, or a column that carries one out of a derived relation, else null — see TableColumn#getSpelledNumber.
      Parameters:
      expr - a select item
      Returns:
      the measured NUMBER, or null
    • visitWindowFunction

      public Object visitWindowFunction(WindowFunctionExpression expr)
      Specified by:
      visitWindowFunction in interface ExpressionVisitor<Object>
    • visitObjectAccess

      public Object visitObjectAccess(ObjectAccessExpression expr)
      Specified by:
      visitObjectAccess in interface ExpressionVisitor<Object>
    • visitArrayAccess

      public Object visitArrayAccess(ArrayAccessExpression expr)
      Specified by:
      visitArrayAccess in interface ExpressionVisitor<Object>
    • visitSubquery

      public Object visitSubquery(SubqueryExpression expr)
      Specified by:
      visitSubquery in interface ExpressionVisitor<Object>
    • visitExecuteImmediate

      public Object visitExecuteImmediate(ExecuteImmediateExpression expr)
      Specified by:
      visitExecuteImmediate in interface ExpressionVisitor<Object>
    • visitJsonObject

      public Object visitJsonObject(JsonObjectExpression expr)
      Specified by:
      visitJsonObject in interface ExpressionVisitor<Object>
    • visitJsonArray

      public Object visitJsonArray(JsonArrayExpression expr)
      Specified by:
      visitJsonArray in interface ExpressionVisitor<Object>
    • visitLikeAnyAll

      public Object visitLikeAnyAll(LikeAnyAllExpression expr)
      Specified by:
      visitLikeAnyAll in interface ExpressionVisitor<Object>
    • visitBetween

      public Object visitBetween(BetweenExpression expr)
      Specified by:
      visitBetween in interface ExpressionVisitor<Object>
    • visitIn

      public Object visitIn(InExpression expr)
      Specified by:
      visitIn in interface ExpressionVisitor<Object>
    • visitQuantifiedComparison

      public Object visitQuantifiedComparison(QuantifiedComparisonExpression expr)
      Specified by:
      visitQuantifiedComparison in interface ExpressionVisitor<Object>
    • visitInterval

      public Object visitInterval(IntervalExpression expr)
      Specified by:
      visitInterval in interface ExpressionVisitor<Object>
    • collationOf

      public ExpressionCollation collationOf(Expression expr)
      The collation expr carries, and the level it holds it at (see CollationLevel). Live-verified, construct by construct:
         x COLLATE 'spec', COLLATE(x, 'spec')   the spec, EXPLICIT; the outermost wins, and COLLATE ''
                                                or a bare NULL operand carries none
         a collated column                      its declared collation, COLUMN
         a || b, CONCAT, CONCAT_WS, GREATEST,
         LEAST, DECODE's and a CASE's results   the operands', settled left to right
         COALESCE, NVL, IFNULL, IFF's and
         NVL2's results                         the same, settled from the LAST one back
         UPPER, LOWER, TRIM, SUBSTR, LEFT, MAX  the first argument's
         a cast, TO_VARCHAR, REPLACE, REPEAT,
         a literal, anything else               none
       

      Two collations that disagree at one level are the compilation error ExpressionCollation.combine(dev.frostlake.executor.expressions.ExpressionCollation, dev.frostlake.executor.expressions.ExpressionCollation) raises, so asking an expression for its collation also judges it — which is how the static channel refuses them over any number of rows.

      Parameters:
      expr - the expression
      Returns:
      its collation
    • validateCollations

      public void validateCollations(Expression expr)
      The collation rules alone, walked over a JOIN's ON condition before any pair of rows is read: AND, OR and NOT are descended, and every comparison, LIKE, IN list and BETWEEN beneath them settles its operands' collations — so two that disagree are refused over tables with no rows, as they are live, rather than surfacing only when a pair happens to be compared.
      Parameters:
      expr - the condition