com.antsdb.saltedfish.sql.mysql.ExprGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.antsdb.saltedfish.sql.mysql.ExprGenerator.java

Source

/*-------------------------------------------------------------------------------------------------
 _______ __   _ _______ _______ ______  ______
 |_____| | \  |    |    |______ |     \ |_____]
 |     | |  \_|    |    ______| |_____/ |_____]
    
 Copyright (c) 2016, antsdb.com and/or its affiliates. All rights reserved. *-xguo0<@
    
 This program is free software: you can redistribute it and/or modify it under the terms of the
 GNU Affero General Public License, version 3, as published by the Free Software Foundation.
    
 You should have received a copy of the GNU Affero General Public License along with this program.
 If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>
-------------------------------------------------------------------------------------------------*/
package com.antsdb.saltedfish.sql.mysql;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.BailErrorStrategy;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang.StringUtils;

import com.antsdb.saltedfish.lexer.MysqlLexer;
import com.antsdb.saltedfish.lexer.MysqlParser;
import com.antsdb.saltedfish.lexer.MysqlParser.Bind_parameterContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Column_name_Context;
import com.antsdb.saltedfish.lexer.MysqlParser.ExprContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Expr_castContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Expr_existContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Expr_functionContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Expr_in_selectContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Expr_in_valuesContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Expr_matchContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Expr_parenthesisContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Expr_selectContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Literal_intervalContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Literal_valueContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Literal_value_binaryContext;
import com.antsdb.saltedfish.lexer.MysqlParser.System_variable_referenceContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Unary_operatorContext;
import com.antsdb.saltedfish.lexer.MysqlParser.Variable_referenceContext;
import com.antsdb.saltedfish.sql.DataType;
import com.antsdb.saltedfish.sql.GeneratorContext;
import com.antsdb.saltedfish.sql.OrcaException;
import com.antsdb.saltedfish.sql.planner.Planner;
import com.antsdb.saltedfish.sql.planner.PlannerField;
import com.antsdb.saltedfish.sql.vdm.BindParameter;
import com.antsdb.saltedfish.sql.vdm.BytesValue;
import com.antsdb.saltedfish.sql.vdm.CurrentTime;
import com.antsdb.saltedfish.sql.vdm.CurrentTimestamp;
import com.antsdb.saltedfish.sql.vdm.CursorMaker;
import com.antsdb.saltedfish.sql.vdm.FieldValue;
import com.antsdb.saltedfish.sql.vdm.FuncAbs;
import com.antsdb.saltedfish.sql.vdm.FuncBase64;
import com.antsdb.saltedfish.sql.vdm.FuncCast;
import com.antsdb.saltedfish.sql.vdm.FuncConcat;
import com.antsdb.saltedfish.sql.vdm.FuncCount;
import com.antsdb.saltedfish.sql.vdm.FuncCurDate;
import com.antsdb.saltedfish.sql.vdm.FuncCurrentUser;
import com.antsdb.saltedfish.sql.vdm.FuncDatabase;
import com.antsdb.saltedfish.sql.vdm.FuncDateAdd;
import com.antsdb.saltedfish.sql.vdm.FuncDateFormat;
import com.antsdb.saltedfish.sql.vdm.FuncDateSub;
import com.antsdb.saltedfish.sql.vdm.FuncDistinct;
import com.antsdb.saltedfish.sql.vdm.FuncElt;
import com.antsdb.saltedfish.sql.vdm.FuncEmptyClob;
import com.antsdb.saltedfish.sql.vdm.FuncField;
import com.antsdb.saltedfish.sql.vdm.FuncFindInSet;
import com.antsdb.saltedfish.sql.vdm.FuncGroupConcat;
import com.antsdb.saltedfish.sql.vdm.FuncHex;
import com.antsdb.saltedfish.sql.vdm.FuncLeft;
import com.antsdb.saltedfish.sql.vdm.FuncLength;
import com.antsdb.saltedfish.sql.vdm.FuncLocate;
import com.antsdb.saltedfish.sql.vdm.FuncLower;
import com.antsdb.saltedfish.sql.vdm.FuncMax;
import com.antsdb.saltedfish.sql.vdm.FuncMin;
import com.antsdb.saltedfish.sql.vdm.FuncMonth;
import com.antsdb.saltedfish.sql.vdm.FuncNow;
import com.antsdb.saltedfish.sql.vdm.FuncRand;
import com.antsdb.saltedfish.sql.vdm.FuncSubstringIndex;
import com.antsdb.saltedfish.sql.vdm.FuncSum;
import com.antsdb.saltedfish.sql.vdm.FuncToTimestamp;
import com.antsdb.saltedfish.sql.vdm.FuncTrim;
import com.antsdb.saltedfish.sql.vdm.FuncUpper;
import com.antsdb.saltedfish.sql.vdm.FuncUser;
import com.antsdb.saltedfish.sql.vdm.FuncVersion;
import com.antsdb.saltedfish.sql.vdm.FuncWeekday;
import com.antsdb.saltedfish.sql.vdm.FuncYear;
import com.antsdb.saltedfish.sql.vdm.Function;
import com.antsdb.saltedfish.sql.vdm.LongValue;
import com.antsdb.saltedfish.sql.vdm.NullValue;
import com.antsdb.saltedfish.sql.vdm.NumericValue;
import com.antsdb.saltedfish.sql.vdm.ObjectName;
import com.antsdb.saltedfish.sql.vdm.OpAdd;
import com.antsdb.saltedfish.sql.vdm.OpAddTime;
import com.antsdb.saltedfish.sql.vdm.OpAnd;
import com.antsdb.saltedfish.sql.vdm.OpBetween;
import com.antsdb.saltedfish.sql.vdm.OpBinary;
import com.antsdb.saltedfish.sql.vdm.OpConcat;
import com.antsdb.saltedfish.sql.vdm.OpEqual;
import com.antsdb.saltedfish.sql.vdm.OpEqualNull;
import com.antsdb.saltedfish.sql.vdm.OpExists;
import com.antsdb.saltedfish.sql.vdm.OpInSelect;
import com.antsdb.saltedfish.sql.vdm.OpInValues;
import com.antsdb.saltedfish.sql.vdm.OpInterval;
import com.antsdb.saltedfish.sql.vdm.OpIsNull;
import com.antsdb.saltedfish.sql.vdm.OpLarger;
import com.antsdb.saltedfish.sql.vdm.OpLargerEqual;
import com.antsdb.saltedfish.sql.vdm.OpLess;
import com.antsdb.saltedfish.sql.vdm.OpLessEqual;
import com.antsdb.saltedfish.sql.vdm.OpLike;
import com.antsdb.saltedfish.sql.vdm.OpMatch;
import com.antsdb.saltedfish.sql.vdm.OpMinusTime;
import com.antsdb.saltedfish.sql.vdm.OpMultiply;
import com.antsdb.saltedfish.sql.vdm.OpNegate;
import com.antsdb.saltedfish.sql.vdm.OpNot;
import com.antsdb.saltedfish.sql.vdm.OpOr;
import com.antsdb.saltedfish.sql.vdm.OpRegexp;
import com.antsdb.saltedfish.sql.vdm.OpSequenceValue;
import com.antsdb.saltedfish.sql.vdm.OpSingleValueQuery;
import com.antsdb.saltedfish.sql.vdm.Operator;
import com.antsdb.saltedfish.sql.vdm.RowidValue;
import com.antsdb.saltedfish.sql.vdm.StringLiteral;
import com.antsdb.saltedfish.sql.vdm.SysDate;
import com.antsdb.saltedfish.sql.vdm.SystemVariableValue;
import com.antsdb.saltedfish.sql.vdm.UserVariableValue;
import com.antsdb.saltedfish.util.BytesUtil;

