Search in sources :

Example 16 with ExpressionNode

use of io.questdb.griffin.model.ExpressionNode in project questdb by bluestreak01.

the class WhereClauseParser method extract.

IntrinsicModel extract(AliasTranslator translator, ExpressionNode node, RecordMetadata m, CharSequence preferredKeyColumn, int timestampIndex, FunctionParser functionParser, RecordMetadata metadata, SqlExecutionContext executionContext) throws SqlException {
    this.timestamp = timestampIndex < 0 ? null : m.getColumnName(timestampIndex);
    this.preferredKeyColumn = preferredKeyColumn;
    IntrinsicModel model = models.next();
    if (removeAndIntrinsics(translator, model, node, m, functionParser, metadata, executionContext)) {
        return model;
    }
    ExpressionNode root = node;
    while (!stack.isEmpty() || node != null) {
        if (node != null) {
            if (isAndKeyword(node.token)) {
                if (!removeAndIntrinsics(translator, model, node.rhs, m, functionParser, metadata, executionContext)) {
                    stack.push(node.rhs);
                }
                node = removeAndIntrinsics(translator, model, node.lhs, m, functionParser, metadata, executionContext) ? null : node.lhs;
            } else {
                node = stack.poll();
            }
        } else {
            node = stack.poll();
        }
    }
    applyKeyExclusions(translator, model);
    model.filter = collapseIntrinsicNodes(root);
    return model;
}
Also used : ExpressionNode(io.questdb.griffin.model.ExpressionNode) IntrinsicModel(io.questdb.griffin.model.IntrinsicModel)

Example 17 with ExpressionNode

use of io.questdb.griffin.model.ExpressionNode in project questdb by bluestreak01.

the class WhereClauseParser method analyzeBetween0.

private boolean analyzeBetween0(IntrinsicModel model, ExpressionNode col, ExpressionNode between, boolean isNegated, FunctionParser functionParser, RecordMetadata metadata, SqlExecutionContext executionContext) throws SqlException {
    if (!isTimestamp(col)) {
        return false;
    }
    ExpressionNode lo = between.args.getQuick(1);
    ExpressionNode hi = between.args.getQuick(0);
    try {
        model.setBetweenNegated(isNegated);
        boolean isBetweenTranslated = translateBetweenToTimestampModel(model, functionParser, metadata, executionContext, lo);
        if (isBetweenTranslated) {
            isBetweenTranslated = translateBetweenToTimestampModel(model, functionParser, metadata, executionContext, hi);
        }
        if (isBetweenTranslated) {
            between.intrinsicValue = IntrinsicModel.TRUE;
            return true;
        }
    } finally {
        model.clearBetweenTempParsing();
    }
    return false;
}
Also used : ExpressionNode(io.questdb.griffin.model.ExpressionNode)

Example 18 with ExpressionNode

use of io.questdb.griffin.model.ExpressionNode in project questdb by bluestreak01.

the class WhereClauseParser method analyzeInInterval.

private boolean analyzeInInterval(IntrinsicModel model, ExpressionNode col, ExpressionNode in, boolean isNegated, FunctionParser functionParser, RecordMetadata metadata, SqlExecutionContext executionContext) throws SqlException {
    if (!isTimestamp(col)) {
        return false;
    }
    if (in.paramCount == 2) {
        // Single value ts in '2010-01-01' - treat string literal as an interval, not single Timestamp point
        ExpressionNode inArg = in.rhs;
        if (inArg.type == ExpressionNode.CONSTANT) {
            if (isNullKeyword(inArg.token)) {
                if (!isNegated) {
                    model.intersectIntervals(Numbers.LONG_NaN, Numbers.LONG_NaN);
                } else {
                    model.subtractIntervals(Numbers.LONG_NaN, Numbers.LONG_NaN);
                }
            } else {
                if (!isNegated) {
                    model.intersectIntervals(inArg.token, 1, inArg.token.length() - 1, inArg.position);
                } else {
                    model.subtractIntervals(inArg.token, 1, inArg.token.length() - 1, inArg.position);
                }
            }
            in.intrinsicValue = IntrinsicModel.TRUE;
            return true;
        } else if (isFunc(inArg)) {
            // Single value ts in $1 - treat string literal as an interval, not single Timestamp point
            Function f1 = functionParser.parseFunction(inArg, metadata, executionContext);
            if (checkFunctionCanBeTimestampInterval(executionContext, f1)) {
                if (f1.isConstant()) {
                    CharSequence funcVal = f1.getStr(null);
                    if (!isNegated) {
                        model.intersectIntervals(funcVal, 0, funcVal.length(), inArg.position);
                    } else {
                        model.subtractIntervals(funcVal, 0, funcVal.length(), inArg.position);
                    }
                } else if (f1.isRuntimeConstant()) {
                    if (!isNegated) {
                        model.intersectRuntimeIntervals(f1);
                    } else {
                        model.subtractRuntimeIntervals(f1);
                    }
                } else {
                    return false;
                }
                in.intrinsicValue = IntrinsicModel.TRUE;
                return true;
            } else {
                checkFunctionCanBeTimestamp(metadata, executionContext, f1, inArg.position);
                // This is same as equals
                return analyzeTimestampEqualsFunction(model, in, f1, inArg.position);
            }
        }
    } else {
        // NOT IN can be translated in any case as series of subtractions
        if (!model.hasIntervalFilters() || isNegated) {
            int n = in.args.size() - 1;
            for (int i = 0; i < n; i++) {
                ExpressionNode inListItem = in.args.getQuick(i);
                if (inListItem.type != ExpressionNode.CONSTANT) {
                    return false;
                }
            }
            for (int i = 0; i < n; i++) {
                ExpressionNode inListItem = in.args.getQuick(i);
                long ts = parseTokenAsTimestamp(inListItem);
                if (!isNegated) {
                    if (i == 0) {
                        model.intersectIntervals(ts, ts);
                    } else {
                        model.unionIntervals(ts, ts);
                    }
                } else {
                    model.subtractIntervals(ts, ts);
                }
            }
            in.intrinsicValue = IntrinsicModel.TRUE;
            return true;
        }
    }
    return false;
}
Also used : AbstractGeoHashFunction(io.questdb.griffin.engine.functions.AbstractGeoHashFunction) Function(io.questdb.cairo.sql.Function) ExpressionNode(io.questdb.griffin.model.ExpressionNode) FlyweightCharSequence(io.questdb.std.str.FlyweightCharSequence)

