/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.flink.table.planner.delegation.hive.parse;

import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple4;
import org.apache.flink.table.api.Schema;
import org.apache.flink.table.api.ValidationException;
import org.apache.flink.table.calcite.bridge.CalciteContext;
import org.apache.flink.table.catalog.Catalog;
import org.apache.flink.table.catalog.CatalogBaseTable;
import org.apache.flink.table.catalog.CatalogDatabase;
import org.apache.flink.table.catalog.CatalogDatabaseImpl;
import org.apache.flink.table.catalog.CatalogFunction;
import org.apache.flink.table.catalog.CatalogFunctionImpl;
import org.apache.flink.table.catalog.CatalogPartition;
import org.apache.flink.table.catalog.CatalogPartitionImpl;
import org.apache.flink.table.catalog.CatalogPartitionSpec;
import org.apache.flink.table.catalog.CatalogRegistry;
import org.apache.flink.table.catalog.CatalogTable;
import org.apache.flink.table.catalog.CatalogView;
import org.apache.flink.table.catalog.Column;
import org.apache.flink.table.catalog.FunctionCatalog;
import org.apache.flink.table.catalog.FunctionLanguage;
import org.apache.flink.table.catalog.ObjectIdentifier;
import org.apache.flink.table.catalog.ObjectPath;
import org.apache.flink.table.catalog.ResolvedCatalogBaseTable;
import org.apache.flink.table.catalog.ResolvedCatalogTable;
import org.apache.flink.table.catalog.ResolvedCatalogView;
import org.apache.flink.table.catalog.ResolvedSchema;
import org.apache.flink.table.catalog.TableChange;
import org.apache.flink.table.catalog.UnresolvedIdentifier;
import org.apache.flink.table.catalog.exceptions.DatabaseNotExistException;
import org.apache.flink.table.catalog.hive.HiveCatalog;
import org.apache.flink.table.catalog.hive.client.HiveShim;
import org.apache.flink.table.catalog.hive.client.HiveShimLoader;
import org.apache.flink.table.catalog.hive.factories.HiveFunctionDefinitionFactory;
import org.apache.flink.table.catalog.hive.util.AlterHiveDatabaseOp;
import org.apache.flink.table.catalog.hive.util.Constants;
import org.apache.flink.table.catalog.hive.util.HiveDDLUtils;
import org.apache.flink.table.catalog.hive.util.HiveTableUtil;
import org.apache.flink.table.catalog.hive.util.HiveTypeUtil;
import org.apache.flink.table.factories.FactoryUtil;
import org.apache.flink.table.functions.FunctionDefinition;
import org.apache.flink.table.functions.hive.HiveFunctionWrapper;
import org.apache.flink.table.functions.hive.HiveGenericUDF;
import org.apache.flink.table.legacy.api.constraints.UniqueConstraint;
import org.apache.flink.table.operations.CreateTableASOperation;
import org.apache.flink.table.operations.DescribeTableOperation;
import org.apache.flink.table.operations.Operation;
import org.apache.flink.table.operations.QueryOperation;
import org.apache.flink.table.operations.ShowDatabasesOperation;
import org.apache.flink.table.operations.ShowFunctionsOperation;
import org.apache.flink.table.operations.ShowPartitionsOperation;
import org.apache.flink.table.operations.ShowTablesOperation;
import org.apache.flink.table.operations.ShowViewsOperation;
import org.apache.flink.table.operations.UseDatabaseOperation;
import org.apache.flink.table.operations.ddl.AddPartitionsOperation;
import org.apache.flink.table.operations.ddl.AlterDatabaseOperation;
import org.apache.flink.table.operations.ddl.AlterPartitionPropertiesOperation;
import org.apache.flink.table.operations.ddl.AlterTableChangeOperation;
import org.apache.flink.table.operations.ddl.AlterTableRenameOperation;
import org.apache.flink.table.operations.ddl.AlterTableSchemaOperation;
import org.apache.flink.table.operations.ddl.AlterViewAsOperation;
import org.apache.flink.table.operations.ddl.AlterViewPropertiesOperation;
import org.apache.flink.table.operations.ddl.AlterViewRenameOperation;
import org.apache.flink.table.operations.ddl.CreateCatalogFunctionOperation;
import org.apache.flink.table.operations.ddl.CreateDatabaseOperation;
import org.apache.flink.table.operations.ddl.CreateTableOperation;
import org.apache.flink.table.operations.ddl.CreateTempSystemFunctionOperation;
import org.apache.flink.table.operations.ddl.CreateViewOperation;
import org.apache.flink.table.operations.ddl.DropCatalogFunctionOperation;
import org.apache.flink.table.operations.ddl.DropDatabaseOperation;
import org.apache.flink.table.operations.ddl.DropPartitionsOperation;
import org.apache.flink.table.operations.ddl.DropTableOperation;
import org.apache.flink.table.operations.ddl.DropTempSystemFunctionOperation;
import org.apache.flink.table.operations.ddl.DropViewOperation;
import org.apache.flink.table.planner.delegation.hive.HiveParser;
import org.apache.flink.table.planner.delegation.hive.HiveParserCalcitePlanner;
import org.apache.flink.table.planner.delegation.hive.HiveParserConstants;
import org.apache.flink.table.planner.delegation.hive.HiveParserDMLHelper;
import org.apache.flink.table.planner.delegation.hive.copy.HiveASTParseUtils;
import org.apache.flink.table.planner.delegation.hive.copy.HiveParserASTNode;
import org.apache.flink.table.planner.delegation.hive.copy.HiveParserAuthorizationParseUtils;
import org.apache.flink.table.planner.delegation.hive.copy.HiveParserBaseSemanticAnalyzer;
import org.apache.flink.table.planner.delegation.hive.copy.HiveParserBaseSemanticAnalyzer.HiveParserRowFormatParams;
import org.apache.flink.table.planner.delegation.hive.copy.HiveParserContext;
import org.apache.flink.table.planner.delegation.hive.copy.HiveParserQueryState;
import org.apache.flink.table.planner.delegation.hive.copy.HiveParserRowResolver;
import org.apache.flink.table.planner.delegation.hive.copy.HiveParserSemanticAnalyzer;
import org.apache.flink.table.planner.delegation.hive.copy.HiveParserStorageFormat;
import org.apache.flink.table.planner.delegation.hive.operations.HiveExecutableOperation;
import org.apache.flink.table.planner.delegation.hive.operations.HiveShowCreateTableOperation;
import org.apache.flink.table.planner.utils.HiveCatalogUtils;
import org.apache.flink.table.planner.utils.TableSchemaUtils;
import org.apache.flink.table.resource.ResourceType;
import org.apache.flink.table.resource.ResourceUri;

import org.antlr.runtime.tree.CommonTree;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.tools.FrameworkConfig;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.hive.common.StatsSetupConst;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.metastore.TableType;
import org.apache.hadoop.hive.metastore.api.FieldSchema;
import org.apache.hadoop.hive.metastore.api.SkewedInfo;
import org.apache.hadoop.hive.ql.ErrorMsg;
import org.apache.hadoop.hive.ql.exec.ColumnInfo;
import org.apache.hadoop.hive.ql.exec.FunctionUtils;
import org.apache.hadoop.hive.ql.lib.Node;
import org.apache.hadoop.hive.ql.lib.PreOrderWalker;
import org.apache.hadoop.hive.ql.metadata.Table;
import org.apache.hadoop.hive.ql.parse.SemanticException;
import org.apache.hadoop.hive.ql.plan.ExprNodeDesc;
import org.apache.hadoop.hive.ql.plan.HiveOperation;
import org.apache.hadoop.hive.ql.plan.PrincipalDesc;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDFMacro;
import org.apache.hadoop.hive.serde.serdeConstants;
import org.apache.hadoop.hive.serde2.typeinfo.CharTypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.DecimalTypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;
import org.apache.hadoop.hive.serde2.typeinfo.VarcharTypeInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static org.apache.flink.table.catalog.hive.util.AlterTableOp.ALTER_COLUMNS;
import static org.apache.flink.table.catalog.hive.util.AlterTableOp.CHANGE_FILE_FORMAT;
import static org.apache.flink.table.catalog.hive.util.AlterTableOp.CHANGE_LOCATION;
import static org.apache.flink.table.catalog.hive.util.AlterTableOp.CHANGE_SERDE_PROPS;
import static org.apache.flink.table.catalog.hive.util.AlterTableOp.CHANGE_TBL_PROPS;
import static org.apache.flink.table.catalog.hive.util.Constants.ALTER_COL_CASCADE;
import static org.apache.flink.table.catalog.hive.util.Constants.ALTER_DATABASE_OP;
import static org.apache.flink.table.catalog.hive.util.Constants.ALTER_TABLE_OP;
import static org.apache.flink.table.catalog.hive.util.Constants.COLLECTION_DELIM;
import static org.apache.flink.table.catalog.hive.util.Constants.DATABASE_LOCATION_URI;
import static org.apache.flink.table.catalog.hive.util.Constants.DATABASE_OWNER_NAME;
import static org.apache.flink.table.catalog.hive.util.Constants.DATABASE_OWNER_TYPE;
import static org.apache.flink.table.catalog.hive.util.Constants.ESCAPE_CHAR;
import static org.apache.flink.table.catalog.hive.util.Constants.FIELD_DELIM;
import static org.apache.flink.table.catalog.hive.util.Constants.LINE_DELIM;
import static org.apache.flink.table.catalog.hive.util.Constants.MAPKEY_DELIM;
import static org.apache.flink.table.catalog.hive.util.Constants.NOT_NULL_COLS;
import static org.apache.flink.table.catalog.hive.util.Constants.NOT_NULL_CONSTRAINT_TRAITS;
import static org.apache.flink.table.catalog.hive.util.Constants.PK_CONSTRAINT_TRAIT;
import static org.apache.flink.table.catalog.hive.util.Constants.SERDE_INFO_PROP_PREFIX;
import static org.apache.flink.table.catalog.hive.util.Constants.SERDE_LIB_CLASS_NAME;
import static org.apache.flink.table.catalog.hive.util.Constants.SERIALIZATION_NULL_FORMAT;
import static org.apache.flink.table.catalog.hive.util.Constants.STORED_AS_FILE_FORMAT;
import static org.apache.flink.table.catalog.hive.util.Constants.STORED_AS_INPUT_FORMAT;
import static org.apache.flink.table.catalog.hive.util.Constants.STORED_AS_OUTPUT_FORMAT;
import static org.apache.flink.table.catalog.hive.util.Constants.TABLE_IS_EXTERNAL;
import static org.apache.flink.table.catalog.hive.util.Constants.TABLE_LOCATION_URI;
import static org.apache.flink.table.catalog.hive.util.HiveDDLUtils.COL_DELIMITER;
import static org.apache.flink.table.planner.delegation.hive.copy.HiveParserBaseSemanticAnalyzer.NotNullConstraint;
import static org.apache.flink.table.planner.delegation.hive.copy.HiveParserBaseSemanticAnalyzer.PrimaryKey;
import static org.apache.flink.table.planner.delegation.hive.copy.HiveParserBaseSemanticAnalyzer.getColumns;
import static org.apache.flink.table.planner.delegation.hive.copy.HiveParserBaseSemanticAnalyzer.stripQuotes;

/**
 * Ported hive's org.apache.hadoop.hive.ql.parse.DDLSemanticAnalyzer, and also incorporated
 * functionalities from SemanticAnalyzer and FunctionSemanticAnalyzer. It's mainly used to convert
 * {@link HiveParserASTNode} to the corresponding {@link Operation}.
 */
public class HiveParserDDLSemanticAnalyzer {
    private static final Logger LOG = LoggerFactory.getLogger(HiveParserDDLSemanticAnalyzer.class);
    private static final Map<Integer, String> TokenToTypeName = new HashMap<>();

    private final Set<String> reservedPartitionValues;
    private final HiveConf conf;
    private final HiveParserQueryState queryState;
    private final Catalog hiveCatalog;
    private final CatalogRegistry catalogRegistry;
    private final String currentDB;
    private final HiveParser hiveParser;
    private final HiveFunctionDefinitionFactory funcDefFactory;
    private final HiveShim hiveShim;
    private final HiveParserContext context;
    private final HiveParserDMLHelper dmlHelper;
    private final FrameworkConfig frameworkConfig;
    private final RelOptCluster cluster;
    private final ClassLoader classLoader;
    private final FunctionCatalog functionCatalog;

