/*
 * Copyright (C) 2016-2022 ActionTech.
 * based on code by MyCATCopyrightHolder Copyright (c) 2013, OpenCloudDB/MyCAT.
 * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.
 */

package com.actiontech.dble.server.parser;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLLimit;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.SQLReplaceable;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;

import java.util.List;

/**
 * @author dcy
 * Create Date: 2020-12-25
 */
public class PrepareChangeVisitor extends MySqlASTVisitorAdapter {
    boolean isFirstSelectQuery = true;

    @Override
    public boolean visit(MySqlSelectQueryBlock x) {

        /*
            Change sql.
            append the  '1!=1' condition.
            So every sql  return field packets with zero rows.
         */

        if (x.getFrom() != null) {
            final SQLExpr sqlExpr = SQLUtils.buildCondition(SQLBinaryOperator.BooleanAnd, SQLUtils.toSQLExpr("1!=1"), true, x.getWhere());
            x.setWhere(sqlExpr);
        } else {
            //if a sql without reference any table. it shouldn't add '1!=1' condition.  Although it works in msyql 8.0 but cause syntax error in mysql 5.7.
        }

        x.setGroupBy(null);
        x.setOrderBy(null);
        /*
            Double guarantee
        */
        x.setLimit(new SQLLimit(new SQLIntegerExpr(0)));
        if (isFirstSelectQuery) {
            final List<String> commentsDirect = x.getBeforeCommentsDirect();
            final String comment = "/* used for prepare statement. */";
            if (commentsDirect != null) {
                commentsDirect.add(0, comment);
            } else {
                x.addBeforeComment(comment);
            }

            isFirstSelectQuery = false;
        }

        /*
            in single node mysql ,one '1!=1' condition is enough.It always return zero rows.
            because dble split complex query before send.So every query should append this condition.So...there need return true to access nested select.
         */
        return true;
    }


    @Override
    public boolean visit(SQLVariantRefExpr x) {
        if (x.getParent() != null && x.getParent() instanceof SQLSelectItem) {
            ((SQLSelectItem) x.getParent()).replace(x, SQLUtils.toSQLExpr("true"));
            return false;
        }
        SQLObject parent;
        SQLObject current = x.getParent();

        //nest lookup. find closest SQLReplaceable and replace if possible.
        while (current != null && (parent = current.getParent()) != null) {

            if (parent instanceof SQLReplaceable) {
                if (current instanceof SQLExpr) {
                    ((SQLReplaceable) parent).replace((SQLExpr) current, SQLUtils.toSQLExpr("true"));
                    break;
                } else {
                    current = parent;
                }
            } else {
                current = parent;

            }
        }
        return false;
    }


}
