/*
 * Copyright 1999-2017 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.druid.sql.dialect.mysql.visitor;

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.expr.SQLListExpr;
import com.alibaba.druid.sql.ast.statement.*;
import com.alibaba.druid.sql.dialect.mysql.ast.*;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.*;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr;
import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr;
import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlUserName;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.*;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement.TableSpaceOption;
import com.alibaba.druid.sql.repository.SchemaRepository;
import com.alibaba.druid.sql.visitor.SchemaStatVisitor;
import com.alibaba.druid.stat.TableStat;
import com.alibaba.druid.stat.TableStat.Mode;

public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlASTVisitor {
    public MySqlSchemaStatVisitor() {
        super(DbType.mysql);
    }

    public MySqlSchemaStatVisitor(SchemaRepository repository) {
        super(repository);
    }

    public boolean visit(SQLSelectStatement x) {
        if (repository != null
                && x.getParent() == null) {
            repository.resolve(x);
        }

        return true;
    }

    @Override
    public DbType getDbType() {
        return DbType.mysql;
    }

    // DUAL
    public boolean visit(MySqlDeleteStatement x) {
        if (repository != null
                && x.getParent() == null) {
            repository.resolve(x);
        }

        SQLTableSource from = x.getFrom();
        if (from != null) {
            from.accept(this);
        }

        SQLTableSource using = x.getUsing();
        if (using != null) {
            using.accept(this);
        }

        SQLTableSource tableSource = x.getTableSource();
        tableSource.accept(this);

        if (tableSource instanceof SQLExprTableSource) {
            TableStat stat = this.getTableStat((SQLExprTableSource) tableSource);
            stat.incrementDeleteCount();
        }

        accept(x.getWhere());

        accept(x.getOrderBy());
        accept(x.getLimit());

        return false;
    }

    @Override
    public void endVisit(MySqlInsertStatement x) {
        setModeOrigin(x);
    }

    @Override
    public boolean visit(MySqlInsertStatement x) {
        if (repository != null
                && x.getParent() == null) {
            repository.resolve(x);
        }

        setMode(x, Mode.Insert);

        TableStat stat = getTableStat(x.getTableSource());

        if (stat != null) {
            stat.incrementInsertCount();
        }

        accept(x.getColumns());
        accept(x.getValuesList());
        accept(x.getQuery());
        accept(x.getDuplicateKeyUpdate());

        return false;
    }

    @Override
    public boolean visit(MySqlTableIndex x) {
        return false;
    }

    @Override
    public boolean visit(MySqlKey x) {
        for (SQLObject item : x.getColumns()) {
            item.accept(this);
        }
        return false;
    }

    @Override
    public boolean visit(MySqlPrimaryKey x) {
        for (SQLSelectOrderByItem item : x.getColumns()) {
            SQLExpr expr = item.getExpr();
            expr.accept(this);
        }
        return false;
    }

    @Override
    public boolean visit(SQLShowColumnsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowDatabaseStatusStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlCreateExternalCatalogStatement x) {
        return true;
    }

    @Override
    public boolean visit(MySqlCreateUserStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlPartitionByKey x) {
        accept(x.getColumns());
        return false;
    }

    @Override
    public boolean visit(MySqlUpdatePlanCacheStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowPlanCacheStatusStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlClearPlanCacheStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlDisabledPlanCacheStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterDatabaseSetOption x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterDatabaseKillJob x) {
        return false;
    }

    @Override
    public boolean visit(MySqlExplainPlanCacheStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlOutFileExpr x) {
        return false;
    }

    @Override
    public boolean visit(MySqlExplainStatement x) {
        if (repository != null
                && x.getParent() == null) {
            repository.resolve(x);
        }

        SQLName tableName = x.getTableName();
        if (tableName != null) {
            getTableStat(tableName);

            SQLName columnName = x.getColumnName();
            if (columnName != null) {
                addColumn(tableName, columnName.getSimpleName());
            }
        }

        if (x.getStatement() != null) {
            accept(x.getStatement());
        }

        return false;
    }

    @Override
    public boolean visit(MySqlUpdateStatement x) {
        visit((SQLUpdateStatement) x);
        for (SQLExpr item : x.getReturning()) {
            item.accept(this);
        }

        return false;
    }

    @Override
    public boolean visit(MySqlSetTransactionStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowHMSMetaStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowAuthorsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowBinaryLogsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowMasterLogsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowCollationStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowBinLogEventsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowCharacterSetStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowContributorsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowCreateDatabaseStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowCreateEventStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowCreateFunctionStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowCreateProcedureStatement x) {
        return false;
    }

    @Override
    public boolean visit(SQLShowCreateTableStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowCreateTriggerStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowEngineStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowEnginesStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowErrorsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowEventsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowFunctionCodeStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowFunctionStatusStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowGrantsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlUserName x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowMasterStatusStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowOpenTablesStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowPluginsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowPartitionsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowPrivilegesStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowProcedureCodeStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowProcedureStatusStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowProcessListStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowProfileStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowProfilesStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowRelayLogEventsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowRuleStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowRuleStatusStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowSlaveHostsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowSequencesStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowSlowStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowSlaveStatusStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowTableStatusStatement x) {
        return false;
    }

    @Override
    public boolean visit(MysqlShowDbLockStatement x) {
        return false;
    }

    @Override
    public boolean visit(MysqlShowHtcStatement x) {
        return false;
    }

    @Override
    public boolean visit(MysqlShowStcStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowTriggersStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowTraceStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowBroadcastsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowDdlStatusStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowDsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowTopologyStatement x) {
        return false;
    }

    @Override
    public boolean visit(SQLShowVariantsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlRenameTableStatement.Item x) {
        return false;
    }

    @Override
    public boolean visit(MySqlRenameTableStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlUseIndexHint x) {
        return false;
    }

    @Override
    public boolean visit(MySqlIgnoreIndexHint x) {
        return false;
    }

    @Override
    public boolean visit(MySqlLockTableStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlLockTableStatement.Item x) {
        return false;
    }

    @Override
    public boolean visit(MySqlUnlockTablesStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlForceIndexHint x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTableChangeColumn x) {
        SQLAlterTableStatement stmt = (SQLAlterTableStatement) x.getParent();

        SQLName table = stmt.getName();

        SQLName column = x.getColumnName();
        String columnName = column.toString();
        addColumn(table, columnName);
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTableModifyColumn x) {
        SQLAlterTableStatement stmt = (SQLAlterTableStatement) x.getParent();

        SQLName table = stmt.getName();

        SQLName column = x.getNewColumnDefinition().getName();
        String columnName = column.toString();
        addColumn(table, columnName);

        return false;
    }

    @Override
    public boolean visit(SQLAlterCharacter x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTableOption x) {
        return false;
    }

    @Override
    public boolean visit(MySqlCreateTableStatement x) {
        if (repository != null
                && x.getParent() == null) {
            repository.resolve(x);
        }

        boolean val = super.visit((SQLCreateTableStatement) x);

        SQLExpr union = x.getOption("union");
        if (union instanceof SQLListExpr) {
            for (SQLExpr item : ((SQLListExpr) union).getItems()) {
                if (item instanceof SQLName) {
                    getTableStatWithUnwrap(item);
                }
            }
        }
        if (x.getWithSelect() instanceof SQLSelectStatement) {
            visit((SQLSelectStatement) x.getWithSelect());
        }

        return val;
    }

    @Override
    public boolean visit(MySqlHelpStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlCharExpr x) {
        return false;
    }

    @Override
    public boolean visit(MySqlUnique x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTableDiscardTablespace x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTableImportTablespace x) {
        return false;
    }

    @Override
    public boolean visit(TableSpaceOption x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTableAlterColumn x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTableForce x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTableLock x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTableOrderBy x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTableValidation x) {
        return false;
    }

    @Override
    public boolean visit(MySqlCaseStatement x) {
        accept(x.getWhenList());
        return false;
    }

    @Override
    public boolean visit(MySqlSelectIntoStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlWhenStatement x) {
        accept(x.getStatements());
        return false;
    }

    @Override
    public boolean visit(MySqlLeaveStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlIterateStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlRepeatStatement x) {
        accept(x.getStatements());
        return false;
    }

    @Override
    public boolean visit(MySqlCursorDeclareStatement x) {
        accept(x.getSelect());
        return false;
    }

    @Override
    public boolean visit(MySqlUpdateTableSource x) {
        if (x.getUpdate() != null) {
            return this.visit(x.getUpdate());
        }
        return false;
    }

    @Override
    public boolean visit(MySqlSubPartitionByKey x) {
        return false;
    }

    @Override
    public boolean visit(MySqlSubPartitionByList x) {
        return false;
    }

    @Override
    public boolean visit(MySqlDeclareHandlerStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlDeclareConditionStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlFlushStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlEventSchedule x) {
        return false;
    }

    @Override
    public boolean visit(MySqlCreateEventStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlCreateAddLogFileGroupStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlCreateServerStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlCreateTableSpaceStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterEventStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterLogFileGroupStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterServerStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTablespaceStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlChecksumTableStatement x) {
        return true;
    }

    @Override
    public boolean visit(MySqlShowDatasourcesStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowNodeStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowHelpStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlFlashbackStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowConfigStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowPlanCacheStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowPhysicalProcesslistStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlRenameSequenceStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlCheckTableStatement x) {
        for (SQLExprTableSource tableSource : x.getTables()) {
            tableSource.accept(this);
        }
        return false;
    }

    @Override
    public boolean visit(MysqlCreateFullTextCharFilterStatement x) {
        return false;
    }

    @Override
    public boolean visit(MysqlShowFullTextStatement x) {
        return false;
    }

    @Override
    public boolean visit(MysqlShowCreateFullTextStatement x) {
        return false;
    }

    @Override
    public boolean visit(MysqlAlterFullTextStatement x) {
        return false;
    }

    @Override
    public boolean visit(MysqlDropFullTextStatement x) {
        return false;
    }

    @Override
    public boolean visit(MysqlCreateFullTextTokenizerStatement x) {
        return false;
    }

    @Override
    public boolean visit(MysqlCreateFullTextTokenFilterStatement x) {
        return false;
    }

    @Override
    public boolean visit(MysqlCreateFullTextAnalyzerStatement x) {
        return false;
    }

    @Override
    public boolean visit(MysqlCreateFullTextDictionaryStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlAlterTableAlterFullTextIndex x) {
        return false;
    }

    @Override
    public boolean visit(MySqlExecuteForAdsStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlManageInstanceGroupStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlRaftMemberChangeStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlRaftLeaderTransferStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlMigrateStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowClusterNameStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowJobStatusStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlShowMigrateTaskStatusStatement x) {
        return false;
    }

    @Override
    public boolean visit(MySqlSubPartitionByValue x) {
        return false;
    }

    @Override
    public boolean visit(MySqlExtPartition x) {
        return true;
    }

    @Override
    public boolean visit(MySqlExtPartition.Item x) {
        return false;
    }
}