    static {
        TokenToTypeName.put(HiveASTParser.TOK_BOOLEAN, serdeConstants.BOOLEAN_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_TINYINT, serdeConstants.TINYINT_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_SMALLINT, serdeConstants.SMALLINT_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_INT, serdeConstants.INT_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_BIGINT, serdeConstants.BIGINT_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_FLOAT, serdeConstants.FLOAT_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_DOUBLE, serdeConstants.DOUBLE_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_STRING, serdeConstants.STRING_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_CHAR, serdeConstants.CHAR_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_VARCHAR, serdeConstants.VARCHAR_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_BINARY, serdeConstants.BINARY_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_DATE, serdeConstants.DATE_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_DATETIME, serdeConstants.DATETIME_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_TIMESTAMP, serdeConstants.TIMESTAMP_TYPE_NAME);
        TokenToTypeName.put(
                HiveASTParser.TOK_INTERVAL_YEAR_MONTH,
                HiveParserConstants.INTERVAL_YEAR_MONTH_TYPE_NAME);
        TokenToTypeName.put(
                HiveASTParser.TOK_INTERVAL_DAY_TIME,
                HiveParserConstants.INTERVAL_DAY_TIME_TYPE_NAME);
        TokenToTypeName.put(HiveASTParser.TOK_DECIMAL, serdeConstants.DECIMAL_TYPE_NAME);
    }

    public static String getTypeName(HiveParserASTNode node) throws SemanticException {
        int token = node.getType();
        String typeName;

        // datetime type isn't currently supported
        if (token == HiveASTParser.TOK_DATETIME) {
            throw new ValidationException(ErrorMsg.UNSUPPORTED_TYPE.getMsg());
        }

        switch (token) {
            case HiveASTParser.TOK_CHAR:
                CharTypeInfo charTypeInfo = HiveASTParseUtils.getCharTypeInfo(node);
                typeName = charTypeInfo.getQualifiedName();
                break;
            case HiveASTParser.TOK_VARCHAR:
                VarcharTypeInfo varcharTypeInfo = HiveASTParseUtils.getVarcharTypeInfo(node);
                typeName = varcharTypeInfo.getQualifiedName();
                break;
            case HiveASTParser.TOK_DECIMAL:
                DecimalTypeInfo decTypeInfo = HiveASTParseUtils.getDecimalTypeTypeInfo(node);
                typeName = decTypeInfo.getQualifiedName();
                break;
            default:
                typeName = TokenToTypeName.get(token);
        }
        return typeName;
    }

    public HiveParserDDLSemanticAnalyzer(
            HiveParserQueryState queryState,
            Catalog hiveCatalog,
            CatalogRegistry catalogRegistry,
            HiveParser hiveParser,
            HiveShim hiveShim,
            HiveParserContext context,
            HiveParserDMLHelper dmlHelper,
            FrameworkConfig frameworkConfig,
            RelOptCluster cluster,
            CalciteContext calciteContext)
            throws SemanticException {
        this.queryState = queryState;
        this.conf = queryState.getConf();
        this.hiveCatalog = hiveCatalog;
        this.currentDB = catalogRegistry.getCurrentDatabase();
        this.catalogRegistry = catalogRegistry;
        this.hiveParser = hiveParser;
        this.funcDefFactory = new HiveFunctionDefinitionFactory(hiveShim);
        this.hiveShim = hiveShim;
        this.context = context;
        this.dmlHelper = dmlHelper;
        this.frameworkConfig = frameworkConfig;
        this.cluster = cluster;
        this.classLoader = calciteContext.getClassLoader();
        this.functionCatalog = calciteContext.getFunctionCatalog();
        reservedPartitionValues = new HashSet<>();
        // Partition can't have this name
        reservedPartitionValues.add(HiveConf.getVar(conf, HiveConf.ConfVars.DEFAULTPARTITIONNAME));
        reservedPartitionValues.add(
                HiveConf.getVar(conf, HiveConf.ConfVars.DEFAULT_ZOOKEEPER_PARTITION_NAME));
        // Partition value can't end in this suffix
        reservedPartitionValues.add(
                HiveConf.getVar(conf, HiveConf.ConfVars.METASTORE_INT_ORIGINAL));
        reservedPartitionValues.add(
                HiveConf.getVar(conf, HiveConf.ConfVars.METASTORE_INT_ARCHIVED));
        reservedPartitionValues.add(
                HiveConf.getVar(conf, HiveConf.ConfVars.METASTORE_INT_EXTRACTED));
    }

    private Table getTable(ObjectPath tablePath) {
        return new Table(HiveCatalogUtils.getTable(hiveCatalog, tablePath));
    }

    public Operation convertToOperation(HiveParserASTNode ast) throws SemanticException {
        Operation res = null;
        switch (ast.getType()) {
            case HiveASTParser.TOK_ALTERTABLE:
                res = convertAlterTable(ast);
                break;
            case HiveASTParser.TOK_DROPTABLE:
                res = convertDropTable(ast, null);
                break;
            case HiveASTParser.TOK_DESCTABLE:
                res = convertDescribeTable(ast);
                break;
            case HiveASTParser.TOK_SHOWDATABASES:
                res = convertShowDatabases(catalogRegistry.getCurrentCatalog());
                break;
            case HiveASTParser.TOK_SHOWTABLES:
                res = convertShowTables(ast, false);
                break;
            case HiveASTParser.TOK_SHOWFUNCTIONS:
                res = convertShowFunctions(ast);
                break;
            case HiveASTParser.TOK_SHOWVIEWS:
                res = convertShowTables(ast, true);
                break;
            case HiveASTParser.TOK_DROPVIEW:
                res = convertDropTable(ast, TableType.VIRTUAL_VIEW);
                break;
            case HiveASTParser.TOK_ALTERVIEW:
                res = convertAlterView(ast);
                break;
            case HiveASTParser.TOK_SHOWPARTITIONS:
                res = convertShowPartitions(ast);
                break;
            case HiveASTParser.TOK_CREATEDATABASE:
                res = convertCreateDatabase(ast);
                break;
            case HiveASTParser.TOK_DROPDATABASE:
                res = convertDropDatabase(ast);
                break;
            case HiveASTParser.TOK_SWITCHDATABASE:
                res = convertSwitchDatabase(ast);
                break;
            case HiveASTParser.TOK_ALTERDATABASE_PROPERTIES:
                res = convertAlterDatabaseProperties(ast);
                break;
            case HiveASTParser.TOK_ALTERDATABASE_OWNER:
                res = convertAlterDatabaseOwner(ast);
                break;
            case HiveASTParser.TOK_ALTERDATABASE_LOCATION:
                res = convertAlterDatabaseLocation(ast);
                break;
            case HiveASTParser.TOK_CREATETABLE:
                res = convertCreateTable(ast);
                break;
            case HiveASTParser.TOK_CREATEVIEW:
                res = convertCreateView(ast);
                break;
            case HiveASTParser.TOK_CREATEFUNCTION:
                res = convertCreateFunction(ast);
                break;
            case HiveASTParser.TOK_DROPFUNCTION:
                res = convertDropFunction(ast);
                break;
            case HiveASTParser.TOK_CREATEMACRO:
                res = convertCreateMacro(ast);
                break;
            case HiveASTParser.TOK_DROPMACRO:
                res = convertDropMacro(ast);
                break;
            case HiveASTParser.TOK_SHOW_CREATETABLE:
                res = convertShowCreateTable(ast);
                break;
            case HiveASTParser.TOK_DESCFUNCTION:
            case HiveASTParser.TOK_DESCDATABASE:
            case HiveASTParser.TOK_TRUNCATETABLE:
            case HiveASTParser.TOK_CREATEINDEX:
            case HiveASTParser.TOK_DROPINDEX:
            case HiveASTParser.TOK_SHOWLOCKS:
            case HiveASTParser.TOK_SHOWDBLOCKS:
            case HiveASTParser.TOK_SHOW_COMPACTIONS:
            case HiveASTParser.TOK_SHOW_TRANSACTIONS:
            case HiveASTParser.TOK_ABORT_TRANSACTIONS:
            case HiveASTParser.TOK_MSCK:
            case HiveASTParser.TOK_ALTERINDEX_REBUILD:
            case HiveASTParser.TOK_ALTERINDEX_PROPERTIES:
            case HiveASTParser.TOK_SHOWINDEXES:
            case HiveASTParser.TOK_LOCKTABLE:
            case HiveASTParser.TOK_UNLOCKTABLE:
            case HiveASTParser.TOK_LOCKDB:
            case HiveASTParser.TOK_UNLOCKDB:
            case HiveASTParser.TOK_CREATEROLE:
            case HiveASTParser.TOK_DROPROLE:
            case HiveASTParser.TOK_SHOW_ROLE_GRANT:
            case HiveASTParser.TOK_SHOW_ROLE_PRINCIPALS:
            case HiveASTParser.TOK_SHOW_ROLES:
            case HiveASTParser.TOK_GRANT_ROLE:
            case HiveASTParser.TOK_REVOKE_ROLE:
            case HiveASTParser.TOK_GRANT:
            case HiveASTParser.TOK_SHOW_GRANT:
            case HiveASTParser.TOK_REVOKE:
            case HiveASTParser.TOK_SHOW_SET_ROLE:
            case HiveASTParser.TOK_CACHE_METADATA:
            case HiveASTParser.TOK_DROP_MATERIALIZED_VIEW:
            case HiveASTParser.TOK_SHOW_CREATEDATABASE:
            case HiveASTParser.TOK_SHOWCOLUMNS:
            case HiveASTParser.TOK_SHOW_TABLESTATUS:
            case HiveASTParser.TOK_SHOW_TBLPROPERTIES:
            case HiveASTParser.TOK_SHOWCONF:
            default:
                handleUnsupportedOperation(ast);
        }
        return res;
    }

    private Operation convertAlterTable(HiveParserASTNode input) throws SemanticException {
        Operation operation = null;
        HiveParserASTNode ast = (HiveParserASTNode) input.getChild(1);
        ObjectIdentifier tableIdentifier =
                HiveParserBaseSemanticAnalyzer.getObjectIdentifier(
                        catalogRegistry, (HiveParserASTNode) input.getChild(0));
        String[] qualified =
                new String[] {tableIdentifier.getDatabaseName(), tableIdentifier.getObjectName()};
        String tableName = HiveParserBaseSemanticAnalyzer.getDotName(qualified);
        HashMap<String, String> partSpec = null;
        HiveParserASTNode partSpecNode = (HiveParserASTNode) input.getChild(2);
        if (partSpecNode != null) {
            partSpec = getPartSpec(partSpecNode);
        }
        ResolvedCatalogBaseTable<?> alteredTable = getAlteredTable(tableName, false);
        switch (ast.getType()) {
            case HiveASTParser.TOK_ALTERTABLE_RENAME:
                operation = convertAlterTableRename(tableName, ast, false);
                break;
            case HiveASTParser.TOK_ALTERTABLE_ADDCOLS:
                operation = convertAlterTableModifyCols(alteredTable, tableName, ast, false);
                break;
            case HiveASTParser.TOK_ALTERTABLE_REPLACECOLS:
                operation = convertAlterTableModifyCols(alteredTable, tableName, ast, true);
                break;
            case HiveASTParser.TOK_ALTERTABLE_RENAMECOL:
                operation = convertAlterTableChangeCol(alteredTable, qualified, ast);
                break;
            case HiveASTParser.TOK_ALTERTABLE_ADDPARTS:
                operation = convertAlterTableAddParts(qualified, ast);
                break;
            case HiveASTParser.TOK_ALTERTABLE_DROPPARTS:
                operation = convertAlterTableDropParts(qualified, ast);
                break;
            case HiveASTParser.TOK_ALTERTABLE_PROPERTIES:
                operation =
                        convertAlterTableProps(alteredTable, tableName, null, ast, false, false);
                break;
            case HiveASTParser.TOK_ALTERTABLE_DROPPROPERTIES:
                operation = convertAlterTableProps(alteredTable, tableName, null, ast, false, true);
                break;
            case HiveASTParser.TOK_ALTERTABLE_UPDATESTATS:
                operation =
                        convertAlterTableProps(
                                alteredTable, tableName, partSpec, ast, false, false);
                break;
            case HiveASTParser.TOK_ALTERTABLE_FILEFORMAT:
                operation = convertAlterTableFileFormat(alteredTable, ast, tableName, partSpec);
                break;
            case HiveASTParser.TOK_ALTERTABLE_LOCATION:
                operation = convertAlterTableLocation(alteredTable, ast, tableName, partSpec);
                break;
            case HiveASTParser.TOK_ALTERTABLE_SERIALIZER:
                operation = convertAlterTableSerde(alteredTable, ast, tableName, partSpec);
                break;
            case HiveASTParser.TOK_ALTERTABLE_SERDEPROPERTIES:
                operation = convertAlterTableSerdeProps(alteredTable, ast, tableName, partSpec);
                break;
            case HiveASTParser.TOK_ALTERTABLE_TOUCH:
            case HiveASTParser.TOK_ALTERTABLE_ARCHIVE:
            case HiveASTParser.TOK_ALTERTABLE_UNARCHIVE:
            case HiveASTParser.TOK_ALTERTABLE_PARTCOLTYPE:
            case HiveASTParser.TOK_ALTERTABLE_SKEWED:
            case HiveASTParser.TOK_ALTERTABLE_EXCHANGEPARTITION:
            case HiveASTParser.TOK_ALTERTABLE_MERGEFILES:
            case HiveASTParser.TOK_ALTERTABLE_RENAMEPART:
            case HiveASTParser.TOK_ALTERTABLE_SKEWED_LOCATION:
            case HiveASTParser.TOK_ALTERTABLE_BUCKETS:
            case HiveASTParser.TOK_ALTERTABLE_CLUSTER_SORT:
            case HiveASTParser.TOK_ALTERTABLE_COMPACT:
            case HiveASTParser.TOK_ALTERTABLE_UPDATECOLSTATS:
            case HiveASTParser.TOK_ALTERTABLE_DROPCONSTRAINT:
            case HiveASTParser.TOK_ALTERTABLE_ADDCONSTRAINT:
                handleUnsupportedOperation(ast);
                break;
            default:
                throw new ValidationException("Unknown AST node for ALTER TABLE: " + ast);
        }
        return operation;
    }