public class ExprGenerator {
    static Map<String, Class<? extends Function>> _functionByName = new HashMap<>();

    static {
        _functionByName.put("abs", FuncAbs.class);
        _functionByName.put("adddate", FuncDateAdd.class);
        _functionByName.put("concat", FuncConcat.class);
        _functionByName.put("curdate", FuncCurDate.class);
        _functionByName.put("current_user", FuncCurrentUser.class);
        _functionByName.put("database", FuncDatabase.class);
        _functionByName.put("date_add", FuncDateAdd.class);
        _functionByName.put("date_format", FuncDateFormat.class);
        _functionByName.put("date_sub", FuncDateSub.class);
        _functionByName.put("elt", FuncElt.class);
        _functionByName.put("empty_clob", FuncEmptyClob.class);
        _functionByName.put("field", FuncField.class);
        _functionByName.put("find_in_set", FuncFindInSet.class);
        _functionByName.put("hex", FuncHex.class);
        _functionByName.put("length", FuncLength.class);
        _functionByName.put("left", FuncLeft.class);
        _functionByName.put("locate", FuncLocate.class);
        _functionByName.put("lower", FuncLower.class);
        _functionByName.put("month", FuncMonth.class);
        _functionByName.put("now", FuncNow.class);
        _functionByName.put("rand", FuncRand.class);
        _functionByName.put("subdate", FuncDateSub.class);
        _functionByName.put("substring_index", FuncSubstringIndex.class);
        _functionByName.put("totimestamp", FuncToTimestamp.class);
        _functionByName.put("to_base64", FuncBase64.class);
        _functionByName.put("to_timestamp", FuncToTimestamp.class);
        _functionByName.put("trim", FuncTrim.class);
        _functionByName.put("user", FuncUser.class);
        _functionByName.put("upper", FuncUpper.class);
        _functionByName.put("user", FuncUser.class);
        _functionByName.put("version", FuncVersion.class);
        _functionByName.put("weekday", FuncWeekday.class);
        _functionByName.put("year", FuncYear.class);
    }