Example 19 with ExpressionNode

use of io.questdb.griffin.model.ExpressionNode in project questdb by bluestreak01.

the class ExpressionParser method parseExpr.

@SuppressWarnings("ConstantConditions")
void parseExpr(GenericLexer lexer, ExpressionParserListener listener) throws SqlException {
    try {
        int paramCount = 0;
        int braceCount = 0;
        int bracketCount = 0;
        int betweenCount = 0;
        int betweenAndCount = 0;
        int caseCount = 0;
        int argStackDepth = 0;
        int castAsCount = 0;
        int betweenStartCaseCount = 0;
        ExpressionNode node;
        CharSequence tok;
        char thisChar;
        int prevBranch;
        int thisBranch = BRANCH_NONE;
        boolean asPoppedNull = false;
        OUT: while ((tok = SqlUtil.fetchNext(lexer)) != null) {
            thisChar = tok.charAt(0);
            prevBranch = thisBranch;
            boolean processDefaultBranch = false;
            final int position = lexer.lastTokenPosition();
            switch(thisChar) {
                case '-':
                case '+':
                    processDefaultBranch = true;
                    if (prevBranch == BRANCH_CONSTANT) {
                        if (position > 0) {
                            char c = lexer.getContent().charAt(position - 1);
                            if (c == 'e' || c == 'E') {
                                // Incomplete scientific floating-point literal
                                ExpressionNode en = opStack.peek();
                                ((GenericLexer.FloatingSequence) en.token).setHi(position + 1);
                                processDefaultBranch = false;
                            }
                        }
                    }
                    break;
                case '.':
                    // Check what is on stack. If we have 'a .b' we have to stop processing
                    if (thisBranch == BRANCH_LITERAL || thisBranch == BRANCH_CONSTANT) {
                        char c = lexer.getContent().charAt(position - 1);
                        if (GenericLexer.WHITESPACE_CH.contains(c)) {
                            lexer.unparse();
                            break OUT;
                        }
                        if (Chars.isQuote(c)) {
                            ExpressionNode en = opStack.pop();
                            CharacterStoreEntry cse = characterStore.newEntry();
                            cse.put(GenericLexer.unquote(en.token)).put('.');
                            opStack.push(expressionNodePool.next().of(ExpressionNode.LITERAL, cse.toImmutable(), Integer.MIN_VALUE, en.position));
                        } else {
                            // attach dot to existing literal or constant
                            ExpressionNode en = opStack.peek();
                            ((GenericLexer.FloatingSequence) en.token).setHi(position + 1);
                        }
                    }
                    if (prevBranch == BRANCH_DOT || prevBranch == BRANCH_DOT_DEREFERENCE) {
                        throw SqlException.$(position, "too many dots");
                    }
                    if (prevBranch == BRANCH_RIGHT_BRACE) {
                        thisBranch = BRANCH_DOT_DEREFERENCE;
                    } else {
                        thisBranch = BRANCH_DOT;
                    }
                    break;
                case ',':
                    if (prevBranch == BRANCH_COMMA || prevBranch == BRANCH_LEFT_BRACE) {
                        throw missingArgs(position);
                    }
                    thisBranch = BRANCH_COMMA;
                    if (braceCount == 0) {
                        // comma outside of braces
                        lexer.unparse();
                        break OUT;
                    }
                    if (castBraceCountStack.peek() == braceCount) {
                        throw SqlException.$(position, "',' is not expected here");
                    }
                    // parentheses were mismatched.
                    while ((node = opStack.pop()) != null && node.token.charAt(0) != '(') {
                        argStackDepth = onNode(listener, node, argStackDepth);
                    }
                    if (node != null) {
                        opStack.push(node);
                    }
                    paramCount++;
                    break;
                case '[':
                    if (isTypeQualifier()) {
                        ExpressionNode en = opStack.peek();
                        ((GenericLexer.FloatingSequence) en.token).setHi(position + 1);
                    } else {
                        thisBranch = BRANCH_LEFT_BRACKET;
                        // If the token is a left parenthesis, then push it onto the stack.
                        paramCountStack.push(paramCount);
                        paramCount = 0;
                        argStackDepthStack.push(argStackDepth);
                        argStackDepth = 0;
                        // pop left literal or . expression, e.g. "a.b[i]"
                        // the precedence of [ is fixed to 2
                        ExpressionNode other;
                        while ((other = opStack.peek()) != null) {
                            if ((2 > other.precedence)) {
                                argStackDepth = onNode(listener, other, argStackDepth);
                                opStack.pop();
                            } else {
                                break;
                            }
                        }
                        // precedence must be max value to make sure control node isn't
                        // consumed as parameter to a greedy function
                        opStack.push(expressionNodePool.next().of(ExpressionNode.CONTROL, "[", Integer.MAX_VALUE, position));
                        bracketCount++;
                        braceCountStack.push(braceCount);
                        braceCount = 0;
                    }
                    break;
                case ']':
                    if (isTypeQualifier()) {
                        ExpressionNode en = opStack.peek();
                        ((GenericLexer.FloatingSequence) en.token).setHi(position + 1);
                    } else {
                        if (bracketCount == 0) {
                            lexer.unparse();
                            break OUT;
                        }
                        thisBranch = BRANCH_RIGHT_BRACKET;
                        if (prevBranch == BRANCH_LEFT_BRACKET) {
                            throw SqlException.$(position, "missing array index");
                        }
                        bracketCount--;
                        // pop the array index from the stack, it could be an operator
                        while ((node = opStack.pop()) != null && (node.type != ExpressionNode.CONTROL || node.token.charAt(0) != '[')) {
                            argStackDepth = onNode(listener, node, argStackDepth);
                        }
                        if (argStackDepthStack.notEmpty()) {
                            argStackDepth += argStackDepthStack.pop();
                        }
                        if (paramCountStack.notEmpty()) {
                            paramCount = paramCountStack.pop();
                        }
                        if (braceCountStack.notEmpty()) {
                            braceCount = braceCountStack.pop();
                        }
                        node = expressionNodePool.next().of(ExpressionNode.ARRAY_ACCESS, "[]", 2, position);
                        node.paramCount = 2;
                        opStack.push(node);
                    }
                    break;
                case 'g':
                case 'G':
                    if (SqlKeywords.isGeoHashKeyword(tok)) {
                        CharSequence geohashTok = GenericLexer.immutableOf(tok);
                        tok = SqlUtil.fetchNext(lexer);
                        if (tok == null || tok.charAt(0) != '(') {
                            lexer.backTo(position + SqlKeywords.GEOHASH_KEYWORD_LENGTH, geohashTok);
                            tok = geohashTok;
                            processDefaultBranch = true;
                            break;
                        }
                        tok = SqlUtil.fetchNext(lexer);
                        if (tok != null && tok.charAt(0) != ')') {
                            opStack.push(expressionNodePool.next().of(ExpressionNode.CONSTANT, lexer.immutablePairOf(geohashTok, tok), Integer.MIN_VALUE, position));
                            tok = SqlUtil.fetchNext(lexer);
                            if (tok == null || tok.charAt(0) != ')') {
                                throw SqlException.$(lexer.lastTokenPosition(), "invalid GEOHASH, missing ')'");
                            }
                        } else {
                            throw SqlException.$(lexer.lastTokenPosition(), "invalid GEOHASH, invalid type precision");
                        }
                        thisBranch = BRANCH_GEOHASH;
                    } else {
                        processDefaultBranch = true;
                    }
                    break;
                case '#':
                    if (isGeoHashCharsConstant(tok)) {
                        // e.g. #sp052w92p1p8
                        thisBranch = BRANCH_CONSTANT;
                        CharSequence geohashTok = GenericLexer.immutableOf(tok);
                        // optional / bits '/dd', '/d'
                        CharSequence slash = SqlUtil.fetchNext(lexer);
                        if (slash == null || slash.charAt(0) != '/') {
                            lexer.unparse();
                            opStack.push(expressionNodePool.next().of(ExpressionNode.CONSTANT, // standard token, no suffix '/d', '/dd'
                            geohashTok, Integer.MIN_VALUE, position));
                            break;
                        }
                        tok = SqlUtil.fetchNext(lexer);
                        if (tok == null || !Chars.isOnlyDecimals(tok)) {
                            // ranges are checked later by FunctionParser.createConstant
                            throw SqlException.$(lexer.lastTokenPosition(), "missing bits size for GEOHASH constant");
                        }
                        opStack.push(expressionNodePool.next().of(ExpressionNode.CONSTANT, // token plus suffix '/d', '/dd', where d in [0..9]
                        lexer.immutablePairOf(geohashTok, '/', tok), Integer.MIN_VALUE, position));
                        break;
                    }
                    if (isGeoHashBitsConstant(tok)) {
                        // e.g. ##01110001
                        thisBranch = BRANCH_CONSTANT;
                        opStack.push(expressionNodePool.next().of(ExpressionNode.CONSTANT, // geohash bit literals do not allow suffix syntax
                        GenericLexer.immutableOf(tok), Integer.MIN_VALUE, position));
                        break;
                    }
                    processDefaultBranch = true;
                    break;
                case '(':
                    if (prevBranch == BRANCH_RIGHT_BRACE) {
                        throw SqlException.$(position, "not a method call");
                    }
                    thisBranch = BRANCH_LEFT_BRACE;
                    // If the token is a left parenthesis, then push it onto the stack.
                    paramCountStack.push(paramCount);
                    paramCount = 0;
                    argStackDepthStack.push(argStackDepth);
                    argStackDepth = 0;
                    bracketCountStack.push(bracketCount);
                    bracketCount = 0;
                    // precedence must be max value to make sure control node isn't
                    // consumed as parameter to a greedy function
                    opStack.push(expressionNodePool.next().of(ExpressionNode.CONTROL, "(", Integer.MAX_VALUE, position));
                    // check if this brace was opened after 'cast'
                    if (castBraceCountStack.size() > 0 && castBraceCountStack.peek() == -1) {
                        castBraceCountStack.update(braceCount + 1);
                    }
                    braceCount++;
                    break;
                case ')':
                    if (prevBranch == BRANCH_COMMA) {
                        throw missingArgs(position);
                    }
                    if (braceCount == 0) {
                        lexer.unparse();
                        break OUT;
                    }
                    thisBranch = BRANCH_RIGHT_BRACE;
                    int localParamCount = (prevBranch == BRANCH_LEFT_BRACE ? 0 : paramCount + 1);
                    final boolean thisWasCast;
                    if (castBraceCountStack.size() > 0 && castBraceCountStack.peek() == braceCount) {
                        if (castAsCount == 0) {
                            throw SqlException.$(position, "'as' missing");
                        }
                        castAsCount--;
                        castBraceCountStack.pop();
                        thisWasCast = true;
                    } else {
                        thisWasCast = false;
                    }
                    braceCount--;
                    // If the stack runs out without finding a left parenthesis, then there are mismatched parentheses.
                    while ((node = opStack.pop()) != null && node.token.charAt(0) != '(') {
                        // special case - (*) expression
                        if (Chars.equals(node.token, '*') && argStackDepth == 0 && isCount()) {
                            argStackDepth = onNode(listener, node, 2);
                        } else {
                            if (thisWasCast) {
                                if (prevBranch != BRANCH_GEOHASH) {
                                    // validate type
                                    final short columnTypeTag = ColumnType.tagOf(node.token);
                                    if (((columnTypeTag < ColumnType.BOOLEAN || columnTypeTag > ColumnType.LONG256) && !asPoppedNull) || (columnTypeTag == ColumnType.GEOHASH && node.type == ExpressionNode.LITERAL)) {
                                        throw SqlException.$(node.position, "invalid type");
                                    }
                                    node.type = ExpressionNode.CONSTANT;
                                }
                            }
                            argStackDepth = onNode(listener, node, argStackDepth);
                        }
                    }
                    if (argStackDepthStack.notEmpty()) {
                        argStackDepth += argStackDepthStack.pop();
                    }
                    // enable operation or literal absorb parameters
                    if ((node = opStack.peek()) != null && (node.type == ExpressionNode.LITERAL || (node.type == ExpressionNode.SET_OPERATION))) {
                        if (!SqlKeywords.isBetweenKeyword(node.token) || betweenCount == betweenAndCount) {
                            node.paramCount = localParamCount + Math.max(0, node.paramCount - 1);
                            node.type = ExpressionNode.FUNCTION;
                            argStackDepth = onNode(listener, node, argStackDepth);
                            opStack.pop();
                        }
                    } else {
                        // peek the op stack to make sure it isn't a repeating brace
                        if (localParamCount > 1 && (node = opStack.peek()) != null && node.token.charAt(0) == '(') {
                            throw SqlException.$(position, "no function or operator?");
                        }
                    }
                    if (paramCountStack.notEmpty()) {
                        paramCount = paramCountStack.pop();
                    }
                    if (bracketCountStack.notEmpty()) {
                        bracketCount = bracketCountStack.pop();
                    }
                    break;
                case 'c':
                case 'C':
                    if (SqlKeywords.isCastKeyword(tok)) {
                        CharSequence caseTok = GenericLexer.immutableOf(tok);
                        tok = SqlUtil.fetchNext(lexer);
                        if (tok == null || tok.charAt(0) != '(') {
                            lexer.backTo(position + SqlKeywords.CASE_KEYWORD_LENGTH, caseTok);
                            tok = caseTok;
                            processDefaultBranch = true;
                            break;
                        }
                        lexer.backTo(position + SqlKeywords.CASE_KEYWORD_LENGTH, caseTok);
                        tok = caseTok;
                        if (prevBranch != BRANCH_DOT_DEREFERENCE) {
                            castBraceCountStack.push(-1);
                            thisBranch = BRANCH_OPERATOR;
                            opStack.push(expressionNodePool.next().of(ExpressionNode.LITERAL, "cast", Integer.MIN_VALUE, position));
                            break;
                        } else {
                            throw SqlException.$(position, "'cast' is not allowed here");
                        }
                    }
                    processDefaultBranch = true;
                    break;
                case 'a':
                case 'A':
                    if (SqlKeywords.isAsKeyword(tok)) {
                        if (castAsCount < castBraceCountStack.size()) {
                            thisBranch = BRANCH_CAST_AS;
                            // push existing args to the listener
                            int nodeCount = 0;
                            while ((node = opStack.pop()) != null && node.token.charAt(0) != '(') {
                                nodeCount++;
                                asPoppedNull = SqlKeywords.isNullKeyword(node.token);
                                argStackDepth = onNode(listener, node, argStackDepth);
                            }
                            if (nodeCount != 1) {
                                asPoppedNull = false;
                            }
                            if (node != null) {
                                opStack.push(node);
                            }
                            paramCount++;
                            castAsCount++;
                        } else {
                            processDefaultBranch = true;
                        }
                    } else if (SqlKeywords.isAndKeyword(tok)) {
                        if (caseCount == betweenStartCaseCount && betweenCount > betweenAndCount) {
                            betweenAndCount++;
                            thisBranch = BRANCH_BETWEEN_END;
                            while ((node = opStack.pop()) != null && !SqlKeywords.isBetweenKeyword(node.token)) {
                                argStackDepth = onNode(listener, node, argStackDepth);
                            }
                            if (node != null) {
                                opStack.push(node);
                            }
                        } else {
                            processDefaultBranch = true;
                        }
                    } else if (SqlKeywords.isAllKeyword(tok)) {
                        ExpressionNode operator = opStack.peek();
                        if (operator == null || operator.type != ExpressionNode.OPERATION) {
                            throw SqlException.$(position, "missing operator");
                        }
                        CharSequence funcName = allFunctions.get(operator.token);
                        if (funcName != null && operator.paramCount == 2) {
                            operator.type = ExpressionNode.FUNCTION;
                            operator.token = funcName;
                        } else {
                            throw SqlException.$(operator.position, "unexpected operator");
                        }
                    } else {
                        processDefaultBranch = true;
                    }
                    break;
                case 'b':
                case 'B':
                    if (SqlKeywords.isBetweenKeyword(tok)) {
                        thisBranch = BRANCH_BETWEEN_START;
                        if (betweenCount > betweenAndCount) {
                            // Nested between are not supported
                            throw SqlException.$(position, "between statements cannot be nested");
                        }
                        betweenCount++;
                        betweenStartCaseCount = caseCount;
                    }
                    processDefaultBranch = true;
                    break;
                case 's':
                case 'S':
                    if (SqlKeywords.isSelectKeyword(tok)) {
                        thisBranch = BRANCH_LAMBDA;
                        if (betweenCount > 0) {
                            throw SqlException.$(position, "constant expected");
                        }
                        argStackDepth = processLambdaQuery(lexer, listener, argStackDepth);
                    } else {
                        processDefaultBranch = true;
                    }
                    break;
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '\'':
                case 'E':
                    if (prevBranch != BRANCH_DOT_DEREFERENCE) {
                        // check if this is E'str'
                        if (thisChar == 'E' && (tok.length() < 3 || tok.charAt(1) != '\'')) {
                            processDefaultBranch = true;
                            break;
                        }
                        thisBranch = BRANCH_CONSTANT;
                        if (prevBranch == BRANCH_CONSTANT && position > 0) {
                            char prevChar = lexer.getContent().charAt(position - 1);
                            if (prevChar == '-' || prevChar == '+') {
                                final ExpressionNode en = opStack.peek();
                                if (en.token instanceof GenericLexer.FloatingSequence) {
                                    ((GenericLexer.FloatingSequence) en.token).setHi(lexer.getTokenHi());
                                    break;
                                }
                            }
                        }
                        if (prevBranch == BRANCH_DOT) {
                            final ExpressionNode en = opStack.peek();
                            if (en != null && en.type != ExpressionNode.CONTROL && en.type != ExpressionNode.OPERATION) {
                                // check if this is '1.2' or '1. 2'
                                if (position > 0 && lexer.getContent().charAt(position - 1) == '.') {
                                    if (en.token instanceof GenericLexer.FloatingSequence) {
                                        ((GenericLexer.FloatingSequence) en.token).setHi(lexer.getTokenHi());
                                    } else {
                                        opStack.pop();
                                        CharacterStoreEntry cse = characterStore.newEntry();
                                        cse.put(en.token).put(GenericLexer.unquote(tok));
                                        opStack.push(expressionNodePool.next().of(ExpressionNode.LITERAL, cse.toImmutable(), Integer.MIN_VALUE, en.position));
                                    }
                                    break;
                                }
                            } else {
                                opStack.push(expressionNodePool.next().of(ExpressionNode.CONSTANT, lexer.immutableBetween(position - 1, lexer.getTokenHi()), 0, position));
                                break;
                            }
                        }
                        if (prevBranch != BRANCH_DOT && nonLiteralBranches.excludes(prevBranch)) {
                            if (SqlKeywords.isQuote(tok)) {
                                throw SqlException.$(position, "unclosed quoted string?");
                            }
                            opStack.push(expressionNodePool.next().of(ExpressionNode.CONSTANT, GenericLexer.immutableOf(tok), 0, position));
                            break;
                        } else {
                            if (opStack.size() > 0 && prevBranch == BRANCH_LITERAL && thisChar == '\'') {
                                ExpressionNode prevNode = opStack.pop();
                                // This is postgres syntax to cast string literal to a type
                                // timestamp '2005-04-02 12:00:00-07'
                                // long '12321312'
                                // timestamp with time zone '2005-04-02 12:00:00-07'
                                // validate type
                                final short columnType = ColumnType.tagOf(prevNode.token);
                                if (columnType < ColumnType.BOOLEAN || columnType > ColumnType.LONG256) {
                                    throw SqlException.$(prevNode.position, "invalid type");
                                } else {
                                    ExpressionNode stringLiteral = expressionNodePool.next().of(ExpressionNode.CONSTANT, GenericLexer.immutableOf(tok), 0, position);
                                    onNode(listener, stringLiteral, 0);
                                    prevNode.type = ExpressionNode.CONSTANT;
                                    onNode(listener, prevNode, 0);
                                    ExpressionNode cast = expressionNodePool.next().of(ExpressionNode.FUNCTION, "cast", 0, prevNode.position);
                                    cast.paramCount = 2;
                                    onNode(listener, cast, argStackDepth + 2);
                                    argStackDepth++;
                                    break;
                                }
                            }
                            if (opStack.size() > 1) {
                                throw SqlException.$(position, "dangling expression");
                            }
                            lexer.unparse();
                            break OUT;
                        }
                    } else {
                        throw SqlException.$(position, "constant is not allowed here");
                    }
                case 'N':
                case 'n':
                    if (SqlKeywords.isNotKeyword(tok)) {
                        ExpressionNode nn = opStack.peek();
                        if (nn != null && nn.type == ExpressionNode.LITERAL) {
                            opStack.pop();
                            node = expressionNodePool.next().of(ExpressionNode.OPERATION, GenericLexer.immutableOf(tok), 11, position);
                            node.paramCount = 1;
                            opStack.push(node);
                            opStack.push(nn);
                            break;
                        }
                    }
                case 't':
                case 'T':
                case 'f':
                case 'F':
                    if (SqlKeywords.isNanKeyword(tok) || SqlKeywords.isNullKeyword(tok) || SqlKeywords.isTrueKeyword(tok) || SqlKeywords.isFalseKeyword(tok)) {
                        if (prevBranch != BRANCH_DOT_DEREFERENCE) {
                            thisBranch = BRANCH_CONSTANT;
                            // If the token is a number, then add it to the output queue.
                            opStack.push(expressionNodePool.next().of(ExpressionNode.CONSTANT, GenericLexer.immutableOf(tok), 0, position));
                        } else {
                            throw SqlException.$(position, "constant is not allowed here");
                        }
                        break;
                    }
                    processDefaultBranch = true;
                    break;
                case '*':
                    // special case for tab.*
                    if (prevBranch == BRANCH_DOT) {
                        thisBranch = BRANCH_LITERAL;
                        final ExpressionNode en = opStack.peek();
                        if (en != null && en.type != ExpressionNode.CONTROL) {
                            // leverage the fact '*' is dedicated token and it returned from cache
                            // therefore lexer.tokenHi does not move when * follows dot without whitespace
                            // e.g. 'a.*'
                            GenericLexer.FloatingSequence fs = (GenericLexer.FloatingSequence) en.token;
                            if (GenericLexer.WHITESPACE_CH.excludes(lexer.getContent().charAt(position - 1))) {
                                fs.setHi(position + 1);
                            } else {
                                // in this case we have whitespace, e.g. 'a. *'
                                processDefaultBranch = true;
                            }
                        } else {
                            opStack.push(expressionNodePool.next().of(ExpressionNode.CONSTANT, lexer.immutableBetween(position - 1, lexer.getTokenHi()), 0, position));
                        }
                        break;
                    }
                default:
                    processDefaultBranch = true;
                    break;
            }
            if (processDefaultBranch) {
                OperatorExpression op;
                if ((op = OperatorExpression.opMap.get(tok)) != null) {
                    thisBranch = BRANCH_OPERATOR;
                    // If the token is an operator, o1, then:
                    // while there is an operator token, o2, at the top of the operator stack, and either
                    // o1 is left-associative and its precedence is less than or equal to that of o2, or
                    // o1 is right associative, and has precedence less than that of o2,
                    // then pop o2 off the operator stack, onto the output queue;
                    // push o1 onto the operator stack.
                    int operatorType = op.type;
                    if (thisChar == '-' || thisChar == '~') {
                        switch(prevBranch) {
                            case BRANCH_OPERATOR:
                            case BRANCH_LEFT_BRACE:
                            case BRANCH_COMMA:
                            case BRANCH_NONE:
                            case BRANCH_CASE_CONTROL:
                                // we have unary minus
                                operatorType = OperatorExpression.UNARY;
                                break;
                            default:
                                break;
                        }
                    }
                    ExpressionNode other;
                    // this is to maintain correctness of -a^b
                    while ((other = opStack.peek()) != null) {
                        boolean greaterPrecedence = (op.leftAssociative && op.precedence >= other.precedence) || (!op.leftAssociative && op.precedence > other.precedence);
                        if (greaterPrecedence && (operatorType != OperatorExpression.UNARY || (operatorType == OperatorExpression.UNARY && other.paramCount == 1))) {
                            argStackDepth = onNode(listener, other, argStackDepth);
                            opStack.pop();
                        } else {
                            break;
                        }
                    }
                    node = expressionNodePool.next().of(op.type == OperatorExpression.SET ? ExpressionNode.SET_OPERATION : ExpressionNode.OPERATION, op.token, op.precedence, position);
                    if (operatorType == OperatorExpression.UNARY) {
                        node.paramCount = 1;
                    } else if (SqlKeywords.isBetweenKeyword(node.token)) {
                        node.paramCount = 3;
                    } else {
                        node.paramCount = 2;
                    }
                    opStack.push(node);
                } else if (caseCount > 0 || nonLiteralBranches.excludes(thisBranch)) {
                    // are going to flush operation stack
                    if (Chars.toLowerCaseAscii(thisChar) == 'c' && SqlKeywords.isCaseKeyword(tok)) {
                        if (prevBranch != BRANCH_DOT_DEREFERENCE) {
                            caseCount++;
                            paramCountStack.push(paramCount);
                            paramCount = 0;
                            caseBraceCountStack.push(braceCount);
                            braceCount = 0;
                            argStackDepthStack.push(argStackDepth);
                            argStackDepth = 0;
                            opStack.push(expressionNodePool.next().of(ExpressionNode.FUNCTION, "case", Integer.MAX_VALUE, position));
                            thisBranch = BRANCH_CASE_START;
                            continue;
                        } else {
                            throw SqlException.$(position, "'case' is not allowed here");
                        }
                    }
                    thisBranch = BRANCH_LITERAL;
                    if (caseCount > 0) {
                        switch(Chars.toLowerCaseAscii(thisChar)) {
                            case 'e':
                                if (SqlKeywords.isEndKeyword(tok)) {
                                    if (prevBranch == BRANCH_CASE_CONTROL) {
                                        throw missingArgs(position);
                                    }
                                    if (paramCount == 0) {
                                        throw SqlException.$(position, "'when' expected");
                                    }
                                    // If the stack runs out without finding a left parenthesis, then there are mismatched parentheses.
                                    while ((node = opStack.pop()) != null && !SqlKeywords.isCaseKeyword(node.token)) {
                                        argStackDepth = onNode(listener, node, argStackDepth);
                                    }
                                    // now we need to restore stack depth before 'case' entry
                                    if (argStackDepthStack.notEmpty()) {
                                        argStackDepth += argStackDepthStack.pop();
                                    }
                                    if (caseBraceCountStack.notEmpty()) {
                                        braceCount = caseBraceCountStack.pop();
                                    }
                                    node.paramCount = paramCount;
                                    // we also add number of 'case' arguments to original stack depth
                                    argStackDepth = onNode(listener, node, argStackDepth + paramCount);
                                    // make sure we restore paramCount
                                    if (paramCountStack.notEmpty()) {
                                        paramCount = paramCountStack.pop();
                                    }
                                    caseCount--;
                                    continue;
                                }
                            // fall through
                            case 'w':
                            case 't':
                                int keywordIndex = caseKeywords.get(tok);
                                if (keywordIndex > -1) {
                                    if (prevBranch == BRANCH_CASE_CONTROL) {
                                        throw missingArgs(position);
                                    }
                                    // we need to track argument consumption so that operators and functions
                                    // do no steal parameters outside of local 'case' scope
                                    int argCount = 0;
                                    while ((node = opStack.pop()) != null && !SqlKeywords.isCaseKeyword(node.token)) {
                                        argStackDepth = onNode(listener, node, argStackDepth);
                                        argCount++;
                                    }
                                    if (paramCount == 0) {
                                        if (argCount == 0) {
                                            // this is 'case when', we will
                                            // indicate that this is regular 'case' to the rewrite logic
                                            onNode(listener, expressionNodePool.next().of(ExpressionNode.LITERAL, null, Integer.MIN_VALUE, -1), argStackDepth);
                                        }
                                        paramCount++;
                                    }
                                    switch(keywordIndex) {
                                        // when
                                        case 0:
                                        case // else
                                        2:
                                            if ((paramCount % 2) == 0) {
                                                throw SqlException.$(position, "'then' expected");
                                            }
                                            break;
                                        default:
                                            // then
                                            if ((paramCount % 2) != 0) {
                                                throw SqlException.$(position, "'when' expected");
                                            }
                                            break;
                                    }
                                    if (node != null) {
                                        opStack.push(node);
                                    }
                                    argStackDepth = 0;
                                    paramCount++;
                                    thisBranch = BRANCH_CASE_CONTROL;
                                    continue;
                                }
                                break;
                            default:
                                break;
                        }
                    }
                    if (prevBranch == BRANCH_DOT) {
                        // this deals with 'table.column' situations
                        ExpressionNode en = opStack.peek();
                        if (en == null) {
                            throw SqlException.$(position, "qualifier expected");
                        }
                        if (GenericLexer.WHITESPACE_CH.contains(lexer.getContent().charAt(position - 1))) {
                            // 'a. b'
                            lexer.unparse();
                            break;
                        }
                        if (Chars.isQuoted(tok) || en.token instanceof CharacterStore.NameAssemblerCharSequence) {
                            // replacing node, must remove old one from stack
                            opStack.pop();
                            // this was more analogous to 'a."b"'
                            CharacterStoreEntry cse = characterStore.newEntry();
                            cse.put(en.token).put(GenericLexer.unquote(tok));
                            opStack.push(expressionNodePool.next().of(ExpressionNode.LITERAL, cse.toImmutable(), Integer.MIN_VALUE, en.position));
                        } else {
                            final GenericLexer.FloatingSequence fsA = (GenericLexer.FloatingSequence) en.token;
                            // vanilla 'a.b', just concat tokens efficiently
                            fsA.setHi(lexer.getTokenHi());
                        }
                    } else if (prevBranch != BRANCH_DOT_DEREFERENCE) {
                        // If the token is a function token, then push it onto the stack.
                        opStack.push(expressionNodePool.next().of(ExpressionNode.LITERAL, GenericLexer.unquote(tok), Integer.MIN_VALUE, position));
                    } else {
                        argStackDepth++;
                        final ExpressionNode dotDereference = expressionNodePool.next().of(ExpressionNode.OPERATION, ".", DOT_PRECEDENCE, position);
                        dotDereference.paramCount = 2;
                        opStack.push(dotDereference);
                        opStack.push(expressionNodePool.next().of(ExpressionNode.MEMBER_ACCESS, GenericLexer.unquote(tok), Integer.MIN_VALUE, position));
                    }
                } else {
                    ExpressionNode last;
                    if ((last = this.opStack.peek()) != null && SqlKeywords.isTimestampKeyword(last.token) && (SqlKeywords.isWithKeyword(tok) || SqlKeywords.isTimeKeyword(tok) || SqlKeywords.isZoneKeyword(tok))) {
                        // Skip "with time zone" part of "timestamp with time zone" for Postgres compatibility #740, #980
                        continue;
                    }
                    // literal can be at start of input, after a bracket or part of an operator
                    // all other cases are illegal and will be considered end-of-input
                    lexer.unparse();
                    break;
                }
            }
        }
        while ((node = opStack.pop()) != null) {
            if (node.token.charAt(0) == '(') {
                throw SqlException.$(node.position, "unbalanced (");
            }
            // our array dereference is dangling
            if (node.type == ExpressionNode.CONTROL && node.token.charAt(0) == '[') {
                throw SqlException.$(node.position, "unbalanced ]");
            }
            if (SqlKeywords.isCaseKeyword(node.token)) {
                throw SqlException.$(node.position, "unbalanced 'case'");
            }
            if (node.type == ExpressionNode.CONTROL) {
                // break on any other control node to allow parser to be reenterable
                // put control node back on stack because we don't own it
                opStack.push(node);
                break;
            }
            argStackDepth = onNode(listener, node, argStackDepth);
        }
    } catch (SqlException e) {
        opStack.clear();
        backupCastBraceCountStack.clear();
        backupParamCountStack.clear();
        backupArgStackDepthStack.clear();
        throw e;
    } finally {
        argStackDepthStack.clear();
        paramCountStack.clear();
        castBraceCountStack.clear();
        caseBraceCountStack.clear();
    }
}
Also used : ExpressionNode(io.questdb.griffin.model.ExpressionNode)