    private Operation convertDropFunction(HiveParserASTNode ast) {
        // ^(TOK_DROPFUNCTION identifier ifExists? $temp?)
        String functionName = ast.getChild(0).getText();
        boolean ifExists = (ast.getFirstChildWithType(HiveASTParser.TOK_IFEXISTS) != null);

        boolean isTemporaryFunction =
                (ast.getFirstChildWithType(HiveASTParser.TOK_TEMPORARY) != null);
        if (isTemporaryFunction) {
            return new DropTempSystemFunctionOperation(functionName, ifExists);
        } else {
            ObjectIdentifier identifier = parseObjectIdentifier(functionName);
            return new DropCatalogFunctionOperation(identifier, ifExists, false);
        }
    }

    private Operation convertCreateFunction(HiveParserASTNode ast) throws SemanticException {
        // ^(TOK_CREATEFUNCTION identifier StringLiteral ({isTempFunction}? => TOK_TEMPORARY))
        String functionName = ast.getChild(0).getText().toLowerCase();
        boolean isTemporaryFunction =
                (ast.getFirstChildWithType(HiveASTParser.TOK_TEMPORARY) != null);
        String className =
                HiveParserBaseSemanticAnalyzer.unescapeSQLString(ast.getChild(1).getText());

        // Temp functions are not allowed to have qualified names.
        if (isTemporaryFunction && FunctionUtils.isQualifiedFunctionName(functionName)) {
            // hive's temporary function is more like flink's temp system function, e.g. doesn't
            // belong to a catalog/db
            throw new ValidationException(
                    "Temporary function cannot be created with a qualified name.");
        }

        // find any referenced resources
        List<ResourceUri> resources = getResourceList(ast);

        if (isTemporaryFunction) {
            functionCatalog.registerFunctionJarResources(functionName, resources);
            FunctionDefinition funcDefinition =
                    funcDefFactory.createFunctionDefinition(
                            functionName,
                            new CatalogFunctionImpl(className, FunctionLanguage.JAVA, resources),
                            () -> classLoader);
            return new CreateTempSystemFunctionOperation(functionName, false, funcDefinition);
        } else {
            ObjectIdentifier identifier = parseObjectIdentifier(functionName);
            CatalogFunction catalogFunction =
                    new CatalogFunctionImpl(className, FunctionLanguage.JAVA, resources);
            return new CreateCatalogFunctionOperation(identifier, catalogFunction, false, false);
        }
    }

    private List<ResourceUri> getResourceList(HiveParserASTNode ast) throws SemanticException {
        List<ResourceUri> resources = new ArrayList<>();
        HiveParserASTNode resourcesNode =
                (HiveParserASTNode) ast.getFirstChildWithType(HiveASTParser.TOK_RESOURCE_LIST);
        if (resourcesNode != null) {
            for (int idx = 0; idx < resourcesNode.getChildCount(); idx++) {
                // ^(TOK_RESOURCE_URI $resType $resPath)
                HiveParserASTNode resNode = (HiveParserASTNode) resourcesNode.getChild(idx);
                if (resNode.getToken().getType() != HiveASTParser.TOK_RESOURCE_URI) {
                    throw new SemanticException(
                            "Expected token type TOK_RESOURCE_URI but found " + resNode.getToken());
                }
                if (resNode.getChildCount() != 2) {
                    throw new SemanticException(
                            "Expected 2 child nodes of TOK_RESOURCE_URI but found "
                                    + resNode.getChildCount());
                }
                HiveParserASTNode resTypeNode = (HiveParserASTNode) resNode.getChild(0);
                HiveParserASTNode resUriNode = (HiveParserASTNode) resNode.getChild(1);
                ResourceType resourceType = getResourceType(resTypeNode);
                resources.add(new ResourceUri(resourceType, stripQuotes(resUriNode.getText())));
            }
        }

        return resources;
    }

    private ResourceType getResourceType(HiveParserASTNode token) throws SemanticException {
        switch (token.getType()) {
            case HiveASTParser.TOK_JAR:
                return ResourceType.JAR;
            case HiveASTParser.TOK_FILE:
                return ResourceType.FILE;
            case HiveASTParser.TOK_ARCHIVE:
                return ResourceType.ARCHIVE;
            default:
                throw new SemanticException("Unexpected token " + token);
        }
    }

    private Operation convertCreateMacro(HiveParserASTNode ast) throws SemanticException {
        String macroName = ast.getChild(0).getText();
        if (FunctionUtils.isQualifiedFunctionName(macroName)) {
            throw new SemanticException(
                    String.format(
                            "CREATE TEMPORARY MACRO doesn't allow \".\" character in the macro name, but the name is \"%s\".",
                            macroName));
        }

        // macro use table's columns as argument, so get the corresponding column
        List<FieldSchema> arguments = getColumns((HiveParserASTNode) ast.getChild(1), true);
        Set<String> actualColumnNames = getActualColumnNames(ast, arguments);

        HiveParserRowResolver rowResolver = new HiveParserRowResolver();
        Tuple2<List<String>, List<TypeInfo>> macroColumnNameAndType =
                getMacroColumnData(arguments, actualColumnNames, rowResolver);
        ExprNodeDesc body = getBody(ast, arguments, rowResolver);

        GenericUDFMacro macro =
                new GenericUDFMacro(
                        macroName, body, macroColumnNameAndType.f0, macroColumnNameAndType.f1);

        FunctionDefinition macroDefinition =
                new HiveGenericUDF(
                        new HiveFunctionWrapper<>(GenericUDFMacro.class, macro), hiveShim);
        // hive's marco is more like flink's temp system function
        return new CreateTempSystemFunctionOperation(macroName, false, macroDefinition);
    }

    private Set<String> getActualColumnNames(HiveParserASTNode ast, List<FieldSchema> arguments)
            throws SemanticException {
        final Set<String> actualColumnNames = new HashSet<>();

        if (!arguments.isEmpty()) {
            // Walk down expression to see which arguments are actually used.
            Node expression = (Node) ast.getChild(2);

            PreOrderWalker walker =
                    new PreOrderWalker(
                            (nd, stack, nodeOutputs) -> {
                                if (nd instanceof HiveParserASTNode) {
                                    HiveParserASTNode node = (HiveParserASTNode) nd;
                                    if (node.getType() == HiveASTParser.TOK_TABLE_OR_COL) {
                                        actualColumnNames.add(node.getChild(0).getText());
                                    }
                                }
                                return null;
                            });
            walker.startWalking(Collections.singleton(expression), null);
        }
        return actualColumnNames;
    }

    private Tuple2<List<String>, List<TypeInfo>> getMacroColumnData(
            List<FieldSchema> arguments,
            Set<String> actualColumnNames,
            HiveParserRowResolver rowResolver)
            throws SemanticException {
        List<String> macroColumnNames = new ArrayList<>();
        List<TypeInfo> macroColumnTypes = new ArrayList<>();
        for (FieldSchema argument : arguments) {
            TypeInfo columnType = TypeInfoUtils.getTypeInfoFromTypeString(argument.getType());
            rowResolver.put(
                    StringUtils.EMPTY,
                    argument.getName(),
                    new ColumnInfo(argument.getName(), columnType, StringUtils.EMPTY, false));
            macroColumnNames.add(argument.getName());
            macroColumnTypes.add(columnType);
        }
        Set<String> expectedColumnNames = new LinkedHashSet<>(macroColumnNames);
        if (!expectedColumnNames.equals(actualColumnNames)) {
            throw new SemanticException(
                    String.format(
                            "Expected columns [%s], but found [%s].",
                            expectedColumnNames, actualColumnNames));
        }
        if (expectedColumnNames.size() != macroColumnNames.size()) {
            throw new SemanticException(
                    "At least one parameter name was used more than once " + macroColumnNames);
        }
        return Tuple2.of(macroColumnNames, macroColumnTypes);
    }

    private ExprNodeDesc getBody(
            HiveParserASTNode ast, List<FieldSchema> arguments, HiveParserRowResolver rowResolver)
            throws SemanticException {
        HiveParserSemanticAnalyzer semanticAnalyzer =
                new HiveParserSemanticAnalyzer(
                        queryState, frameworkConfig, cluster, catalogRegistry);
        return arguments.isEmpty()
                ? semanticAnalyzer.genExprNodeDesc((HiveParserASTNode) ast.getChild(1), rowResolver)
                : semanticAnalyzer.genExprNodeDesc(
                        (HiveParserASTNode) ast.getChild(2), rowResolver);
    }

    private Operation convertDropMacro(HiveParserASTNode ast) throws SemanticException {
        String macroName = ast.getChild(0).getText();
        if (FunctionUtils.isQualifiedFunctionName(macroName)) {
            throw new SemanticException(
                    String.format(
                            "DROP TEMPORARY MACRO doesn't allow \".\" character in the macro name, but the name is \"%s\".",
                            macroName));
        }

        boolean ifExists = (ast.getFirstChildWithType(HiveASTParser.TOK_IFEXISTS) != null);
        // macro is always temporary function
        return new DropTempSystemFunctionOperation(macroName, ifExists);
    }

    private Operation convertShowCreateTable(HiveParserASTNode ast) throws SemanticException {
        ObjectIdentifier identifier =
                HiveParserBaseSemanticAnalyzer.getObjectIdentifier(
                        catalogRegistry, (HiveParserASTNode) ast.getChild(0));
        String[] qualTabName =
                new String[] {identifier.getDatabaseName(), identifier.getObjectName()};
        ObjectPath tablePath = new ObjectPath(qualTabName[0], qualTabName[1]);
        if (!isHive310OrLater()) {
            // before hive3, Hive will check the table type is index table or not
            Table table = getTable(tablePath);
            if (table.getTableType().name().equals("INDEX_TABLE")) {
                throw new SemanticException(
                        String.format(
                                "SHOW CREATE TABLE does not support tables of type INDEX_TABLE.. %s has has table type INDEX_TABLE.",
                                tablePath));
            }
        }
        return new HiveExecutableOperation(new HiveShowCreateTableOperation(tablePath));
    }

    private boolean isHive310OrLater() {
        return HiveShimLoader.getHiveVersion().compareTo(HiveShimLoader.HIVE_VERSION_V3_1_0) >= 0;
    }

