Expr atomicExpr()

in src/main/user-impl/java/com/mysql/cj/xdevapi/ExprParser.java [769:913]


    Expr atomicExpr() { // constant, identifier, variable, function call, etc
        if (this.tokenPos >= this.tokens.size()) {
            throw new WrongArgumentException("No more tokens when expecting one at token position " + this.tokenPos);
        }
        Token t = this.tokens.get(this.tokenPos);
        this.tokenPos++; // consume
        switch (t.type) {
            case EROTEME:
            case COLON: {
                String placeholderName;
                if (currentTokenTypeEquals(TokenType.LNUM_INT)) {
                    // int pos = Integer.parseInt(consumeToken(TokenType.LNUM_INT));
                    // return Expr.newBuilder().setType(Expr.Type.PLACEHOLDER).setPosition(pos).build();
                    placeholderName = consumeToken(TokenType.LNUM_INT);
                } else if (currentTokenTypeEquals(TokenType.IDENT)) {
                    placeholderName = consumeToken(TokenType.IDENT);
                } else if (t.type == TokenType.EROTEME) {
                    placeholderName = String.valueOf(this.positionalPlaceholderCount);
                } else {
                    throw new WrongArgumentException("Invalid placeholder name at token position " + this.tokenPos);
                }
                Expr.Builder placeholder = Expr.newBuilder().setType(Expr.Type.PLACEHOLDER);
                if (this.placeholderNameToPosition.containsKey(placeholderName)) {
                    placeholder.setPosition(this.placeholderNameToPosition.get(placeholderName));
                } else {
                    placeholder.setPosition(this.positionalPlaceholderCount);
                    this.placeholderNameToPosition.put(placeholderName, this.positionalPlaceholderCount);
                    this.positionalPlaceholderCount++;
                }
                return placeholder.build();
            }
            case LPAREN: {
                Expr e = expr();
                consumeToken(TokenType.RPAREN);
                return e;
            }
            case LCURLY: { // JSON object
                Object.Builder builder = Object.newBuilder();
                if (currentTokenTypeEquals(TokenType.LSTRING)) {
                    parseCommaSeparatedList(() -> {
                        String key = consumeToken(TokenType.LSTRING);
                        consumeToken(TokenType.COLON);
                        Expr value = expr();
                        return Collections.singletonMap(key, value);
                    }).stream().map(pair -> pair.entrySet().iterator().next()).map(e -> ObjectField.newBuilder().setKey(e.getKey()).setValue(e.getValue()))
                            .forEach(builder::addFld);
                }
                consumeToken(TokenType.RCURLY);
                return Expr.newBuilder().setType(Expr.Type.OBJECT).setObject(builder.build()).build();
            }
            case LSQBRACKET: { // Array
                Array.Builder builder = Expr.newBuilder().setType(Expr.Type.ARRAY).getArrayBuilder();
                if (!currentTokenTypeEquals(TokenType.RSQBRACKET)) {
                    parseCommaSeparatedList(this::expr).stream().forEach(builder::addValue);
                }
                consumeToken(TokenType.RSQBRACKET);
                return Expr.newBuilder().setType(Expr.Type.ARRAY).setArray(builder).build();
            }
            case CAST: {
                consumeToken(TokenType.LPAREN);
                Operator.Builder builder = Operator.newBuilder().setName(TokenType.CAST.toString().toLowerCase());
                builder.addParam(expr());
                consumeToken(TokenType.AS);
                StringBuilder typeStr = new StringBuilder(this.tokens.get(this.tokenPos).value.toUpperCase());
                // ensure next token is a valid type argument to CAST
                if (currentTokenTypeEquals(TokenType.DECIMAL)) {
                    this.tokenPos++;
                    if (currentTokenTypeEquals(TokenType.LPAREN)) {
                        typeStr.append(consumeToken(TokenType.LPAREN));
                        typeStr.append(consumeToken(TokenType.LNUM_INT));
                        if (currentTokenTypeEquals(TokenType.COMMA)) {
                            typeStr.append(consumeToken(TokenType.COMMA));
                            typeStr.append(consumeToken(TokenType.LNUM_INT));
                        }
                        typeStr.append(consumeToken(TokenType.RPAREN));
                    }
                } else if (currentTokenTypeEquals(TokenType.CHAR) || currentTokenTypeEquals(TokenType.BINARY)) {
                    this.tokenPos++;
                    if (currentTokenTypeEquals(TokenType.LPAREN)) {
                        typeStr.append(consumeToken(TokenType.LPAREN));
                        typeStr.append(consumeToken(TokenType.LNUM_INT));
                        typeStr.append(consumeToken(TokenType.RPAREN));
                    }
                } else if (currentTokenTypeEquals(TokenType.UNSIGNED) || currentTokenTypeEquals(TokenType.SIGNED)) {
                    this.tokenPos++;
                    if (currentTokenTypeEquals(TokenType.INTEGER)) {
                        // don't add optional INTEGER to type string argument
                        consumeToken(TokenType.INTEGER);
                    }
                } else if (currentTokenTypeEquals(TokenType.JSON) || currentTokenTypeEquals(TokenType.DATE) || currentTokenTypeEquals(TokenType.DATETIME)
                        || currentTokenTypeEquals(TokenType.TIME)) {
                    this.tokenPos++;
                } else {
                    throw new WrongArgumentException("Expected valid CAST type argument at " + this.tokenPos);
                }
                consumeToken(TokenType.RPAREN);
                // TODO charset?
                builder.addParam(ExprUtil.buildLiteralScalar(typeStr.toString().getBytes()));
                return Expr.newBuilder().setType(Expr.Type.OPERATOR).setOperator(builder.build()).build();
            }
            case PLUS:
            case MINUS:
                if (currentTokenTypeEquals(TokenType.LNUM_INT) || currentTokenTypeEquals(TokenType.LNUM_DOUBLE)) {
                    // unary operators are handled inline making positive or negative numeric literals
                    this.tokens.get(this.tokenPos).value = t.value + this.tokens.get(this.tokenPos).value;
                    return atomicExpr();
                }
                return buildUnaryOp(t.value, atomicExpr());

            case NOT:
            case NEG:
            case BANG:
                return buildUnaryOp(t.value, atomicExpr());
            case LSTRING:
                return ExprUtil.buildLiteralScalar(t.value);
            case NULL:
                return ExprUtil.buildLiteralNullScalar();
            case LNUM_INT:
                return ExprUtil.buildLiteralScalar(Long.parseLong(t.value));
            case LNUM_DOUBLE:
                return ExprUtil.buildLiteralScalar(Double.parseDouble(t.value));
            case TRUE:
            case FALSE:
                return ExprUtil.buildLiteralScalar(t.type == TokenType.TRUE);
            case DOLLAR:
                return documentField();
            case STAR:
                // special "0-ary" consideration of "*" as an operator (for COUNT(*), etc)
                return starOperator();
            case IDENT:
                this.tokenPos--; // stay on the identifier
                // check for function call which may be: func(...) or schema.func(...)
                if (nextTokenTypeEquals(TokenType.LPAREN) || posTokenTypeEquals(this.tokenPos + 1, TokenType.DOT)
                        && posTokenTypeEquals(this.tokenPos + 2, TokenType.IDENT) && posTokenTypeEquals(this.tokenPos + 3, TokenType.LPAREN)) {
                    return functionCall();
                }
                if (this.allowRelationalColumns) {
                    return columnIdentifier();
                }
                return documentField();
            default:
                break;
        }
        throw new WrongArgumentException("Cannot find atomic expression at token position " + (this.tokenPos - 1));
    }