Example 20 with ExpressionNode

use of io.questdb.griffin.model.ExpressionNode in project questdb by bluestreak01.

the class ExpressionParser method processLambdaQuery.

private int processLambdaQuery(GenericLexer lexer, ExpressionParserListener listener, int argStackDepth) throws SqlException {
    // It is highly likely this expression parser will be re-entered when
    // parsing sub-query. To prevent sub-query consuming operation stack we must add a
    // control node, which would prevent such consumption
    // precedence must be max value to make sure control node isn't
    // consumed as parameter to a greedy function
    opStack.push(expressionNodePool.next().of(ExpressionNode.CONTROL, "|", Integer.MAX_VALUE, lexer.lastTokenPosition()));
    final int paramCountStackSize = copyToBackup(paramCountStack, backupParamCountStack);
    final int argStackDepthStackSize = copyToBackup(argStackDepthStack, backupArgStackDepthStack);
    final int castBraceCountStackSize = copyToBackup(castBraceCountStack, backupCastBraceCountStack);
    final int caseBraceCountStackSize = copyToBackup(caseBraceCountStack, backupCaseBraceCountStack);
    int pos = lexer.lastTokenPosition();
    // allow sub-query to parse "select" keyword
    lexer.unparse();
    ExpressionNode node = expressionNodePool.next().of(ExpressionNode.QUERY, null, 0, pos);
    // validate is Query is allowed
    onNode(listener, node, argStackDepth);
    // we can compile query if all is well
    node.queryModel = sqlParser.parseAsSubQuery(lexer, null);
    argStackDepth = onNode(listener, node, argStackDepth);
    // pop our control node if sub-query hasn't done it
    ExpressionNode control = opStack.peek();
    if (control != null && control.type == ExpressionNode.CONTROL && Chars.equals(control.token, '|')) {
        opStack.pop();
    }
    backupParamCountStack.copyTo(paramCountStack, paramCountStackSize);
    backupArgStackDepthStack.copyTo(argStackDepthStack, argStackDepthStackSize);
    backupCastBraceCountStack.copyTo(castBraceCountStack, castBraceCountStackSize);
    backupCaseBraceCountStack.copyTo(caseBraceCountStack, caseBraceCountStackSize);
    // re-introduce closing brace to this loop
    lexer.unparse();
    return argStackDepth;
}
Also used : ExpressionNode(io.questdb.griffin.model.ExpressionNode)

Aggregations

ExpressionNode (io.questdb.griffin.model.ExpressionNode)22 QueryColumn (io.questdb.griffin.model.QueryColumn)6 FlyweightCharSequence (io.questdb.std.str.FlyweightCharSequence)6 Function (io.questdb.cairo.sql.Function)4 GroupByFunction (io.questdb.griffin.engine.functions.GroupByFunction)3 AbstractGriffinTest (io.questdb.griffin.AbstractGriffinTest)2 SqlException (io.questdb.griffin.SqlException)2 SymbolFunction (io.questdb.griffin.engine.functions.SymbolFunction)2 ObjList (io.questdb.std.ObjList)2 Test (org.junit.Test)2 AbstractGeoHashFunction (io.questdb.griffin.engine.functions.AbstractGeoHashFunction)1 IntrinsicModel (io.questdb.griffin.model.IntrinsicModel)1 QueryModel (io.questdb.griffin.model.QueryModel)1 NotNull (org.jetbrains.annotations.NotNull)1