    private Operation convertAlterView(HiveParserASTNode ast) throws SemanticException {
        Operation operation = null;
        ObjectIdentifier tableIdentifier =
                HiveParserBaseSemanticAnalyzer.getObjectIdentifier(
                        catalogRegistry, (HiveParserASTNode) ast.getChild(0));
        String[] qualified =
                new String[] {tableIdentifier.getDatabaseName(), tableIdentifier.getObjectName()};
        String tableName = HiveParserBaseSemanticAnalyzer.getDotName(qualified);
        ResolvedCatalogBaseTable<?> alteredTable = getAlteredTable(tableName, true);
        if (ast.getChild(1).getType() == HiveASTParser.TOK_QUERY) {
            // alter view as
            operation = convertCreateView(ast);
        } else {
            ast = (HiveParserASTNode) ast.getChild(1);
            switch (ast.getType()) {
                case HiveASTParser.TOK_ALTERVIEW_PROPERTIES:
                    operation =
                            convertAlterTableProps(alteredTable, tableName, null, ast, true, false);
                    break;
                case HiveASTParser.TOK_ALTERVIEW_DROPPROPERTIES:
                    operation =
                            convertAlterTableProps(alteredTable, tableName, null, ast, true, true);
                    break;
                case HiveASTParser.TOK_ALTERVIEW_RENAME:
                    operation = convertAlterTableRename(tableName, ast, true);
                    break;
                case HiveASTParser.TOK_ALTERVIEW_ADDPARTS:
                case HiveASTParser.TOK_ALTERVIEW_DROPPARTS:
                    handleUnsupportedOperation("ADD/DROP PARTITION for view is not supported");
                    break;
                default:
                    throw new ValidationException("Unknown AST node for ALTER VIEW: " + ast);
            }
        }
        return operation;
    }

    private Operation convertCreateView(HiveParserASTNode ast) throws SemanticException {
        ObjectIdentifier tableIdentifier =
                HiveParserBaseSemanticAnalyzer.getObjectIdentifier(
                        catalogRegistry, (HiveParserASTNode) ast.getChild(0));
        String[] qualTabName =
                new String[] {tableIdentifier.getDatabaseName(), tableIdentifier.getObjectName()};
        String dbDotTable = HiveParserBaseSemanticAnalyzer.getDotName(qualTabName);
        List<FieldSchema> cols = null;
        boolean ifNotExists = false;
        boolean isAlterViewAs = false;
        String comment = null;
        HiveParserASTNode selectStmt = null;
        Map<String, String> tblProps = null;
        boolean isMaterialized =
                ast.getToken().getType() == HiveASTParser.TOK_CREATE_MATERIALIZED_VIEW;
        if (isMaterialized) {
            handleUnsupportedOperation("MATERIALIZED VIEW is not supported");
        }
        HiveParserStorageFormat storageFormat = new HiveParserStorageFormat(conf);

        LOG.info("Creating view " + dbDotTable + " position=" + ast.getCharPositionInLine());
        int numCh = ast.getChildCount();
        for (int num = 1; num < numCh; num++) {
            HiveParserASTNode child = (HiveParserASTNode) ast.getChild(num);
            if (storageFormat.fillStorageFormat(child)) {
                handleUnsupportedOperation("FILE FORMAT for view is not supported");
            }
            switch (child.getToken().getType()) {
                case HiveASTParser.TOK_IFNOTEXISTS:
                    ifNotExists = true;
                    break;
                case HiveASTParser.TOK_REWRITE_ENABLED:
                    handleUnsupportedOperation("MATERIALIZED VIEW REWRITE is not supported");
                    break;
                case HiveASTParser.TOK_ORREPLACE:
                    handleUnsupportedOperation("CREATE OR REPLACE VIEW is not supported");
                    break;
                case HiveASTParser.TOK_QUERY:
                    selectStmt = child;
                    break;
                case HiveASTParser.TOK_TABCOLNAME:
                    cols = HiveParserBaseSemanticAnalyzer.getColumns(child);
                    break;
                case HiveASTParser.TOK_TABLECOMMENT:
                    comment =
                            HiveParserBaseSemanticAnalyzer.unescapeSQLString(
                                    child.getChild(0).getText());
                    break;
                case HiveASTParser.TOK_TABLEPROPERTIES:
                    tblProps = getProps((HiveParserASTNode) child.getChild(0));
                    break;
                case HiveASTParser.TOK_TABLEROWFORMAT:
                    handleUnsupportedOperation("ROW FORMAT for view is not supported");
                    break;
                case HiveASTParser.TOK_TABLESERIALIZER:
                    handleUnsupportedOperation("SERDE for view is not supported");
                    break;
                case HiveASTParser.TOK_TABLELOCATION:
                    handleUnsupportedOperation("LOCATION for view is not supported");
                    break;
                case HiveASTParser.TOK_VIEWPARTCOLS:
                    handleUnsupportedOperation("PARTITION COLUMN for view is not supported");
                    break;
                default:
                    throw new ValidationException(
                            "Unknown AST node for CREATE/ALTER VIEW: " + child);
            }
        }

        if (ast.getToken().getType() == HiveASTParser.TOK_ALTERVIEW
                && ast.getChild(1).getType() == HiveASTParser.TOK_QUERY) {
            isAlterViewAs = true;
        }

        queryState.setCommandType(HiveOperation.CREATEVIEW);

        HiveParserCreateViewInfo createViewInfo =
                new HiveParserCreateViewInfo(dbDotTable, cols, selectStmt);
        hiveParser.analyzeCreateView(createViewInfo, context, queryState);

        ObjectIdentifier viewIdentifier = parseObjectIdentifier(createViewInfo.getCompoundName());
        Schema schema =
                HiveTableUtil.createSchema(
                        createViewInfo.getSchema(),
                        Collections.emptyList(),
                        Collections.emptySet(),
                        null);
        Map<String, String> props = new HashMap<>();
        if (isAlterViewAs) {
            ResolvedCatalogBaseTable<?> baseTable = getResolvedCatalogBaseTable(viewIdentifier);
            props.putAll(baseTable.getOptions());
            comment = baseTable.getComment();
        } else {
            if (tblProps != null) {
                props.putAll(tblProps);
            }
        }
        CatalogView catalogView =
                CatalogView.of(
                        schema,
                        comment,
                        createViewInfo.getOriginalText(),
                        createViewInfo.getExpandedText(),
                        props);
        if (isAlterViewAs) {
            return new AlterViewAsOperation(viewIdentifier, catalogView);
        } else {
            return new CreateViewOperation(viewIdentifier, catalogView, ifNotExists, false);
        }
    }

    private Operation convertCreateTable(HiveParserASTNode ast) throws SemanticException {
        ObjectIdentifier tableIdentifier =
                HiveParserBaseSemanticAnalyzer.getObjectIdentifier(
                        catalogRegistry, (HiveParserASTNode) ast.getChild(0));
        String[] qualifiedTabName =
                new String[] {tableIdentifier.getDatabaseName(), tableIdentifier.getObjectName()};
        String dbDotTab = HiveParserBaseSemanticAnalyzer.getDotName(qualifiedTabName);

        String likeTableName;
        List<FieldSchema> cols = new ArrayList<>();
        List<FieldSchema> partCols = new ArrayList<>();
        List<PrimaryKey> primaryKeys = new ArrayList<>();
        List<NotNullConstraint> notNulls = new ArrayList<>();
        String comment = null;
        String location = null;
        Map<String, String> tblProps = null;
        boolean ifNotExists = false;
        boolean isExt = false;
        boolean isTemporary = false;
        HiveParserASTNode selectStmt = null;
        final int createTable = 0; // regular CREATE TABLE
        final int ctlt = 1; // CREATE TABLE LIKE ... (CTLT)
        final int ctas = 2; // CREATE TABLE AS SELECT ... (CTAS)
        int commandType = createTable;

        HiveParserBaseSemanticAnalyzer.HiveParserRowFormatParams rowFormatParams =
                new HiveParserBaseSemanticAnalyzer.HiveParserRowFormatParams();
        HiveParserStorageFormat storageFormat = new HiveParserStorageFormat(conf);

        LOG.info("Creating table " + dbDotTab + " position=" + ast.getCharPositionInLine());
        int numCh = ast.getChildCount();

        // Check the 1st-level children and do simple semantic checks: 1) CTLT and CTAS should not
        // coexists.
        // 2) CTLT or CTAS should not coexists with column list (target table schema).
        // 3) CTAS does not support partitioning (for now).
        for (int num = 1; num < numCh; num++) {
            HiveParserASTNode child = (HiveParserASTNode) ast.getChild(num);
            if (storageFormat.fillStorageFormat(child)) {
                continue;
            }
            switch (child.getToken().getType()) {
                case HiveASTParser.TOK_IFNOTEXISTS:
                    ifNotExists = true;
                    break;
                case HiveASTParser.KW_EXTERNAL:
                    isExt = true;
                    break;
                case HiveASTParser.KW_TEMPORARY:
                    isTemporary = true;
                    break;
                case HiveASTParser.TOK_LIKETABLE:
                    if (child.getChildCount() > 0) {
                        likeTableName =
                                HiveParserBaseSemanticAnalyzer.getUnescapedName(
                                        (HiveParserASTNode) child.getChild(0));
                        if (likeTableName != null) {
                            if (commandType == ctas) {
                                throw new ValidationException(
                                        ErrorMsg.CTAS_CTLT_COEXISTENCE.getMsg());
                            }
                            if (cols.size() != 0) {
                                throw new ValidationException(
                                        ErrorMsg.CTLT_COLLST_COEXISTENCE.getMsg());
                            }
                        }
                        commandType = ctlt;
                        handleUnsupportedOperation("CREATE TABLE LIKE is not supported");
                    }
                    break;

                case HiveASTParser.TOK_QUERY: // CTAS
                    if (commandType == ctlt) {
                        throw new ValidationException(ErrorMsg.CTAS_CTLT_COEXISTENCE.getMsg());
                    }
                    if (cols.size() != 0) {
                        throw new ValidationException(ErrorMsg.CTAS_COLLST_COEXISTENCE.getMsg());
                    }
                    if (partCols.size() != 0) {
                        throw new ValidationException(ErrorMsg.CTAS_PARCOL_COEXISTENCE.getMsg());
                    }
                    if (isExt) {
                        throw new ValidationException(ErrorMsg.CTAS_EXTTBL_COEXISTENCE.getMsg());
                    }
                    commandType = ctas;
                    selectStmt = child;
                    break;
                case HiveASTParser.TOK_TABCOLLIST:
                    cols =
                            HiveParserBaseSemanticAnalyzer.getColumns(
                                    child, true, primaryKeys, notNulls);
                    break;
                case HiveASTParser.TOK_TABLECOMMENT:
                    comment =
                            HiveParserBaseSemanticAnalyzer.unescapeSQLString(
                                    child.getChild(0).getText());
                    break;
                case HiveASTParser.TOK_TABLEPARTCOLS:
                    partCols =
                            HiveParserBaseSemanticAnalyzer.getColumns(
                                    (HiveParserASTNode) child.getChild(0), false);
                    break;
                case HiveASTParser.TOK_TABLEROWFORMAT:
                    rowFormatParams.analyzeRowFormat(child);
                    break;
                case HiveASTParser.TOK_TABLELOCATION:
                    location =
                            HiveParserBaseSemanticAnalyzer.unescapeSQLString(
                                    child.getChild(0).getText());
                    break;
                case HiveASTParser.TOK_TABLEPROPERTIES:
                    tblProps = getProps((HiveParserASTNode) child.getChild(0));
                    break;
                case HiveASTParser.TOK_TABLESERIALIZER:
                    child = (HiveParserASTNode) child.getChild(0);
                    storageFormat.setSerde(
                            HiveParserBaseSemanticAnalyzer.unescapeSQLString(
                                    child.getChild(0).getText()));
                    if (child.getChildCount() == 2) {
                        HiveParserBaseSemanticAnalyzer.readProps(
                                (HiveParserASTNode) (child.getChild(1).getChild(0)),
                                storageFormat.getSerdeProps());
                    }
                    break;
                case HiveASTParser.TOK_ALTERTABLE_BUCKETS:
                    handleUnsupportedOperation("Bucketed table is not supported");
                    break;
                case HiveASTParser.TOK_TABLESKEWED:
                    handleUnsupportedOperation("Skewed table is not supported");
                    break;
                default:
                    throw new ValidationException("Unknown AST node for CREATE TABLE: " + child);
            }
        }

        if (storageFormat.getStorageHandler() != null) {
            handleUnsupportedOperation("Storage handler table is not supported");
        }

        if (commandType == createTable || commandType == ctlt) {
            queryState.setCommandType(HiveOperation.CREATETABLE);
        } else {
            queryState.setCommandType(HiveOperation.CREATETABLE_AS_SELECT);
        }

        storageFormat.fillDefaultStorageFormat(isExt, false);

        if (isTemporary) {
            if (partCols.size() > 0) {
                handleUnsupportedOperation(
                        "Partition columns are not supported on temporary tables");
            }
            handleUnsupportedOperation("Temporary hive table is not supported");
        }

        // Handle different types of CREATE TABLE command
        switch (commandType) {
            case createTable: // REGULAR CREATE TABLE DDL
                tblProps = addDefaultProperties(tblProps);
                return convertCreateTable(
                        dbDotTab,
                        isExt,
                        ifNotExists,
                        isTemporary,
                        cols,
                        partCols,
                        comment,
                        location,
                        tblProps,
                        rowFormatParams,
                        storageFormat,
                        primaryKeys,
                        notNulls);

            case ctlt: // create table like <tbl_name>
                tblProps = addDefaultProperties(tblProps);
                throw new SemanticException("CREATE TABLE LIKE is not supported yet");

            case ctas: // create table as select
                tblProps = addDefaultProperties(tblProps);

                // analyze the query
                HiveParserCalcitePlanner calcitePlanner =
                        hiveParser.createCalcitePlanner(context, queryState);
                calcitePlanner.setCtasCols(cols);
                RelNode queryRelNode = calcitePlanner.genLogicalPlan(selectStmt);
                ResolvedSchema schema =
                        HiveTableUtil.createResolvedSchema(
                                cols, partCols, Collections.emptySet(), null);
                ResolvedCatalogTable destTable =
                        new ResolvedCatalogTable(
                                CatalogTable.of(
                                        Schema.newBuilder().fromResolvedSchema(schema).build(),
                                        comment,
                                        HiveCatalog.getFieldNames(partCols),
                                        tblProps),
                                schema);

                Tuple4<ObjectIdentifier, QueryOperation, Map<String, String>, Boolean>
                        insertOperationInfo =
                                dmlHelper.createInsertOperationInfo(
                                        queryRelNode,
                                        destTable,
                                        tableIdentifier,
                                        Collections.emptyMap(),
                                        Collections.emptyList(),
                                        false);

                CreateTableOperation createTableOperation =
                        convertCreateTable(
                                dbDotTab,
                                isExt,
                                ifNotExists,
                                isTemporary,
                                cols,
                                partCols,
                                comment,
                                location,
                                tblProps,
                                rowFormatParams,
                                storageFormat,
                                primaryKeys,
                                notNulls);

                return new CreateTableASOperation(
                        createTableOperation,
                        insertOperationInfo.f2,
                        insertOperationInfo.f1,
                        insertOperationInfo.f3);
            default:
                throw new ValidationException("Unrecognized command.");
        }
    }