    public static Operator gen(GeneratorContext ctx, Planner planner, ExprContext rule) {
        Operator op = gen_(ctx, planner, rule);
        return op;
    }

    static Operator gen_(GeneratorContext ctx, Planner cursorMeta, ExprContext rule) throws OrcaException {
        if (rule.getChildCount() == 1) {
            return gen_sinlge_node(ctx, cursorMeta, rule);
        } else if (rule.expr_in_values() != null) {
            Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
            return gen_in_values(ctx, cursorMeta, rule.expr_in_values(), left);
        } else if (rule.expr_in_select() != null) {
            Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
            return gen_in_select(ctx, cursorMeta, rule.expr_in_select(), left);
        } else if (rule.unary_operator() != null) {
            Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(1));
            return gen_unary(ctx, cursorMeta, rule.unary_operator(), right);
        } else if (rule.K_REGEXP() != null) {
            // REGEXP operator
            Operator expr = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
            String text = rule.literal_value().getText();
            text = text.substring(1, text.length() - 1);
            return new OpRegexp(expr, text);
        } else if (rule.K_BETWEEN() != null) {
            Operator expr = gen(ctx, cursorMeta, rule.expr(0));
            Operator from = gen(ctx, cursorMeta, rule.expr(1));
            Operator to = gen(ctx, cursorMeta, rule.expr(2));
            return new OpBetween(expr, from, to);
        } else if (rule.getChild(1) instanceof TerminalNode) {
            String op = rule.getChild(1).getText();
            if ("=".equals(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpEqual(left, right);
            } else if ("==".equals(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpEqualNull(left, right);
            } else if ("+".equals(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                if ((left instanceof OpInterval) || (right instanceof OpInterval)) {
                    return new OpAddTime(left, right);
                } else {
                    return new OpAdd(left, right);
                }
            } else if ("-".equals(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                if ((left instanceof OpInterval) || (right instanceof OpInterval)) {
                    return new OpMinusTime(left, right);
                } else {
                    return new OpAdd(left, new OpNegate(right));
                }
            } else if ("*".equals(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpMultiply(left, right);
            } else if (">".equals(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpLarger(left, right);
            } else if (">=".equals(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpLargerEqual(left, right);
            } else if ("<".equals(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpLess(left, right);
            } else if ("<=".equals(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpLessEqual(left, right);
            } else if ("!=".equals(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpNot(new OpEqual(left, right));
            } else if ("<>".equals(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpNot(new OpEqual(left, right));
            } else if ("like".equalsIgnoreCase(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpLike(left, right);
            } else if ("or".equalsIgnoreCase(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpOr(left, right);
            } else if ("and".equalsIgnoreCase(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpAnd(left, right);
            } else if ("||".equalsIgnoreCase(op)) {
                Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                Operator right = gen(ctx, cursorMeta, (ExprContext) rule.getChild(2));
                return new OpConcat(left, right);
            } else if ("is".equalsIgnoreCase(op)) {
                if (rule.getChild(2) instanceof ExprContext) {
                    Operator upstream = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                    return new OpIsNull(upstream);
                } else if (rule.getChild(2) instanceof TerminalNode) {
                    // IS NOT
                    Operator upstream = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                    return new OpNot(new OpIsNull(upstream));
                } else {
                    throw new NotImplementedException();
                }
            } else if ("not".equalsIgnoreCase(op)) {
                if (rule.getChild(2) instanceof TerminalNode) {
                    TerminalNode node = (TerminalNode) rule.getChild(2);
                    if (node.getSymbol().getType() == MysqlParser.K_LIKE) {
                        Operator left = gen(ctx, cursorMeta, (ExprContext) rule.getChild(0));
                        Operator right = genLiteralValue(ctx, cursorMeta, (Literal_valueContext) rule.getChild(3));
                        return new OpNot(new OpLike(left, right));
                    }
                }
                throw new NotImplementedException();
            } else {
                throw new NotImplementedException();
            }
        } else if (rule.K_DISTINCT() != null) {
            Operator op = gen(ctx, cursorMeta, rule.expr(0));
            return new FuncDistinct(op, ctx.allocVariable());
        } else {
            String text = rule.getText();
            throw new NotImplementedException(text);
        }
    }

    private static Operator gen_unary(GeneratorContext ctx, Planner cursorMeta, Unary_operatorContext rule,
            Operator right) {
        TerminalNode token = (TerminalNode) rule.getChild(0);
        if (token.getSymbol().getType() == MysqlParser.K_NOT) {
            return new OpNot(right);
        } else if (token.getSymbol().getType() == MysqlParser.MINUS) {
            return new OpNegate(right);
        } else if (token.getSymbol().getType() == MysqlParser.K_BINARY) {
            return new OpBinary(right);
        } else {
            throw new NotImplementedException();
        }
    }

    private static Operator gen_exists(GeneratorContext ctx, Planner cursorMeta, Expr_existContext rule) {
        if (rule.select_stmt().select_or_values().size() != 1) {
            throw new NotImplementedException();
        }
        CursorMaker select = (CursorMaker) new Select_or_valuesGenerator().genSubquery(ctx, rule.select_stmt(),
                cursorMeta);
        Operator in = new OpExists(select);
        if (rule.K_NOT() != null) {
            in = new OpNot(in);
        }
        return in;
    }

    private static Operator gen_in_select(GeneratorContext ctx, Planner cursorMeta, Expr_in_selectContext rule,
            Operator left) {
        if (rule.select_stmt().select_or_values().size() != 1) {
            throw new NotImplementedException();
        }
        /* workaround for topka query
        CursorMaker select = (CursorMaker)new Select_or_valuesGenerator().genSubquery(
            ctx, 
            rule.select_stmt(),
            cursorMeta);
        */
        CursorMaker select = (CursorMaker) new Select_stmtGenerator().gen(ctx, rule.select_stmt());
        Operator in = new OpInSelect(left, select);
        if (rule.K_NOT() != null) {
            in = new OpNot(in);
        }
        return in;
    }

    private static Operator gen_in_values(GeneratorContext ctx, Planner cursorMeta, Expr_in_valuesContext rule,
            Operator left) {
        List<Operator> values = new ArrayList<Operator>();
        for (ExprContext i : rule.expr()) {
            Operator expr = gen(ctx, cursorMeta, i);
            values.add(expr);
        }
        return new OpInValues(left, values);
    }

    private static Operator genSingleValueQuery(GeneratorContext ctx, Planner cursorMeta, Expr_selectContext rule) {
        if (rule.select_stmt().select_or_values().size() != 1) {
            throw new NotImplementedException();
        }
        CursorMaker select = (CursorMaker) new Select_or_valuesGenerator().genSubquery(ctx, rule.select_stmt(),
                cursorMeta);
        if (select.getCursorMeta().getColumnCount() != 1) {
            throw new OrcaException("Operand should contain 1 column");
        }
        return new OpSingleValueQuery(select);
    }

    public static Operator gen_sinlge_node(GeneratorContext ctx, Planner cursorMeta, ExprContext rule) {
        ParseTree child = rule.getChild(0);
        if (child instanceof Literal_valueContext) {
            return genLiteralValue(ctx, cursorMeta, (Literal_valueContext) child);
        } else if (child instanceof Bind_parameterContext) {
            return genBindParameter(ctx, (Bind_parameterContext) child);
        } else if (child instanceof Column_name_Context) {
            return genColumnValue(ctx, cursorMeta, (Column_name_Context) child);
        } else if (child instanceof Expr_functionContext) {
            return genFunction(ctx, cursorMeta, (Expr_functionContext) child);
        } else if (child instanceof Expr_parenthesisContext) {
            return gen(ctx, cursorMeta, ((Expr_parenthesisContext) child).expr());
        } else if (child instanceof Expr_selectContext) {
            return genSingleValueQuery(ctx, cursorMeta, (Expr_selectContext) child);
        } else if (child instanceof Variable_referenceContext) {
            return genUserVariableRef(ctx, cursorMeta, (Variable_referenceContext) child);
        } else if (child instanceof System_variable_referenceContext) {
            return genSystemVariableRef(ctx, cursorMeta, (System_variable_referenceContext) child);
        } else if (rule.expr_exist() != null) {
            return gen_exists(ctx, cursorMeta, rule.expr_exist());
        } else if (rule.expr_cast() != null) {
            return gen_cast(ctx, cursorMeta, rule.expr_cast());
        } else if (rule.expr_match() != null) {
            return gen_match(ctx, cursorMeta, rule.expr_match());
        } else {
            throw new NotImplementedException();
        }
    }

    private static Operator gen_match(GeneratorContext ctx, Planner planner, Expr_matchContext rule) {
        List<FieldValue> columns = new ArrayList<>();
        rule.column_name_().forEach((it) -> {
            Operator op = genColumnValue(ctx, planner, it);
            if (!(op instanceof FieldValue)) {
                throw new OrcaException("{} is not an column reference", op);
            }
            columns.add((FieldValue) op);
        });
        if (columns.size() <= 0) {
            throw new OrcaException("column reference is missing");
        }
        Operator against = gen(ctx, planner, rule.expr());
        return new OpMatch(columns, against);
    }

    private static Operator gen_cast(GeneratorContext ctx, Planner cursorMeta, Expr_castContext rule) {
        DataType type = DataType.parse(ctx.getTypeFactory(), rule.data_type());
        Operator expr = gen(ctx, cursorMeta, rule.expr());
        return new FuncCast(type, expr);
    }

    static Operator genSystemVariableRef(GeneratorContext ctx, Planner meta,
            System_variable_referenceContext rule) {
        return new SystemVariableValue(rule.getText());
    }

    static Operator genUserVariableRef(GeneratorContext ctx, Planner meta, Variable_referenceContext rule) {
        return new UserVariableValue(rule.getText());
    }

    private static Operator genFunction(GeneratorContext ctx, Planner cursorMeta, Expr_functionContext rule) {
        String name = rule.function_name().getText().toLowerCase();
        Function func = null;
        try {
            Class<? extends Function> klass = _functionByName.get(name);
            if (klass != null) {
                func = _functionByName.get(name).newInstance();
            }
        } catch (Exception x) {
            throw new OrcaException(x);
        }
        if (func == null) {
            // aggregate functions below
            if (name.equalsIgnoreCase("count")) {
                if (rule.expr_function_star_parameter() != null) {
                    func = new FuncCount(ctx.allocVariable());
                    func.addParameter(null);
                } else {
                    if (rule.expr_function_parameters().expr().size() != 1) {
                        throw new OrcaException("count takes one and only one input parameter");
                    }
                    func = new FuncCount(ctx.allocVariable());
                }
            } else if (name.equalsIgnoreCase("max")) {
                func = new FuncMax(ctx.allocVariable());
            } else if (name.equalsIgnoreCase("min")) {
                func = new FuncMin(ctx.allocVariable());
            } else if (name.equalsIgnoreCase("sum")) {
                if (rule.expr_function_parameters() == null) {
                    throw new OrcaException("count takes one and only one input parameter");
                }
                if (rule.expr_function_parameters().expr().size() != 1) {
                    throw new OrcaException("count takes one and only one input parameter");
                }
                Operator expr = ExprGenerator.gen(ctx, cursorMeta, rule.expr_function_parameters().expr(0));
                func = new FuncSum(ctx.allocVariable(), expr);
            } else if (name.equalsIgnoreCase("GROUP_CONCAT")) {
                if (rule.expr_function_parameters().expr().size() != 1) {
                    throw new OrcaException("GROUP_CONCAT requires one input parameter");
                }
                func = new FuncGroupConcat(ctx.allocVariable());
            } else {
                throw new NotImplementedException("function: " + name);
            }
        }
        if (rule.expr_function_parameters() != null) {
            for (ExprContext i : rule.expr_function_parameters().expr()) {
                func.addParameter(ExprGenerator.gen(ctx, cursorMeta, i));
            }
        }
        if ((func.getMinParameters() >= 0) && (func.getChildren().size() < func.getMinParameters())) {
            throw new OrcaException("{}() requires at least {} parameters", name, func.getMinParameters());
        }
        if ((func.getMaxParameters() >= 0) && (func.getChildren().size() > func.getMaxParameters())) {
            throw new OrcaException("{}() cannot take more than {} parameters", name, func.getMaxParameters());
        }
        return func;
    }

    private static Operator genColumnValue(GeneratorContext ctx, Planner planner, Column_name_Context rule) {
        if (planner == null) {
            // why there is no cursor metadata
            throw new OrcaException("missing query");
        }
        ObjectName tableName = TableName.parse(ctx, rule.table_name_());
        String table = (tableName != null) ? tableName.getTableName() : null;
        String column = Utils.getIdentifier(rule.column_name().identifier());

        // check built-in name

        if ("rowid".equalsIgnoreCase(column)) {
            return new RowidValue();
        }
        if ("nextval".equalsIgnoreCase(column)) {
            return genSequenceValue(ctx, rule);
        }

        // check table columns

        PlannerField pos = planner.findField(it -> {
            if (!column.equalsIgnoreCase(it.getName())) {
                return false;
            }
            if (table != null) {
                if (!StringUtils.equalsIgnoreCase(table, it.getTableAlias())) {
                    return false;
                }
            }
            return true;
        });
        if (pos == null) {
            throw new OrcaException("column not found: " + column);
        }
        FieldValue cv = new FieldValue(pos);
        return cv;
    }

    private static Operator genSequenceValue(GeneratorContext ctx, Column_name_Context rule) {
        ObjectName name = (rule.table_name_() != null) ? TableName.parse(ctx, rule.table_name_()) : null;
        return new OpSequenceValue(name);
    }

    public static Operator genLiteralValue(GeneratorContext ctx, Planner planner, Literal_valueContext rule) {
        if (rule.literal_value_binary() != null) {
            return new BytesValue(getBytes(rule.literal_value_binary()));
        } else if (rule.literal_interval() != null) {
            return parseInterval(ctx, planner, rule.literal_interval());
        }
        TerminalNode token = (TerminalNode) rule.getChild(0);
        switch (token.getSymbol().getType()) {
        case MysqlParser.NUMERIC_LITERAL: {
            BigDecimal bd = new BigDecimal(rule.getText());
            try {
                return new LongValue(bd.longValueExact());
            } catch (Exception ignored) {
            }
            return new NumericValue(new BigDecimal(rule.getText()));
        }
        case MysqlParser.STRING_LITERAL:
        case MysqlParser.DOUBLE_QUOTED_LITERAL:
            String value = Utils.getQuotedLiteralValue(rule);
            return new StringLiteral(value);
        case MysqlParser.K_NULL:
            return new NullValue();
        case MysqlParser.K_CURRENT_DATE:
            return new SysDate();
        case MysqlParser.K_CURRENT_TIME:
            return new CurrentTime();
        case MysqlParser.K_CURRENT_TIMESTAMP:
            return new CurrentTimestamp();
        case MysqlParser.K_TRUE:
            return new LongValue(1);
        case MysqlParser.K_FALSE:
            return new LongValue(0);
        case MysqlParser.BLOB_LITERAL:
            String text = rule.BLOB_LITERAL().getText();
            return new BytesValue(mysqlXBinaryToBytes(text));
        case MysqlParser.HEX_LITERAL:
            String hextxt = rule.HEX_LITERAL().getText();
            hextxt = hextxt.substring(2, hextxt.length());
            if (hextxt.length() == 0) {
                return new BytesValue(new byte[0]);
            }
            return new BytesValue(BytesUtil.hexToBytes(hextxt));
        default:
            throw new NotImplementedException();
        }
    }

    private static Operator parseInterval(GeneratorContext ctx, Planner planner, Literal_intervalContext rule) {
        Operator upstream = gen(ctx, planner, rule.expr());
        String unit = rule.WORD().getText();
        long multiplier;
        if (unit.equalsIgnoreCase("SECOND")) {
            multiplier = 1000;
        } else if (unit.equalsIgnoreCase("MINUTE")) {
            multiplier = 1000 * 60;
        } else if (unit.equalsIgnoreCase("HOUR")) {
            multiplier = 1000 * 60 * 60;
        } else if (unit.equalsIgnoreCase("DAY")) {
            multiplier = 1000 * 60 * 60 * 24;
        } else if (unit.equalsIgnoreCase("WEEK")) {
            multiplier = 1000 * 60 * 60 * 24 * 7;
        } else {
            throw new OrcaException("unknown unit of time: " + unit);
        }
        return new OpInterval(upstream, multiplier);
    }

    private static byte[] mysqlXBinaryToBytes(String text) {
        if (text.length() < 3) {
            throw new IllegalArgumentException("invalid binary format: " + text);
        }
        if (text.length() % 2 != 1) {
            throw new IllegalArgumentException("invalid binary format: " + text);
        }
        byte[] bytes = new byte[(text.length() - 3) / 2];
        for (int i = 2; i < text.length() - 1; i += 2) {
            int ch1 = text.charAt(i);
            int ch2 = text.charAt(i + 1);
            ch1 = Character.digit(ch1, 16);
            ch2 = Character.digit(ch2, 16);
            int n = ch1 << 4 | ch2;
            bytes[i / 2 - 1] = (byte) n;
        }
        return bytes;
    }

    private static byte[] getBytes(Literal_value_binaryContext rule) {
        Token token = rule.STRING_LITERAL().getSymbol();
        byte[] bytes = new byte[token.getStopIndex() - token.getStartIndex() - 1];
        CharStream cs = token.getInputStream();
        int pos = cs.index();
        cs.seek(token.getStartIndex() + 1);
        int j = 0;
        for (int i = 0; i < bytes.length; i++) {
            int ch = cs.LA(i + 1);
            if (ch == '\\') {
                i++;
                ch = cs.LA(i + 1);
                if (ch == '0') {
                    ch = 0;
                } else if (ch == 'n') {
                    ch = '\n';
                } else if (ch == 'r') {
                    ch = '\r';
                } else if (ch == 'Z') {
                    ch = '\032';
                }
            }
            bytes[j] = (byte) ch;
            j++;
        }
        cs.seek(pos);
        if (j != bytes.length) {
            // esacpe characters
            byte[] old = bytes;
            bytes = new byte[j];
            System.arraycopy(old, 0, bytes, 0, j);
        }
        return bytes;
    }

    private static Operator genBindParameter(GeneratorContext ctx, Bind_parameterContext rule) {
        return new BindParameter(ctx.getParameterPosition(rule.BIND_PARAMETER()));
    }

    public static Operator gen(GeneratorContext ctx, Planner cursorMeta, String expr) {
        CharStream cs = new ANTLRInputStream(expr);
        MysqlLexer lexer = new MysqlLexer(cs);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        tokens.setTokenSource(lexer);
        MysqlParser parser = new MysqlParser(tokens);
        parser.setErrorHandler(new BailErrorStrategy());
        MysqlParser.ExprContext rule = parser.expr();
        return gen(ctx, cursorMeta, rule);
    }
}