    private CreateTableOperation convertCreateTable(
            String compoundName,
            boolean isExternal,
            boolean ifNotExists,
            boolean isTemporary,
            List<FieldSchema> cols,
            List<FieldSchema> partCols,
            String comment,
            String location,
            Map<String, String> tblProps,
            HiveParserRowFormatParams rowFormatParams,
            HiveParserStorageFormat storageFormat,
            List<PrimaryKey> primaryKeys,
            List<NotNullConstraint> notNullConstraints) {
        Map<String, String> props = new HashMap<>();
        if (tblProps != null) {
            props.putAll(tblProps);
        }
        markHiveConnector(props);
        // external
        if (isExternal) {
            props.put(TABLE_IS_EXTERNAL, "true");
        }
        // PK trait
        UniqueConstraint uniqueConstraint = null;
        if (primaryKeys != null && !primaryKeys.isEmpty()) {
            PrimaryKey primaryKey = primaryKeys.get(0);
            byte trait = 0;
            if (primaryKey.isEnable()) {
                trait = HiveDDLUtils.enableConstraint(trait);
            }
            if (primaryKey.isValidate()) {
                trait = HiveDDLUtils.validateConstraint(trait);
            }
            if (primaryKey.isRely()) {
                trait = HiveDDLUtils.relyConstraint(trait);
            }
            props.put(PK_CONSTRAINT_TRAIT, String.valueOf(trait));
            List<String> pkCols =
                    primaryKeys.stream().map(PrimaryKey::getPk).collect(Collectors.toList());
            String constraintName = primaryKey.getConstraintName();
            if (constraintName == null) {
                constraintName = pkCols.stream().collect(Collectors.joining("_", "PK_", ""));
            }
            uniqueConstraint = UniqueConstraint.primaryKey(constraintName, pkCols);
        }
        // NOT NULL constraints
        List<String> notNullCols = new ArrayList<>();
        if (!notNullConstraints.isEmpty()) {
            List<String> traits = new ArrayList<>();
            for (NotNullConstraint notNull : notNullConstraints) {
                byte trait = 0;
                if (notNull.isEnable()) {
                    trait = HiveDDLUtils.enableConstraint(trait);
                }
                if (notNull.isValidate()) {
                    trait = HiveDDLUtils.validateConstraint(trait);
                }
                if (notNull.isRely()) {
                    trait = HiveDDLUtils.relyConstraint(trait);
                }
                traits.add(String.valueOf(trait));
                notNullCols.add(notNull.getColName());
            }
            props.put(NOT_NULL_CONSTRAINT_TRAITS, String.join(COL_DELIMITER, traits));
            props.put(NOT_NULL_COLS, String.join(COL_DELIMITER, notNullCols));
        }
        // row format
        if (rowFormatParams != null) {
            encodeRowFormat(rowFormatParams, props);
        }
        // storage format
        if (storageFormat != null) {
            encodeStorageFormat(storageFormat, props);
        }
        // location
        if (location != null) {
            props.put(TABLE_LOCATION_URI, location);
        }
        ObjectIdentifier identifier = parseObjectIdentifier(compoundName);
        Set<String> notNullColSet = new HashSet<>(notNullCols);
        if (uniqueConstraint != null) {
            notNullColSet.addAll(uniqueConstraint.getColumns());
        }
        Schema schema = HiveTableUtil.createSchema(cols, partCols, notNullColSet, uniqueConstraint);
        return new CreateTableOperation(
                identifier,
                CatalogTable.of(schema, comment, HiveCatalog.getFieldNames(partCols), props),
                ifNotExists,
                isTemporary);
    }

    private void markHiveConnector(Map<String, String> props) {
        props.put(FactoryUtil.CONNECTOR.key(), Constants.IDENTIFIER);
    }

    public static void encodeRowFormat(
            HiveParserRowFormatParams rowFormatParams, Map<String, String> props) {
        if (rowFormatParams.getFieldDelim() != null) {
            props.put(FIELD_DELIM, rowFormatParams.getFieldDelim());
        }
        if (rowFormatParams.getCollItemDelim() != null) {
            props.put(COLLECTION_DELIM, rowFormatParams.getCollItemDelim());
        }
        if (rowFormatParams.getMapKeyDelim() != null) {
            props.put(MAPKEY_DELIM, rowFormatParams.getMapKeyDelim());
        }
        if (rowFormatParams.getFieldEscape() != null) {
            props.put(ESCAPE_CHAR, rowFormatParams.getFieldEscape());
        }
        if (rowFormatParams.getLineDelim() != null) {
            props.put(LINE_DELIM, rowFormatParams.getLineDelim());
        }
        if (rowFormatParams.getNullFormat() != null) {
            props.put(SERIALIZATION_NULL_FORMAT, rowFormatParams.getNullFormat());
        }
    }

    public static void encodeStorageFormat(
            HiveParserStorageFormat storageFormat, Map<String, String> props) {
        String serdeName = storageFormat.getSerde();
        if (serdeName != null) {
            props.put(SERDE_LIB_CLASS_NAME, serdeName);
        }
        Map<String, String> serdeProps = storageFormat.getSerdeProps();
        if (serdeProps != null) {
            for (String serdeKey : serdeProps.keySet()) {
                props.put(SERDE_INFO_PROP_PREFIX + serdeKey, serdeProps.get(serdeKey));
            }
        }
        if (storageFormat.getInputFormat() != null) {
            props.put(STORED_AS_INPUT_FORMAT, storageFormat.getInputFormat());
        }
        if (storageFormat.getOutputFormat() != null) {
            props.put(STORED_AS_OUTPUT_FORMAT, storageFormat.getOutputFormat());
        }
    }

    private Operation convertAlterDatabaseProperties(HiveParserASTNode ast) {
        String dbName =
                HiveParserBaseSemanticAnalyzer.unescapeIdentifier(ast.getChild(0).getText());
        Map<String, String> dbProps = null;

        for (int i = 1; i < ast.getChildCount(); i++) {
            HiveParserASTNode childNode = (HiveParserASTNode) ast.getChild(i);
            if (childNode.getToken().getType() == HiveASTParser.TOK_DATABASEPROPERTIES) {
                dbProps = getProps((HiveParserASTNode) childNode.getChild(0));
            } else {
                throw new ValidationException(
                        "Unknown AST node for ALTER DATABASE PROPERTIES: " + childNode);
            }
        }
        CatalogDatabase originDB = getDatabase(dbName);
        Map<String, String> props = new HashMap<>(originDB.getProperties());
        props.put(ALTER_DATABASE_OP, AlterHiveDatabaseOp.CHANGE_PROPS.name());
        props.putAll(dbProps);
        CatalogDatabase newDB = new CatalogDatabaseImpl(props, originDB.getComment());
        return new AlterDatabaseOperation(catalogRegistry.getCurrentCatalog(), dbName, newDB);
    }

    private Operation convertAlterDatabaseOwner(HiveParserASTNode ast) throws SemanticException {
        String dbName =
                HiveParserBaseSemanticAnalyzer.getUnescapedName(
                        (HiveParserASTNode) ast.getChild(0));
        PrincipalDesc principalDesc =
                HiveParserAuthorizationParseUtils.getPrincipalDesc(
                        (HiveParserASTNode) ast.getChild(1));

        // The syntax should not allow these fields to be null, but lets verify
        String nullCmdMsg = "can't be null in alter database set owner command";
        if (principalDesc.getName() == null) {
            throw new ValidationException("Owner name " + nullCmdMsg);
        }
        if (principalDesc.getType() == null) {
            throw new ValidationException("Owner type " + nullCmdMsg);
        }
        CatalogDatabase originDB = getDatabase(dbName);
        Map<String, String> props = new HashMap<>(originDB.getProperties());
        props.put(ALTER_DATABASE_OP, AlterHiveDatabaseOp.CHANGE_OWNER.name());
        props.put(DATABASE_OWNER_NAME, principalDesc.getName());
        props.put(DATABASE_OWNER_TYPE, principalDesc.getType().name().toLowerCase());
        CatalogDatabase newDB = new CatalogDatabaseImpl(props, originDB.getComment());
        return new AlterDatabaseOperation(catalogRegistry.getCurrentCatalog(), dbName, newDB);
    }

    private Operation convertAlterDatabaseLocation(HiveParserASTNode ast) throws SemanticException {
        String dbName =
                HiveParserBaseSemanticAnalyzer.getUnescapedName(
                        (HiveParserASTNode) ast.getChild(0));
        String newLocation =
                HiveParserBaseSemanticAnalyzer.unescapeSQLString(ast.getChild(1).getText());
        CatalogDatabase originDB = getDatabase(dbName);
        Map<String, String> props = new HashMap<>(originDB.getProperties());
        props.put(ALTER_DATABASE_OP, CHANGE_LOCATION.name());
        props.put(DATABASE_LOCATION_URI, newLocation);
        CatalogDatabase newDB = new CatalogDatabaseImpl(props, originDB.getComment());
        return new AlterDatabaseOperation(catalogRegistry.getCurrentCatalog(), dbName, newDB);
    }

    private Operation convertCreateDatabase(HiveParserASTNode ast) {
        String dbName =
                HiveParserBaseSemanticAnalyzer.unescapeIdentifier(ast.getChild(0).getText());
        boolean ifNotExists = false;
        String dbComment = null;
        String dbLocation = null;
        Map<String, String> dbProps = null;

        for (int i = 1; i < ast.getChildCount(); i++) {
            HiveParserASTNode childNode = (HiveParserASTNode) ast.getChild(i);
            switch (childNode.getToken().getType()) {
                case HiveASTParser.TOK_IFNOTEXISTS:
                    ifNotExists = true;
                    break;
                case HiveASTParser.TOK_DATABASECOMMENT:
                    dbComment =
                            HiveParserBaseSemanticAnalyzer.unescapeSQLString(
                                    childNode.getChild(0).getText());
                    break;
                case HiveASTParser.TOK_DATABASEPROPERTIES:
                    dbProps = getProps((HiveParserASTNode) childNode.getChild(0));
                    break;
                case HiveASTParser.TOK_DATABASELOCATION:
                    dbLocation =
                            HiveParserBaseSemanticAnalyzer.unescapeSQLString(
                                    childNode.getChild(0).getText());
                    break;
                default:
                    throw new ValidationException(
                            "Unknown AST node for CREATE DATABASE: " + childNode);
            }
        }

        Map<String, String> props = new HashMap<>();
        if (dbProps != null) {
            props.putAll(dbProps);
        }

        if (dbLocation != null) {
            props.put(DATABASE_LOCATION_URI, dbLocation);
        }
        CatalogDatabase catalogDatabase = new CatalogDatabaseImpl(props, dbComment);
        return new CreateDatabaseOperation(
                catalogRegistry.getCurrentCatalog(), dbName, catalogDatabase, ifNotExists);
    }

    private Operation convertDropDatabase(HiveParserASTNode ast) {
        String dbName =
                HiveParserBaseSemanticAnalyzer.unescapeIdentifier(ast.getChild(0).getText());
        boolean ifExists = false;
        boolean ifCascade = false;

        if (null != ast.getFirstChildWithType(HiveASTParser.TOK_IFEXISTS)) {
            ifExists = true;
        }

        if (null != ast.getFirstChildWithType(HiveASTParser.TOK_CASCADE)) {
            ifCascade = true;
        }

        return new DropDatabaseOperation(
                catalogRegistry.getCurrentCatalog(), dbName, ifExists, ifCascade);
    }

    private Operation convertSwitchDatabase(HiveParserASTNode ast) {
        String dbName =
                HiveParserBaseSemanticAnalyzer.unescapeIdentifier(ast.getChild(0).getText());
        return new UseDatabaseOperation(catalogRegistry.getCurrentCatalog(), dbName);
    }

    private Operation convertDropTable(HiveParserASTNode ast, TableType expectedType)
            throws SemanticException {
        String tableName =
                HiveParserBaseSemanticAnalyzer.getUnescapedName(
                        (HiveParserASTNode) ast.getChild(0));
        boolean ifExists = (ast.getFirstChildWithType(HiveASTParser.TOK_IFEXISTS) != null);

        ObjectIdentifier identifier = parseObjectIdentifier(tableName);
        ResolvedCatalogBaseTable<?> baseTable = getResolvedCatalogBaseTable(identifier, true);

        if (expectedType == TableType.VIRTUAL_VIEW) {
            if (baseTable instanceof CatalogTable) {
                throw new ValidationException("DROP VIEW for a table is not allowed");
            }
            return new DropViewOperation(identifier, ifExists, false);
        } else {
            if (baseTable instanceof CatalogView) {
                throw new ValidationException("DROP TABLE for a view is not allowed");
            }
            return new DropTableOperation(identifier, ifExists, false);
        }
    }

    private void validateAlterTableType(Table tbl) {
        if (tbl.isNonNative()) {
            throw new ValidationException(
                    ErrorMsg.ALTER_TABLE_NON_NATIVE.getMsg(tbl.getTableName()));
        }
    }

    private Operation convertAlterTableProps(
            ResolvedCatalogBaseTable alteredTable,
            String tableName,
            HashMap<String, String> partSpec,
            HiveParserASTNode ast,
            boolean expectView,
            boolean isUnset) {

        HashMap<String, String> mapProp =
                getProps((HiveParserASTNode) (ast.getChild(0)).getChild(0));
        // we need to check if the properties are valid, especially for stats.
        // they might be changed via alter table .. update statistics or alter table .. set
        // tblproperties.
        // If the property is not row_count or raw_data_size, it could not be changed through update
        // statistics
        for (Map.Entry<String, String> entry : mapProp.entrySet()) {
            // we make sure that we do not change anything if there is anything wrong.
            if (entry.getKey().equals(StatsSetupConst.ROW_COUNT)
                    || entry.getKey().equals(StatsSetupConst.RAW_DATA_SIZE)) {
                try {
                    Long.parseLong(entry.getValue());
                } catch (Exception e) {
                    throw new ValidationException(
                            "AlterTable "
                                    + entry.getKey()
                                    + " failed with value "
                                    + entry.getValue());
                }
            } else {
                if (HiveOperation.ALTERTABLE_UPDATETABLESTATS
                                .getOperationName()
                                .equals(queryState.getCommandType())
                        || HiveOperation.ALTERTABLE_UPDATEPARTSTATS
                                .getOperationName()
                                .equals(queryState.getCommandType())) {
                    throw new ValidationException(
                            "AlterTable UpdateStats "
                                    + entry.getKey()
                                    + " failed because the only valid keys are "
                                    + StatsSetupConst.ROW_COUNT
                                    + " and "
                                    + StatsSetupConst.RAW_DATA_SIZE);
                }
            }
        }
        if (isUnset) {
            handleUnsupportedOperation("Unset properties not supported");
        }

        if (expectView) {
            return convertAlterViewProps((ResolvedCatalogView) alteredTable, tableName, mapProp);
        } else {
            Map<String, String> newProps = new HashMap<>();
            newProps.put(ALTER_TABLE_OP, CHANGE_TBL_PROPS.name());
            newProps.putAll(mapProp);
            return convertAlterTableProps(alteredTable, tableName, partSpec, newProps);
        }
    }

    private Operation convertAlterTableProps(
            CatalogBaseTable oldBaseTable,
            String tableName,
            Map<String, String> partSpec,
            Map<String, String> newProps) {
        ObjectIdentifier tableIdentifier = parseObjectIdentifier(tableName);
        CatalogTable oldTable = (CatalogTable) oldBaseTable;
        CatalogPartitionSpec catalogPartitionSpec =
                partSpec != null ? new CatalogPartitionSpec(partSpec) : null;
        CatalogPartition catalogPartition =
                partSpec != null ? getPartition(tableIdentifier, catalogPartitionSpec) : null;

        Map<String, String> props = new HashMap<>();
        if (catalogPartition != null) {
            props.putAll(catalogPartition.getProperties());
            props.putAll(newProps);
            return new AlterPartitionPropertiesOperation(
                    tableIdentifier,
                    catalogPartitionSpec,
                    new CatalogPartitionImpl(props, catalogPartition.getComment()));
        } else {
            props.putAll(oldTable.getOptions());
            props.putAll(newProps);
            return new AlterTableChangeOperation(
                    tableIdentifier,
                    newProps.entrySet().stream()
                            .map(entry -> TableChange.set(entry.getKey(), entry.getValue()))
                            .collect(Collectors.toList()),
                    oldTable.copy(props),
                    false);
        }
    }

    private Operation convertAlterTableSerdeProps(
            CatalogBaseTable alteredTable,
            HiveParserASTNode ast,
            String tableName,
            HashMap<String, String> partSpec) {
        HashMap<String, String> mapProp =
                getProps((HiveParserASTNode) (ast.getChild(0)).getChild(0));
        Map<String, String> newProps = new HashMap<>();
        newProps.put(ALTER_TABLE_OP, CHANGE_SERDE_PROPS.name());
        for (String key : mapProp.keySet()) {
            newProps.put(SERDE_INFO_PROP_PREFIX + key, mapProp.get(key));
        }
        return convertAlterTableProps(alteredTable, tableName, partSpec, newProps);
    }

    private Operation convertAlterTableSerde(
            CatalogBaseTable alteredTable,
            HiveParserASTNode ast,
            String tableName,
            HashMap<String, String> partSpec) {
        String serdeName =
                HiveParserBaseSemanticAnalyzer.unescapeSQLString(ast.getChild(0).getText());
        HashMap<String, String> mapProp = null;
        if (ast.getChildCount() > 1) {
            mapProp = getProps((HiveParserASTNode) (ast.getChild(1)).getChild(0));
        }
        Map<String, String> newProps = new HashMap<>();
        newProps.put(ALTER_TABLE_OP, CHANGE_SERDE_PROPS.name());
        newProps.put(SERDE_LIB_CLASS_NAME, serdeName);
        if (mapProp != null) {
            for (String key : mapProp.keySet()) {
                newProps.put(SERDE_INFO_PROP_PREFIX + key, mapProp.get(key));
            }
        }
        return convertAlterTableProps(alteredTable, tableName, partSpec, newProps);
    }

    private Operation convertAlterTableFileFormat(
            CatalogBaseTable alteredTable,
            HiveParserASTNode ast,
            String tableName,
            HashMap<String, String> partSpec)
            throws SemanticException {

        HiveParserStorageFormat format = new HiveParserStorageFormat(conf);
        HiveParserASTNode child = (HiveParserASTNode) ast.getChild(0);

        if (!format.fillStorageFormat(child)) {
            throw new ValidationException("Unknown AST node for ALTER TABLE FILEFORMAT: " + child);
        }

        Map<String, String> newProps = new HashMap<>();
        newProps.put(ALTER_TABLE_OP, CHANGE_FILE_FORMAT.name());
        newProps.put(STORED_AS_FILE_FORMAT, format.getGenericName());
        return convertAlterTableProps(alteredTable, tableName, partSpec, newProps);
    }

    private Operation convertAlterTableLocation(
            CatalogBaseTable alteredTable,
            HiveParserASTNode ast,
            String tableName,
            HashMap<String, String> partSpec) {
        String newLocation =
                HiveParserBaseSemanticAnalyzer.unescapeSQLString(ast.getChild(0).getText());
        Map<String, String> newProps = new HashMap<>();
        newProps.put(ALTER_TABLE_OP, CHANGE_LOCATION.name());
        newProps.put(TABLE_LOCATION_URI, newLocation);

        return convertAlterTableProps(alteredTable, tableName, partSpec, newProps);
    }

    public static HashMap<String, String> getProps(HiveParserASTNode prop) {
        // Must be deterministic order map for consistent q-test output across Java versions
        HashMap<String, String> mapProp = new LinkedHashMap<>();
        HiveParserBaseSemanticAnalyzer.readProps(prop, mapProp);
        return mapProp;
    }

    /** Utility class to resolve QualifiedName. */
    private static class QualifiedNameUtil {

        // Get the fully qualified name in the ast. e.g. the ast of the form ^(DOT^(DOT a b) c) will
        // generate a name of the form a.b.c
        public static String getFullyQualifiedName(HiveParserASTNode ast) {
            if (ast.getChildCount() == 0) {
                return ast.getText();
            } else if (ast.getChildCount() == 2) {
                return getFullyQualifiedName((HiveParserASTNode) ast.getChild(0))
                        + "."
                        + getFullyQualifiedName((HiveParserASTNode) ast.getChild(1));
            } else if (ast.getChildCount() == 3) {
                return getFullyQualifiedName((HiveParserASTNode) ast.getChild(0))
                        + "."
                        + getFullyQualifiedName((HiveParserASTNode) ast.getChild(1))
                        + "."
                        + getFullyQualifiedName((HiveParserASTNode) ast.getChild(2));
            } else {
                return null;
            }
        }

        // get the column path
        // return column name if exists, column could be DOT separated.
        // example: lintString.$elem$.myint
        // return table name for column name if no column has been specified.
        public static String getColPath(
                HiveParserASTNode node,
                String dbName,
                String tableName,
                Map<String, String> partSpec) {

            // if this ast has only one child, then no column name specified.
            if (node.getChildCount() == 1) {
                return tableName;
            }

            HiveParserASTNode columnNode = null;
            // Second child node could be partitionspec or column
            if (node.getChildCount() > 1) {
                if (partSpec == null) {
                    columnNode = (HiveParserASTNode) node.getChild(1);
                } else {
                    columnNode = (HiveParserASTNode) node.getChild(2);
                }
            }

            if (columnNode != null) {
                if (dbName == null) {
                    return tableName + "." + QualifiedNameUtil.getFullyQualifiedName(columnNode);
                } else {
                    return tableName.substring(dbName.length() + 1)
                            + "."
                            + QualifiedNameUtil.getFullyQualifiedName(columnNode);
                }
            } else {
                return tableName;
            }
        }

        // get partition metadata
        public static Map<String, String> getPartitionSpec(HiveParserASTNode ast) {
            HiveParserASTNode partNode = null;
            // if this ast has only one child, then no partition spec specified.
            if (ast.getChildCount() == 1) {
                return null;
            }

            // if ast has two children
            // the 2nd child could be partition spec or columnName
            // if the ast has 3 children, the second *has to* be partition spec
            if (ast.getChildCount() > 2
                    && (ast.getChild(1).getType() != HiveASTParser.TOK_PARTSPEC)) {
                throw new ValidationException(
                        ast.getChild(1).getType() + " is not a partition specification");
            }

            if (ast.getChild(1).getType() == HiveASTParser.TOK_PARTSPEC) {
                partNode = (HiveParserASTNode) ast.getChild(1);
            }

            if (partNode != null) {
                return getPartSpec(partNode);
            }

            return null;
        }
    }

    private CatalogPartition getPartition(
            ObjectIdentifier tableIdentifier, CatalogPartitionSpec partitionSpec) {
        return catalogRegistry
                .getPartition(tableIdentifier, partitionSpec)
                .orElseThrow(
                        () ->
                                new ValidationException(
                                        String.format(
                                                "Partition %s of table %s doesn't exist",
                                                partitionSpec.getPartitionSpec(),
                                                tableIdentifier)));
    }

    /**
     * A query like this will generate a tree as follows "describe formatted default.maptable
     * partition (b=100) id;" TOK_TABTYPE TOK_TABNAME --> root for tablename, 2 child nodes mean DB
     * specified default maptable TOK_PARTSPEC --> root node for partition spec. else columnName
     * TOK_PARTVAL b 100 id --> root node for columnName formatted
     */
    private Operation convertDescribeTable(HiveParserASTNode ast) {
        HiveParserASTNode tableTypeExpr = (HiveParserASTNode) ast.getChild(0);

        String dbName = null;
        String tableName;
        String colPath;
        Map<String, String> partSpec;

        HiveParserASTNode tableNode;

        // process the first node to extract tablename
        // tablename is either TABLENAME or DBNAME.TABLENAME if db is given
        if (tableTypeExpr.getChild(0).getType() == HiveASTParser.TOK_TABNAME) {
            tableNode = (HiveParserASTNode) tableTypeExpr.getChild(0);
            if (tableNode.getChildCount() == 1) {
                tableName = tableNode.getChild(0).getText();
            } else if (tableNode.getChildCount() == 2) {
                dbName = tableNode.getChild(0).getText();
                tableName = dbName + "." + tableNode.getChild(1).getText();
            } else {
                // tablemname is CATALOGNAME.DBNAME.TABLENAME, which is not supported yet.
                // todo: fix it in FLINK-29343
                throw new ValidationException(
                        "Describe a table in specific catalog is not supported in HiveDialect,"
                                + " please switch to Flink default dialect.");
            }
        } else {
            throw new ValidationException(
                    tableTypeExpr.getChild(0).getText() + " is not an expected token type");
        }

        // process the second child,if exists, node to get partition spec(s)
        partSpec = QualifiedNameUtil.getPartitionSpec(tableTypeExpr);

        // process the third child node,if exists, to get partition spec(s)
        colPath = QualifiedNameUtil.getColPath(tableTypeExpr, dbName, tableName, partSpec);

        if (partSpec != null) {
            handleUnsupportedOperation("DESCRIBE PARTITION is not supported");
        }
        if (!colPath.equals(tableName)) {
            handleUnsupportedOperation("DESCRIBE COLUMNS is not supported");
        }

        boolean isExt = false;
        boolean isFormatted = false;
        if (ast.getChildCount() == 2) {
            int descOptions = ast.getChild(1).getType();
            isExt = descOptions == HiveASTParser.KW_EXTENDED;
            isFormatted = descOptions == HiveASTParser.KW_FORMATTED;
            if (descOptions == HiveASTParser.KW_PRETTY) {
                handleUnsupportedOperation("DESCRIBE PRETTY is not supported.");
            }
        }

        ObjectIdentifier tableIdentifier = parseObjectIdentifier(tableName);
        return new HiveExecutableOperation(
                new DescribeTableOperation(tableIdentifier, isExt || isFormatted));
    }

    public static HashMap<String, String> getPartSpec(HiveParserASTNode partspec) {
        if (partspec == null) {
            return null;
        }
        HashMap<String, String> partSpec = new LinkedHashMap<>();
        for (int i = 0; i < partspec.getChildCount(); ++i) {
            HiveParserASTNode partVal = (HiveParserASTNode) partspec.getChild(i);
            String key = partVal.getChild(0).getText();
            String val = null;
            if (partVal.getChildCount() == 3) {
                val = stripQuotes(partVal.getChild(2).getText());
            } else if (partVal.getChildCount() == 2) {
                val = stripQuotes(partVal.getChild(1).getText());
            }
            partSpec.put(key.toLowerCase(), val);
        }
        return partSpec;
    }

    // Get the partition specs from the tree
    private List<Map<String, String>> getPartitionSpecs(CommonTree ast) {
        List<Map<String, String>> partSpecs = new ArrayList<>();
        // get partition metadata if partition specified
        for (int childIndex = 0; childIndex < ast.getChildCount(); childIndex++) {
            HiveParserASTNode partSpecNode = (HiveParserASTNode) ast.getChild(childIndex);
            // sanity check
            if (partSpecNode.getType() == HiveASTParser.TOK_PARTSPEC) {
                Map<String, String> partSpec = getPartSpec(partSpecNode);
                partSpecs.add(partSpec);
            }
        }
        return partSpecs;
    }

    private Operation convertShowPartitions(HiveParserASTNode ast) throws SemanticException {
        String tableName =
                HiveParserBaseSemanticAnalyzer.getUnescapedName(
                        (HiveParserASTNode) ast.getChild(0));
        List<Map<String, String>> partSpecs = getPartitionSpecs(ast);
        // We only can have a single partition spec
        assert (partSpecs.size() <= 1);
        Map<String, String> partSpec = null;
        if (partSpecs.size() > 0) {
            partSpec = partSpecs.get(0);
        }

        ObjectIdentifier tableIdentifier = parseObjectIdentifier(tableName);
        CatalogPartitionSpec spec = null;
        if (partSpec != null && !partSpec.isEmpty()) {
            spec = new CatalogPartitionSpec(new HashMap<>(partSpec));
        }
        return new ShowPartitionsOperation(
                tableIdentifier,
                spec,
                HiveConf.getVar(conf, HiveConf.ConfVars.DEFAULTPARTITIONNAME));
    }

    private Operation convertShowDatabases(String catalogName) {
        return new ShowDatabasesOperation(catalogName);
    }

    private Operation convertShowTables(HiveParserASTNode ast, boolean expectView) {
        String dbName = currentDB;
        String pattern = null;

        if (ast.getChildCount() > 3) {
            throw new ValidationException("Internal error : Invalid AST " + ast.toStringTree());
        }

        switch (ast.getChildCount()) {
            case 1: // Uses a pattern
                pattern =
                        HiveParserBaseSemanticAnalyzer.unescapeSQLString(ast.getChild(0).getText());
                break;
            case 2: // Specifies a DB
                assert (ast.getChild(0).getType() == HiveASTParser.TOK_FROM);
                dbName =
                        HiveParserBaseSemanticAnalyzer.unescapeIdentifier(
                                ast.getChild(1).getText());
                break;
            case 3: // Uses a pattern and specifies a DB
                assert (ast.getChild(0).getType() == HiveASTParser.TOK_FROM);
                dbName =
                        HiveParserBaseSemanticAnalyzer.unescapeIdentifier(
                                ast.getChild(1).getText());
                pattern =
                        HiveParserBaseSemanticAnalyzer.unescapeSQLString(ast.getChild(2).getText());
                break;
            default: // No pattern or DB
                break;
        }
        if (!dbName.equalsIgnoreCase(currentDB)) {
            handleUnsupportedOperation("SHOW TABLES/VIEWS IN DATABASE is not supported");
        }
        if (pattern != null) {
            handleUnsupportedOperation("SHOW TABLES/VIEWS LIKE is not supported");
        }
        return expectView
                ? new ShowViewsOperation(
                        catalogRegistry.getCurrentCatalog(), catalogRegistry.getCurrentDatabase())
                : new ShowTablesOperation(
                        catalogRegistry.getCurrentCatalog(), catalogRegistry.getCurrentDatabase());
    }

    /**
     * Add the task according to the parsed command tree. This is used for the CLI command "SHOW
     * FUNCTIONS;".
     *
     * @param ast The parsed command tree.
     */
    private Operation convertShowFunctions(HiveParserASTNode ast) {
        if (ast.getChildCount() == 2) {
            assert (ast.getChild(0).getType() == HiveASTParser.KW_LIKE);
            throw new ValidationException("SHOW FUNCTIONS LIKE is not supported yet");
        }
        return new ShowFunctionsOperation(
                catalogRegistry.getCurrentCatalog(), catalogRegistry.getCurrentDatabase());
    }

    private Operation convertAlterTableRename(
            String sourceName, HiveParserASTNode ast, boolean expectView) throws SemanticException {
        ObjectIdentifier tableIdentifier =
                HiveParserBaseSemanticAnalyzer.getObjectIdentifier(
                        catalogRegistry, (HiveParserASTNode) ast.getChild(0));
        String[] target =
                new String[] {tableIdentifier.getDatabaseName(), tableIdentifier.getObjectName()};

        String targetName = HiveParserBaseSemanticAnalyzer.getDotName(target);
        ObjectIdentifier objectIdentifier = parseObjectIdentifier(sourceName);

        return expectView
                ? new AlterViewRenameOperation(objectIdentifier, parseObjectIdentifier(targetName))
                : new AlterTableRenameOperation(
                        objectIdentifier, parseObjectIdentifier(targetName), false);
    }

    private Operation convertAlterTableChangeCol(
            CatalogBaseTable alteredTable, String[] qualified, HiveParserASTNode ast)
            throws SemanticException {
        String newComment = null;
        boolean first = false;
        String flagCol = null;
        boolean isCascade = false;
        // col_old_name col_new_name column_type [COMMENT col_comment] [FIRST|AFTER column_name]
        // [CASCADE|RESTRICT]
        String oldColName = ast.getChild(0).getText();
        String newColName = ast.getChild(1).getText();
        String newType =
                HiveParserBaseSemanticAnalyzer.getTypeStringFromAST(
                        (HiveParserASTNode) ast.getChild(2));
        int childCount = ast.getChildCount();
        for (int i = 3; i < childCount; i++) {
            HiveParserASTNode child = (HiveParserASTNode) ast.getChild(i);
            switch (child.getToken().getType()) {
                case HiveASTParser.StringLiteral:
                    newComment = HiveParserBaseSemanticAnalyzer.unescapeSQLString(child.getText());
                    break;
                case HiveASTParser.TOK_ALTERTABLE_CHANGECOL_AFTER_POSITION:
                    flagCol =
                            HiveParserBaseSemanticAnalyzer.unescapeIdentifier(
                                    child.getChild(0).getText());
                    break;
                case HiveASTParser.KW_FIRST:
                    first = true;
                    break;
                case HiveASTParser.TOK_CASCADE:
                    isCascade = true;
                    break;
                case HiveASTParser.TOK_RESTRICT:
                    break;
                default:
                    throw new ValidationException(
                            "Unsupported token: " + child.getToken() + " for alter table");
            }
        }

        // Validate the operation of renaming a column name.
        Table tab = getTable(new ObjectPath(qualified[0], qualified[1]));

        SkewedInfo skewInfo = tab.getTTable().getSd().getSkewedInfo();
        if ((null != skewInfo)
                && (null != skewInfo.getSkewedColNames())
                && skewInfo.getSkewedColNames().contains(oldColName)) {
            throw new ValidationException(
                    oldColName + ErrorMsg.ALTER_TABLE_NOT_ALLOWED_RENAME_SKEWED_COLUMN.getMsg());
        }

        String tblName = HiveParserBaseSemanticAnalyzer.getDotName(qualified);

        ObjectIdentifier tableIdentifier = parseObjectIdentifier(tblName);
        ResolvedCatalogTable oldTable = (ResolvedCatalogTable) alteredTable;
        String oldName = HiveParserBaseSemanticAnalyzer.unescapeIdentifier(oldColName);
        String newName = HiveParserBaseSemanticAnalyzer.unescapeIdentifier(newColName);

        if (oldTable.getPartitionKeys().contains(oldName)) {
            // disallow changing partition columns
            throw new ValidationException("CHANGE COLUMN cannot be applied to partition columns");
        }
        ResolvedSchema oldSchema = oldTable.getResolvedSchema();
        Column newTableColumn =
                Column.physical(
                        newName,
                        HiveTypeUtil.toFlinkType(TypeInfoUtils.getTypeInfoFromTypeString(newType)));
        ResolvedSchema newSchema =
                TableSchemaUtils.changeColumn(oldSchema, oldName, newTableColumn, first, flagCol);
        Map<String, String> props = new HashMap<>(oldTable.getOptions());
        props.put(ALTER_TABLE_OP, ALTER_COLUMNS.name());
        if (isCascade) {
            props.put(ALTER_COL_CASCADE, "true");
        }

        Column oldColumn =
                oldSchema
                        .getColumn(oldName)
                        .orElseThrow(
                                () ->
                                        new ValidationException(
                                                "Can not find the old column: " + oldColName));
        if (newComment != null) {
            newTableColumn = newTableColumn.withComment(newComment);
        } else {
            newTableColumn = newTableColumn.withComment(oldColumn.getComment().orElse(null));
        }

        List<TableChange> tableChanges =
                TableSchemaUtils.buildModifyColumnChange(
                        oldColumn,
                        newTableColumn,
                        first
                                ? TableChange.ColumnPosition.first()
                                : (flagCol == null
                                        ? null
                                        : TableChange.ColumnPosition.after(flagCol)));

        return new AlterTableChangeOperation(
                tableIdentifier,
                tableChanges,
                new ResolvedCatalogTable(
                        CatalogTable.of(
                                Schema.newBuilder().fromResolvedSchema(newSchema).build(),
                                oldTable.getComment(),
                                oldTable.getPartitionKeys(),
                                props),
                        newSchema),
                false);
    }

    private Operation convertAlterTableModifyCols(
            ResolvedCatalogBaseTable<?> alteredTable,
            String tblName,
            HiveParserASTNode ast,
            boolean replace)
            throws SemanticException {

        List<FieldSchema> newCols =
                HiveParserBaseSemanticAnalyzer.getColumns((HiveParserASTNode) ast.getChild(0));
        boolean isCascade = false;
        if (null != ast.getFirstChildWithType(HiveASTParser.TOK_CASCADE)) {
            isCascade = true;
        }

        ObjectIdentifier tableIdentifier = parseObjectIdentifier(tblName);
        ResolvedCatalogTable oldTable = (ResolvedCatalogTable) alteredTable;

        // prepare properties
        Map<String, String> props = new HashMap<>(oldTable.getOptions());
        props.put(ALTER_TABLE_OP, ALTER_COLUMNS.name());
        if (isCascade) {
            props.put(ALTER_COL_CASCADE, "true");
        }
        ResolvedSchema oldSchema = oldTable.getResolvedSchema();
        final int numPartCol = oldTable.getPartitionKeys().size();
        // add existing non-part col if we're not replacing
        List<Column> newColumns = new ArrayList<>();
        if (!replace) {
            List<Column> nonPartCols =
                    oldSchema.getColumns().subList(0, oldSchema.getColumnCount() - numPartCol);

            newColumns.addAll(nonPartCols);
        }
        // add new cols
        for (FieldSchema col : newCols) {
            newColumns.add(
                    Column.physical(
                            col.getName(),
                            HiveTypeUtil.toFlinkType(
                                    TypeInfoUtils.getTypeInfoFromTypeString(col.getType()))));
        }
        // add part cols
        List<Column> partCols =
                oldSchema
                        .getColumns()
                        .subList(
                                oldSchema.getColumnCount() - numPartCol,
                                oldSchema.getColumnCount());
        newColumns.addAll(partCols);
        ResolvedSchema newSchema;
        if (!replace) {
            newSchema =
                    new ResolvedSchema(
                            newColumns,
                            oldSchema.getWatermarkSpecs(),
                            oldSchema.getPrimaryKey().orElse(null));
        } else {
            newSchema = ResolvedSchema.of(newColumns);
        }
        return new AlterTableSchemaOperation(
                tableIdentifier,
                new ResolvedCatalogTable(
                        CatalogTable.of(
                                Schema.newBuilder().fromResolvedSchema(newSchema).build(),
                                oldTable.getComment(),
                                oldTable.getPartitionKeys(),
                                props),
                        newSchema),
                false);
    }

    private Operation convertAlterTableDropParts(String[] qualified, HiveParserASTNode ast) {

        boolean ifExists = ast.getFirstChildWithType(HiveASTParser.TOK_IFEXISTS) != null;
        // If the drop has to fail on non-existent partitions, we cannot batch expressions.
        // That is because we actually have to check each separate expression for existence.
        // We could do a small optimization for the case where expr has all columns and all
        // operators are equality, if we assume those would always match one partition (which
        // may not be true with legacy, non-normalized column values). This is probably a
        // popular case but that's kinda hacky. Let's not do it for now.

        Table tab = getTable(new ObjectPath(qualified[0], qualified[1]));
        // hive represents drop partition specs with generic func desc, but what we need is just
        // spec maps
        List<Map<String, String>> partSpecs = new ArrayList<>();
        for (int i = 0; i < ast.getChildCount(); i++) {
            HiveParserASTNode child = (HiveParserASTNode) ast.getChild(i);
            if (child.getType() == HiveASTParser.TOK_PARTSPEC) {
                partSpecs.add(getPartSpec(child));
            }
        }

        validateAlterTableType(tab);

        ObjectIdentifier tableIdentifier =
                catalogRegistry.qualifyIdentifier(
                        UnresolvedIdentifier.of(qualified[0], qualified[1]));
        List<CatalogPartitionSpec> specs =
                partSpecs.stream().map(CatalogPartitionSpec::new).collect(Collectors.toList());
        return new DropPartitionsOperation(tableIdentifier, ifExists, specs);
    }

    /**
     * Add one or more partitions to a table. Useful when the data has been copied to the right
     * location by some other process.
     */
    private Operation convertAlterTableAddParts(String[] qualified, CommonTree ast) {
        // ^(TOK_ALTERTABLE_ADDPARTS identifier ifNotExists?
        // alterStatementSuffixAddPartitionsElement+)
        boolean ifNotExists = ast.getChild(0).getType() == HiveASTParser.TOK_IFNOTEXISTS;

        Table tab = getTable(new ObjectPath(qualified[0], qualified[1]));
        boolean isView = tab.isView();
        validateAlterTableType(tab);

        int numCh = ast.getChildCount();
        int start = ifNotExists ? 1 : 0;

        String currentLocation = null;
        Map<String, String> currentPartSpec = null;
        // Parser has done some verification, so the order of tokens doesn't need to be verified
        // here.
        List<CatalogPartitionSpec> specs = new ArrayList<>();
        List<CatalogPartition> partitions = new ArrayList<>();
        for (int num = start; num < numCh; num++) {
            HiveParserASTNode child = (HiveParserASTNode) ast.getChild(num);
            switch (child.getToken().getType()) {
                case HiveASTParser.TOK_PARTSPEC:
                    if (currentPartSpec != null) {
                        specs.add(new CatalogPartitionSpec(currentPartSpec));
                        Map<String, String> props = new HashMap<>();
                        if (currentLocation != null) {
                            props.put(TABLE_LOCATION_URI, currentLocation);
                        }
                        partitions.add(new CatalogPartitionImpl(props, null));
                        currentLocation = null;
                    }
                    currentPartSpec = getPartSpec(child);
                    validatePartitionValues(currentPartSpec); // validate reserved values
                    break;
                case HiveASTParser.TOK_PARTITIONLOCATION:
                    // if location specified, set in partition
                    if (isView) {
                        throw new ValidationException("LOCATION clause illegal for view partition");
                    }
                    currentLocation =
                            HiveParserBaseSemanticAnalyzer.unescapeSQLString(
                                    child.getChild(0).getText());
                    break;
                default:
                    throw new ValidationException("Unknown child: " + child);
            }
        }

        // add the last one
        if (currentPartSpec != null) {
            specs.add(new CatalogPartitionSpec(currentPartSpec));
            Map<String, String> props = new HashMap<>();
            if (currentLocation != null) {
                props.put(TABLE_LOCATION_URI, currentLocation);
            }
            partitions.add(new CatalogPartitionImpl(props, null));
        }

        ObjectIdentifier tableIdentifier =
                tab.getDbName() == null
                        ? parseObjectIdentifier(tab.getTableName())
                        : catalogRegistry.qualifyIdentifier(
                                UnresolvedIdentifier.of(tab.getDbName(), tab.getTableName()));
        return new AddPartitionsOperation(tableIdentifier, ifNotExists, specs, partitions);
    }

    private Operation convertAlterViewProps(
            ResolvedCatalogView oldView, String tableName, Map<String, String> newProps) {
        ObjectIdentifier viewIdentifier = parseObjectIdentifier(tableName);
        Map<String, String> props = new HashMap<>(oldView.getOptions());
        props.putAll(newProps);
        ResolvedSchema schema = oldView.getResolvedSchema();
        CatalogView newView =
                new ResolvedCatalogView(
                        CatalogView.of(
                                Schema.newBuilder().fromResolvedSchema(schema).build(),
                                oldView.getComment(),
                                oldView.getOriginalQuery(),
                                oldView.getExpandedQuery(),
                                props),
                        schema);
        return new AlterViewPropertiesOperation(viewIdentifier, newView);
    }

    private ResolvedCatalogBaseTable<?> getAlteredTable(String tableName, boolean expectView) {
        ObjectIdentifier objectIdentifier = parseObjectIdentifier(tableName);
        ResolvedCatalogBaseTable<?> resolvedCatalogBaseTable =
                getResolvedCatalogBaseTable(objectIdentifier);
        if (expectView) {
            if (resolvedCatalogBaseTable instanceof CatalogTable) {
                throw new ValidationException("ALTER VIEW for a table is not allowed");
            }
        } else {
            if (resolvedCatalogBaseTable instanceof CatalogView) {
                throw new ValidationException("ALTER TABLE for a view is not allowed");
            }
        }
        return resolvedCatalogBaseTable;
    }

    private ObjectIdentifier parseObjectIdentifier(String compoundName) {
        UnresolvedIdentifier unresolvedIdentifier = hiveParser.parseIdentifier(compoundName);
        return catalogRegistry.qualifyIdentifier(unresolvedIdentifier);
    }

    private CatalogDatabase getDatabase(String databaseName) {
        Catalog catalog = catalogRegistry.getCatalogOrError(catalogRegistry.getCurrentCatalog());
        CatalogDatabase database;
        try {
            database = catalog.getDatabase(databaseName);
        } catch (DatabaseNotExistException e) {
            throw new ValidationException(String.format("Database %s not exists", databaseName), e);
        }
        return database;
    }

    private ResolvedCatalogBaseTable<?> getResolvedCatalogBaseTable(
            ObjectIdentifier tableIdentifier) {
        return getResolvedCatalogBaseTable(tableIdentifier, false);
    }

    private ResolvedCatalogBaseTable<?> getResolvedCatalogBaseTable(
            ObjectIdentifier tableIdentifier, boolean ifExists) {
        if (catalogRegistry.isTemporaryTable(tableIdentifier)) {
            throw new ValidationException(
                    String.format("Table or View %s is temporary.", tableIdentifier.toString()));
        }
        Optional<ResolvedCatalogBaseTable<?>> optionalCatalogTable =
                catalogRegistry.getCatalogBaseTable(tableIdentifier);
        if (!optionalCatalogTable.isPresent()) {
            if (ifExists) {
                return null;
            } else {
                throw new ValidationException(
                        String.format(
                                "Table or View %s doesn't exist.", tableIdentifier.toString()));
            }
        }
        return optionalCatalogTable.get();
    }

    /**
     * Certain partition values are are used by hive. e.g. the default partition in dynamic
     * partitioning and the intermediate partition values used in the archiving process. Naturally,
     * prohibit the user from creating partitions with these reserved values. The check that this
     * function is more restrictive than the actual limitation, but it's simpler. Should be okay
     * since the reserved names are fairly long and uncommon.
     */
    private void validatePartitionValues(Map<String, String> partSpec) {
        for (Map.Entry<String, String> e : partSpec.entrySet()) {
            for (String s : reservedPartitionValues) {
                String value = e.getValue();
                if (value != null && value.contains(s)) {
                    throw new ValidationException(
                            ErrorMsg.RESERVED_PART_VAL.getMsg(
                                    "(User value: "
                                            + e.getValue()
                                            + " Reserved substring: "
                                            + s
                                            + ")"));
                }
            }
        }
    }

    private Map<String, String> addDefaultProperties(Map<String, String> tblProp) {
        Map<String, String> retValue;
        if (tblProp == null) {
            retValue = new HashMap<>();
        } else {
            retValue = tblProp;
        }
        String paraString = HiveConf.getVar(conf, HiveConf.ConfVars.NEWTABLEDEFAULTPARA);
        if (paraString != null && !paraString.isEmpty()) {
            for (String keyValuePair : paraString.split(",")) {
                String[] keyValue = keyValuePair.split("=", 2);
                if (keyValue.length != 2) {
                    continue;
                }
                if (!retValue.containsKey(keyValue[0])) {
                    retValue.put(keyValue[0], keyValue[1]);
                }
            }
        }
        return retValue;
    }

    private static void handleUnsupportedOperation(HiveParserASTNode astNode) {
        throw new ValidationException(
                null, new UnsupportedOperationException("Unsupported operation: " + astNode));
    }

    private static void handleUnsupportedOperation(String message) {
        throw new ValidationException(null, new UnsupportedOperationException(message));
    }
}
