/* -------------------------------------------------------------------------
 *
 * parse_utilcmd.cpp
 *	  Perform parse analysis work for various utility commands
 *
 * Formerly we did this work during parse_analyze() in analyze.c.  However
 * that is fairly unsafe in the presence of querytree caching, since any
 * database state that we depend on in making the transformations might be
 * obsolete by the time the utility command is executed; and utility commands
 * have no infrastructure for holding locks or rechecking plan validity.
 * Hence these functions are now called at the start of execution of their
 * respective utility commands.
 *
 *
 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 * Portions Copyright (c) 2010-2012 Postgres-XC Development Group
 *
 *	src/common/backend/parser/parse_utilcmd.cpp
 *
 * -------------------------------------------------------------------------
 */
#include <string.h>
#include "postgres.h"
#include "knl/knl_variable.h"

#include "access/reloptions.h"
#include "access/gtm.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/gs_column_keys.h"
#include "catalog/gs_encrypted_columns.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_partition_fn.h"
#include "catalog/pg_type.h"
#include "catalog/pg_proc.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/primnodes.h"
#include "optimizer/clauses.h"
#include "parser/analyze.h"
#include "parser/parse_clause.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "parser/parse_utilcmd.h"
#include "parser/parse_oper.h"
#include "parser/parse_coerce.h"
#ifdef PGXC
#include "optimizer/pgxcship.h"
#include "pgstat.h"
#include "pgxc/groupmgr.h"
#include "pgxc/locator.h"
#include "pgxc/pgxc.h"
#include "optimizer/pgxcplan.h"
#include "optimizer/nodegroups.h"
#include "pgxc/execRemote.h"
#include "pgxc/redistrib.h"
#include "executor/node/nodeModifyTable.h"
#endif
#include "parser/parser.h"
#include "rewrite/rewriteManip.h"
#include "executor/node/nodeModifyTable.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/extended_statistics.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/rel_gs.h"
#include "utils/partitionkey.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
#include "utils/numeric.h"
#include "utils/numeric_gs.h"
#include "utils/int16.h"
#include "mb/pg_wchar.h"
#include "gaussdb_version.h"
#include "gs_ledger/ledger_utils.h"
#include "client_logic/client_logic.h"
#include "client_logic/client_logic_enums.h"
#include "storage/checksum_impl.h"
#include "catalog/gs_collation.h"

/* State shared by transformCreateSchemaStmt and its subroutines */
typedef struct {
    const char* stmtType; /* "CREATE SCHEMA" or "ALTER SCHEMA" */
    char* schemaname;     /* name of schema */
    char* authid;         /* owner of schema */
    List* sequences;      /* CREATE SEQUENCE items */
    List* tables;         /* CREATE TABLE items */
    List* views;          /* CREATE VIEW items */
    List* indexes;        /* CREATE INDEX items */
    List* triggers;       /* CREATE TRIGGER items */
    List* grants;         /* GRANT items */
} CreateSchemaStmtContext;

#define ALTER_FOREIGN_TABLE "ALTER FOREIGN TABLE"
#define CREATE_FOREIGN_TABLE "CREATE FOREIGN TABLE"
#define ALTER_TABLE "ALTER TABLE"
#define CREATE_TABLE "CREATE TABLE"

#define RELATION_ISNOT_REGULAR_PARTITIONED(relation)                                                              \
    (((relation)->rd_rel->relkind != RELKIND_RELATION && (relation)->rd_rel->relkind != RELKIND_FOREIGN_TABLE && \
      (relation)->rd_rel->relkind != RELKIND_STREAM) || RelationIsNonpartitioned((relation)))

static void transformColumnDefinition(CreateStmtContext* cxt, ColumnDef* column, bool preCheck);
static void transformTableConstraint(CreateStmtContext* cxt, Constraint* constraint);
static void transformTableLikeClause(
    CreateStmtContext* cxt, TableLikeClause* table_like_clause, bool preCheck, bool isFirstNode, bool is_row_table, TransformTableType transformType);
/* decide if serial column should be copied under analyze */
static bool IsCreatingSeqforAnalyzeTempTable(CreateStmtContext* cxt);
static void transformTableLikePartitionProperty(Relation relation, HeapTuple partitionTableTuple, List** partKeyColumns,
    List* partitionList, List** partitionDefinitions);
static IntervalPartitionDefState* TransformTableLikeIntervalPartitionDef(HeapTuple partitionTableTuple);
static void transformTableLikePartitionKeys(
    Relation relation, HeapTuple partitionTableTuple, List** partKeyColumns, List** partKeyPosList);
static void transformTableLikePartitionBoundaries(
    Relation relation, List* partKeyPosList, List* partitionList, List** partitionDefinitions);
static void transformOfType(CreateStmtContext* cxt, TypeName* ofTypename);
static List* get_collation(Oid collation, Oid actual_datatype);
static List* get_opclass(Oid opclass, Oid actual_datatype);
static void checkPartitionValue(CreateStmtContext* cxt, CreateStmt* stmt);
static void TransformListDistributionValue(ParseState* pstate, Node** listDistDef, bool needCheck);
static void checkClusterConstraints(CreateStmtContext* cxt);
static void checkPsortIndexCompatible(IndexStmt* stmt);
static void checkCBtreeIndexCompatible(IndexStmt* stmt);
static void checkCGinBtreeIndexCompatible(IndexStmt* stmt);

static void checkReserveColumn(CreateStmtContext* cxt);
static void CheckDistributionSyntax(CreateStmt* stmt);
static void CheckListRangeDistribClause(CreateStmtContext *cxt, CreateStmt *stmt);

static void transformIndexConstraints(CreateStmtContext* cxt);
static void checkPartitionConstraintWithExpr(Constraint* con);
static void checkConditionForTransformIndex(
    Constraint* constraint, CreateStmtContext* cxt, Oid index_oid, Relation index_rel);
static IndexStmt* transformIndexConstraint(Constraint* constraint, CreateStmtContext* cxt, bool mustGlobal = false);
static void transformFKConstraints(CreateStmtContext* cxt, bool skipValidation, bool isAddConstraint);
static void transformConstraintAttrs(CreateStmtContext* cxt, List* constraintList);
static void transformColumnType(CreateStmtContext* cxt, ColumnDef* column);
static void setSchemaName(char* context_schema, char** stmt_schema_name);
static void TransformTempAutoIncrement(ColumnDef* column, CreateStmt* stmt);
static int128 TransformAutoIncStart(CreateStmt* stmt);
static void identity_type_dmod(ColumnDef* column);
static int identity_only(ColumnDef* column);

/*
 * @hdfs
 * The following three functions are used for HDFS foreign talbe constraint.
 */
static void setInternalFlagIndexStmt(List* IndexList);
static void checkInformationalConstraint(Node* node, bool isForeignTbl);
static void checkConstraint(CreateStmtContext* cxt, Node* node);
static void setMemCheckFlagForIdx(List* IndexList);

/* check partition name */
static void check_partition_name_internal(List* partitionList, bool isPartition);
static void check_partition_name_start_end(List* partitionList, bool isPartition);

/* for range partition: start/end syntax */
static void precheck_start_end_defstate(List* pos, FormData_pg_attribute* attrs,
    RangePartitionStartEndDefState* defState, bool isPartition);
static Datum get_partition_arg_value(Node* node, bool* isnull);
static Datum evaluate_opexpr(
    ParseState* pstate, List* oprname, Node* leftarg, Node* rightarg, Oid* restypid, int location);
static Const* coerce_partition_arg(ParseState* pstate, Node* node, Oid targetType);
static Oid choose_coerce_type(Oid leftid, Oid rightid);
static void get_rel_partition_info(Relation partTableRel, List** pos, Const** upBound);
static void get_src_partition_bound(Relation partTableRel, Oid srcPartOid, Const** lowBound, Const** upBound);
static Oid get_split_partition_oid(Relation partTableRel, SplitPartitionState* splitState);
static List* add_range_partition_def_state(List* xL, List* boundary, const char* partName, const char* tblSpaceName);
static bool CheckStepInRange(ParseState *pstate, Const *startVal, Const *endVal, Const *everyVal,
    Form_pg_attribute attr);
static List* divide_start_end_every_internal(ParseState* pstate, char* partName, Form_pg_attribute attr,
    Const* startVal, Const* endVal, Node* everyExpr, int* numPart,
    int maxNum, bool isinterval, bool needCheck, bool isPartition);
static List* DividePartitionStartEndInterval(ParseState* pstate, Form_pg_attribute attr, char* partName,
    Const* startVal, Const* endVal, Const* everyVal, Node* everyExpr, int* numPart, int maxNum, bool isPartition);
extern Node* makeAConst(Value* v, int location);
static bool IsElementExisted(List* indexElements, IndexElem* ielem);
static char* CreatestmtGetOrientation(CreateStmt *stmt);
static void CheckAutoIncrementIndex(CreateStmtContext *cxt);
static List* semtc_generate_hash_partition_defs(CreateStmt* stmt, const char* prefix_name, int part_count);
static void TransformModifyColumndef(CreateStmtContext* cxt, AlterTableCmd* cmd);
static void TransformColumnDefinitionOptions(CreateStmtContext* cxt, ColumnDef* column);
static void TransformColumnDefinitionConstraints(
    CreateStmtContext* cxt, ColumnDef* column, bool preCheck, bool is_modify);
#define REDIS_SCHEMA "data_redis"

/*
 * transformCreateStmt -
 *	  parse analysis for CREATE TABLE
 *
 * Returns a List of utility commands to be done in sequence.  One of these
 * will be the transformed CreateStmt, but there may be additional actions
 * to be done before and after the actual DefineRelation() call.
 *
 * SQL92 allows constraints to be scattered all over, so thumb through
 * the columns and collect all constraints into one place.
 * If there are any implied indices (e.g. UNIQUE or PRIMARY KEY)
 * then expand those into multiple IndexStmt blocks.
 *	  - thomas 1997-12-02
 */

static void checkPartitionConstraintWithExpr(Constraint* con)
{
    /* the primary key and unique index of partition table does not support expression index */
    if (con->contype == CONSTR_PRIMARY || con->contype == CONSTR_UNIQUE) {
        ListCell* lc_key = NULL;
        
        foreach(lc_key, con->keys) {
            IndexElem *elem = (IndexElem*)lfirst(lc_key);
            
            if (PointerIsValid(elem->expr)) {
                ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("partition table does not support expression index")));
            }
        }
    }
}

Oid check_collation_by_charset(const char* collate, int charset)
{
    Oid coll_oid = InvalidOid;
    coll_oid = GetSysCacheOid3(COLLNAMEENCNSP, PointerGetDatum(collate),
                               Int32GetDatum(charset),
                               ObjectIdGetDatum(PG_CATALOG_NAMESPACE));
    if (coll_oid == InvalidOid) {
        coll_oid = get_collation_oid_with_lower_name(collate, charset);
        if (coll_oid == InvalidOid) {
            ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
                    errmsg("collation \"%s\" for encoding \"%s\" does not exist",
                        collate, pg_encoding_to_char(charset))));
        }
    }
    return coll_oid;
}

/*
 * transform_default_collation -
 *      Returns the processed collation oid of schema、relation or attribute level.
 */
Oid transform_default_collation(const char* collate, int charset, Oid def_coll_oid, bool is_attr)
{
    Oid coll_oid = InvalidOid;
    HeapTuple coll_tup;
    
    if (collate != NULL && charset != PG_INVALID_ENCODING) {
        coll_oid = check_collation_by_charset(collate, charset);
    } else if (collate != NULL) {
        CatCList* list = NULL;
        list = SearchSysCacheList1(COLLNAMEENCNSP, PointerGetDatum(collate));
        if (list->n_members == 0) {
            /* If the collate string is in uppercase, change to lowercase and search it again */
            coll_oid = get_collation_oid_with_lower_name(collate, charset);
            if (coll_oid == InvalidOid) {
                ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
                        errmsg("collation \"%s\" does not exist", collate)));
            }
        } else if (list->n_members > 1) {
            /*
             * opengauss may have collation with same name. When specifying the collation in attribute,
             * you can specify these collation for forward compatibility.
             */
            if (is_attr) {
                charset = GetDatabaseEncoding();
                coll_oid = check_collation_by_charset(collate, charset);
            } else {
                ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT),
                        errmsg("there is more than one collation \"%s\" with the same name", collate)));
            }
        } else {
            coll_tup = t_thrd.lsc_cxt.FetchTupleFromCatCList(list, 0);
            charset = ((Form_pg_collation)GETSTRUCT(coll_tup))->collencoding;
            if (!is_attr && charset == PG_INVALID_ENCODING) {
                ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
                        errmsg("collation \"%s\" have no corresponding encoding", collate)));
            }
            coll_oid = HeapTupleGetOid(coll_tup);
        }

        ReleaseSysCacheList(list);
    } else if (charset != PG_INVALID_ENCODING) {
        coll_oid = get_default_collation_by_charset(charset);
    } else {
        coll_oid = def_coll_oid;
    }

    if (!is_attr && OidIsValid(coll_oid) && !COLLATION_IN_B_FORMAT(coll_oid)) {
        ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("this collation only cannot be specified here")));
    }
    if ((!ENABLE_MULTI_CHARSET || t_thrd.proc->workingVersionNum < MULTI_CHARSET_VERSION_NUM) &&
        charset != PG_INVALID_ENCODING && charset != PG_SQL_ASCII && charset != GetDatabaseEncoding()) {
        ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("difference between the charset and the database encoding has not supported")));
    }
    return coll_oid;
}

Oid fill_relation_collation(const char* collate, int charset, List** options, Oid nsp_coll_oid)
{
    if (!OidIsValid(nsp_coll_oid) && USE_DEFAULT_COLLATION) {
        nsp_coll_oid = get_default_collation_by_charset(GetDatabaseEncoding());
    }
    Oid coll_oid = transform_default_collation(collate, charset, nsp_coll_oid);
    ListCell* cell = NULL;
    DefElem* opt = NULL;
    
    if (coll_oid == InvalidOid) {
        return coll_oid;
    }
    /* If specified by reloption, this is the case. */
    foreach(cell, *options) {
        opt = (DefElem*)lfirst(cell);
        if (strncmp(opt->defname, "collate", strlen("collate")) == 0) {
            return coll_oid;
        }
    }
    *options = lappend(*options, makeDefElem("collate", (Node*)makeInteger(coll_oid)));
    return coll_oid;
}

static bool is_create_as_col_store(CreateStmt* stmt)
{
    ListCell *cell = NULL;
    char* storeTypeStr = NULL;
    foreach(cell, stmt->options) {
        DefElem *def = (DefElem *)lfirst(cell);
        if (pg_strcasecmp(def->defname, "orientation") == 0) {
            if (nodeTag(def->arg) == T_String) {
                storeTypeStr = strVal(def->arg);
            } else if (nodeTag(def->arg) == T_TypeName) {
                storeTypeStr = TypeNameToString((TypeName *)def->arg);
            } else {
                Assert(false);
            }
        }
    }
    return storeTypeStr && (pg_strcasecmp(storeTypeStr, ORIENTATION_COLUMN) == 0 ||
        pg_strcasecmp(storeTypeStr, ORIENTATION_ORC) == 0);
}

List* transformCreateStmt(CreateStmt* stmt, const char* queryString, const List* uuids, bool preCheck,
Oid *namespaceid, bool isFirstNode)
{
    ParseState* pstate = NULL;
    CreateStmtContext cxt;
    List* result = NIL;
    List* save_alist = NIL;
    ListCell* elements = NULL;
    Oid existing_relid;
    bool is_ledger_nsp = false;
    bool is_row_table = is_ledger_rowstore(stmt->options);
    
    if (uuids != NIL) {
        list_free_deep(stmt->uuids);
        stmt->uuids = (List*)copyObject(uuids);
    }
    
    if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP && stmt->relation->schemaname)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("temporary tables cannot specify a schema name")));

    /*
     * Look up the creation namespace.	This also checks permissions on the
     * target namespace, locks it against concurrent drops, checks for a
     * preexisting relation in that namespace with the same name, and updates
     * stmt->relation->relpersistence if the select namespace is temporary.
     */
    *namespaceid = RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, &existing_relid, RELKIND_RELATION);

    /*
     * Check whether relation is in ledger schema. If it is, we add hash column.
     */
    HeapTuple tp = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(*namespaceid));
    if (HeapTupleIsValid(tp)) {
        bool is_null = true;
        Datum datum = SysCacheGetAttr(NAMESPACEOID, tp, Anum_pg_namespace_nspblockchain, &is_null);
        if (!is_null) {
            is_ledger_nsp = DatumGetBool(datum);
        }
        ReleaseSysCache(tp);
    }

    if (is_ledger_nsp && stmt->partTableState != NULL && stmt->partTableState->subPartitionState != NULL) {
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 (errmsg("Un-support feature"), errdetail("Subpartition table does not support ledger user table."),
                  errcause("The function is not implemented."), erraction("Use other actions instead."))));
    }

    /*
     * If the relation already exists and the user specified "IF NOT EXISTS",
     * bail out with a NOTICE.
     */
    if (stmt->if_not_exists && OidIsValid(existing_relid)) {
        bool exists_ok = true;

        /*
         * If we are in an extension script, insist that the pre-existing
         * object be a member of the extension, to avoid security risks.
         */
        ObjectAddress address;

        ObjectAddressSet(address, RelationRelationId, existing_relid);
        checkMembershipInCurrentExtension(&address);

        /* 
         * Emit the right error or warning message for a "CREATE" command issued on a exist relation.
         * remote node : should have relation if recieve "IF NOT EXISTS" stmt.
         */
        if (!u_sess->attr.attr_common.xc_maintenance_mode && !IS_SINGLE_NODE) {
            if (!(IS_PGXC_COORDINATOR && !IsConnFromCoord())) {
                exists_ok = false;
            }
        }
        if (exists_ok) {
            ereport(NOTICE,
                (errcode(ERRCODE_DUPLICATE_TABLE),
                    errmsg("relation \"%s\" already exists, skipping", stmt->relation->relname)));
        } else {
            ereport(ERROR,
                (errcode(ERRCODE_DUPLICATE_TABLE),
                    errmsg("relation \"%s\" already exists", stmt->relation->relname)));
        }
        return NIL;
    }

    /*
     * Transform node group name of table in logic cluster.
     * If not TO GROUP clause, add default node group to the CreateStmt;
     * If logic cluster is redistributing, modify node group to target node group
     * except delete delta table.
     */
    if (IS_PGXC_COORDINATOR && in_logic_cluster()) {
        char* group_name = NULL;
        if (stmt->subcluster == NULL && !IsA(stmt, CreateForeignTableStmt)) {
            group_name = PgxcGroupGetCurrentLogicCluster();

            if (group_name != NULL) {
                stmt->subcluster = makeNode(PGXCSubCluster);
                stmt->subcluster->clustertype = SUBCLUSTER_GROUP;
                stmt->subcluster->members = list_make1(makeString(group_name));
            }
        } else if (stmt->subcluster != NULL && stmt->subcluster->clustertype == SUBCLUSTER_GROUP) {
            Assert(stmt->subcluster->members->length == 1);
            group_name = strVal(linitial(stmt->subcluster->members));
            Assert(group_name != NULL);

            if (IsLogicClusterRedistributed(group_name)) {
                /*Specially handle delete delta table.*/
                bool is_delete_delta = false;
                if (!IsA(stmt, CreateForeignTableStmt) && stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED) {
                    is_delete_delta = RelationIsDeleteDeltaTable(stmt->relation->relname);
                }

                /*Logic cluster is redistributing, modify node group to target node group */
                if (!is_delete_delta) {
                    Value* val = (Value*)linitial(stmt->subcluster->members);
                    group_name = PgxcGroupGetStmtExecGroupInRedis();

                    if (group_name != NULL) {
                        pfree_ext(strVal(val));
                        strVal(val) = group_name;
                    }
                }
            }
        }
    }

    /*
     * If the target relation name isn't schema-qualified, make it so.  This
     * prevents some corner cases in which added-on rewritten commands might
     * think they should apply to other relations that have the same name and
     * are earlier in the search path.	But a local temp table is effectively
     * specified to be in pg_temp, so no need for anything extra in that case.
     */
    if (stmt->relation->schemaname == NULL && stmt->relation->relpersistence != RELPERSISTENCE_TEMP) {
        /* If create schema already set, use the latest one */
        if (t_thrd.proc->workingVersionNum >= 92408 && u_sess->catalog_cxt.setCurCreateSchema) {
            stmt->relation->schemaname = pstrdup(u_sess->catalog_cxt.curCreateSchema);
        } else {
            stmt->relation->schemaname = get_namespace_name(*namespaceid);
        }
        if (t_thrd.proc->workingVersionNum >= 92408 && IS_MAIN_COORDINATOR &&
            !u_sess->catalog_cxt.setCurCreateSchema) {
            u_sess->catalog_cxt.setCurCreateSchema = true;
            u_sess->catalog_cxt.curCreateSchema =
                MemoryContextStrdup(u_sess->top_transaction_mem_cxt,stmt->relation->schemaname);
        }
    }

    /* Set up pstate and CreateStmtContext */
    pstate = make_parsestate(NULL);
    pstate->p_sourcetext = queryString;

    cxt.pstate = pstate;
    if (IsA(stmt, CreateForeignTableStmt))
        cxt.stmtType = CREATE_FOREIGN_TABLE;
    else
        cxt.stmtType = CREATE_TABLE;
    cxt.relation = stmt->relation;
    cxt.rel = NULL;
    cxt.inhRelations = stmt->inhRelations;
    cxt.subcluster = stmt->subcluster;
    cxt.isalter = false;
    cxt.columns = NIL;
    cxt.ckconstraints = NIL;
    cxt.fkconstraints = NIL;
    cxt.ixconstraints = NIL;
    cxt.clusterConstraints = NIL;
    cxt.inh_indexes = NIL;
    cxt.blist = NIL;
    cxt.alist = NIL;
    cxt.pkey = NULL;
    cxt.csc_partTableState = NULL;
    cxt.reloptions = NIL;
    cxt.hasoids = false;

#ifdef PGXC
    cxt.fallback_dist_col = NULL;
    cxt.distributeby = NULL;
#endif
    cxt.node = (Node*)stmt;
    cxt.internalData = stmt->internalData;
    cxt.isResizing = false;
    cxt.ofType = (stmt->ofTypename != NULL);

    /* We have gen uuids, so use it */
    if (stmt->uuids != NIL)
        cxt.uuids = stmt->uuids;

    if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && stmt->internalData != NULL) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Do not support create table with INERNAL DATA clause.")));
    }

    if (IsA(stmt, CreateForeignTableStmt)) {
        CreateForeignTableStmt* fStmt = (CreateForeignTableStmt*)stmt;
        cxt.canInfomationalConstraint = CAN_BUILD_INFORMATIONAL_CONSTRAINT_BY_STMT(fStmt);
    } else {
        cxt.canInfomationalConstraint = false;
    }

    AssertEreport(stmt->ofTypename == NULL || stmt->inhRelations == NULL, MOD_OPT, "");

    if (stmt->ofTypename)
        transformOfType(&cxt, stmt->ofTypename);

    ListCell *cell = NULL;
    foreach (cell, stmt->tableOptions) {
        void *pointer = lfirst(cell);
        if (IsA(pointer, CommentStmt)) {
            CommentStmt *commentStmt = (CommentStmt *)pointer;
            commentStmt->objtype = OBJECT_TABLE;
            commentStmt->objname = list_make1(makeString(cxt.relation->relname));
            if (cxt.relation->schemaname) {
                commentStmt->objname = lcons(makeString(cxt.relation->schemaname), commentStmt->objname);
            }
            cxt.alist = lappend(cxt.alist, commentStmt);
            break;
        }
    }

    Oid rel_coll_oid = DEFAULT_COLLATION_OID;
    if (DB_CMPT_B) {
        Oid nspdefcoll = get_nsp_default_collation(*namespaceid);
        if (stmt->collate || stmt->charset != PG_INVALID_ENCODING || nspdefcoll != InvalidOid) {
            rel_coll_oid = transform_default_collation(stmt->collate, stmt->charset, nspdefcoll);
        }
    }
    cxt.rel_coll_id = rel_coll_oid;

    /*
     * Run through each primary element in the table creation clause. Separate
     * column defs from constraints, and do preliminary analysis.
     */
    int count = 0;
    foreach (elements, stmt->tableElts) {
        TableLikeClause* tbl_like_clause = NULL;
        TransformTableType transformType = TRANSFORM_INVALID;
        Node* element = (Node*)lfirst(elements);
        cxt.uuids = stmt->uuids;

        switch (nodeTag(element)) {
            case T_ColumnDef:
                if (is_ledger_nsp && strcmp(((ColumnDef*)element)->colname, "hash") == 0 &&
                    !IsA(stmt, CreateForeignTableStmt) && stmt->relation->relpersistence == RELPERSISTENCE_PERMANENT &&
                    is_row_table) {
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("\"hash\" column is reserved for system in ledger schema.")));
                }

                count += identity_only((ColumnDef*)element);
                if (count > 1) {
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("table can only have one identity column")));
                }

                transformColumnDefinition(&cxt, (ColumnDef*)element, !isFirstNode && preCheck);

                if (((ColumnDef *)element)->clientLogicColumnRef != NULL) {
                    if (stmt->partTableState != NULL && stmt->partTableState->subPartitionState != NULL) {
                        ereport(
                            ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             (errmsg("Un-support feature"),
                              errdetail("For subpartition table, cryptographic database is not yet supported."),
                              errcause("The function is not implemented."), erraction("Use other actions instead."))));
                    }
                }

                break;

            case T_Constraint:
                transformTableConstraint(&cxt, (Constraint*)element);
                break;

            case T_TableLikeClause:
                tbl_like_clause = (TableLikeClause*)element;
#ifndef ENABLE_MULTIPLE_NODES
                if (tbl_like_clause->options & CREATE_TABLE_LIKE_DISTRIBUTION)
                    DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
                if (PointerIsValid(stmt->partTableState) && (tbl_like_clause->options & CREATE_TABLE_LIKE_PARTITION)) {
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("unsupport \"like clause including partition\" for partitioned table"),
                            errdetail("use either \"like clause including partition\" or \"partition by\" clause")));
                }
                if (PointerIsValid(stmt->options) && (tbl_like_clause->options & CREATE_TABLE_LIKE_RELOPTIONS)) {
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("unsupport \"like clause including reloptions\" together with \"with\""),
                            errdetail("use either \"like clause including reloptions\" or \"with\" clause")));
                }
#ifdef PGXC
                if (IS_PGXC_COORDINATOR && (tbl_like_clause->options & CREATE_TABLE_LIKE_DISTRIBUTION)) {
                    if (PointerIsValid(stmt->distributeby)) {
                        ereport(ERROR,
                            (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                errmsg(
                                    "unsupport \"like clause including distribution\" together with \"distribute by\""),
                                errdetail(
                                    "use either \"like clause including distribution\" or \"distribute by\" clause")));
                    }
                }
#endif
                if (!(tbl_like_clause->options & CREATE_TABLE_LIKE_RELOPTIONS)) {
                    //transformType is only use in hashbucket data transfer
                    if (is_ledger_hashbucketstore(stmt->options)) {
                        transformType = TRANSFORM_TO_HASHBUCKET;
                    } else {
                        transformType = TRANSFORM_TO_NONHASHBUCKET;
                    }
                }
                transformTableLikeClause(&cxt, (TableLikeClause*)element, !isFirstNode && preCheck, isFirstNode, is_row_table, transformType);
                if (stmt->relation->relpersistence != RELPERSISTENCE_TEMP &&
                    tbl_like_clause->relation->relpersistence == RELPERSISTENCE_TEMP)
                    ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                            errmsg("do not support create non-temp table like temp table")));
                break;

            default:
                ereport(ERROR,
                    (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                        errmsg("unrecognized node type: %d", (int)nodeTag(element))));
                break;
        }
    }

    /*
     * add hash column for table in ledger scheam;
     */
    if (is_ledger_nsp && !IsA(stmt, CreateForeignTableStmt) &&
        stmt->relation->relpersistence == RELPERSISTENCE_PERMANENT && is_ledger_rowstore(stmt->options)) {
        ColumnDef* col = makeNode(ColumnDef);
        col->colname = pstrdup("hash");
        col->typname = SystemTypeName("hash16");
        col->kvtype = 0;
        col->inhcount = 0;
        col->is_local = true;
        col->is_not_null = false;
        col->is_from_type = false;
        col->storage = 0;
        col->cmprs_mode = ATT_CMPR_UNDEFINED;
        col->raw_default = NULL;
        col->cooked_default = NULL;
        col->collClause = NULL;
        col->collOid = InvalidOid;
        col->constraints = NIL;
        col->fdwoptions = NIL;
        col->update_default = NULL;
        transformColumnDefinition(&cxt, col, !isFirstNode && preCheck);
    }

    // cxt.csc_partTableState is the partitionState generated
    // from like including partition clause
    if (cxt.csc_partTableState != NULL) {
        Assert(stmt->partTableState == NULL);
        stmt->partTableState = cxt.csc_partTableState;
    }
    /* check syntax for CREATE TABLE */
    checkPartitionSynax(stmt);

    /*
     * @hdfs
     * If the table is foreign table, must be gotten the ispartitioned value
     * from part_state struct.
     */
    if (IsA(stmt, CreateForeignTableStmt)) {
        CreateForeignTableStmt* ftblStmt = (CreateForeignTableStmt*)stmt;
        if (NULL != ftblStmt->part_state) {
            cxt.ispartitioned = true;
            cxt.partitionKey = ftblStmt->part_state->partitionKey;
            cxt.subPartitionKey = NULL;
        } else {
            cxt.ispartitioned = false;
        }
    } else {
        cxt.ispartitioned = PointerIsValid(stmt->partTableState);
        if (cxt.ispartitioned) {
            cxt.partitionKey = stmt->partTableState->partitionKey;
            if (stmt->partTableState->subPartitionState != NULL) {
                cxt.subPartitionKey = stmt->partTableState->subPartitionState->partitionKey;
            } else {
                cxt.subPartitionKey = NULL;
            }
        }
    }

    /* expresssion syntax is not supported in distributed database,
     * therefore, we do not have to judge whether to support distributed database in any mode.
     */
    if (cxt.ispartitioned && DB_IS_CMPT(B_FORMAT)) {
        ListCell* lc_con = NULL;
        
        foreach (lc_con, cxt.ixconstraints) {
            Constraint* constraint = (Constraint*)lfirst(lc_con);
            AssertEreport(IsA(constraint, Constraint), MOD_OPT, "");

            checkPartitionConstraintWithExpr(constraint);
        }
    }

    checkPartitionValue(&cxt, stmt);

    /*
     * transform START/END into LESS/THAN:
     * Put this part behind checkPartitionValue(), since we assume start/end/every-paramters
     * have already been transformed from A_Const into Const.
     */
    if (stmt->partTableState && is_start_end_def_list(stmt->partTableState->partitionList)) {
        List* pos = NIL;
        TupleDesc desc;

        /* get partition key position */
        pos = GetPartitionkeyPos(stmt->partTableState->partitionKey, cxt.columns);

        /* get descriptor */
        desc = BuildDescForRelation(cxt.columns);

        /* entry of transform */
        stmt->partTableState->partitionList = transformRangePartStartEndStmt(
            pstate, stmt->partTableState->partitionList, pos, desc->attrs, 0, NULL, NULL, true);
    }

    if (PointerIsValid(stmt->partTableState)) {
// only check partition name duplication on primary coordinator
#ifdef PGXC
        if ((IS_PGXC_COORDINATOR && !IsConnFromCoord()) || IS_SINGLE_NODE) {
            if (stmt->partTableState->subPartitionState != NULL) {
                checkSubPartitionName(stmt->partTableState->partitionList);
            } else {
                checkPartitionName(stmt->partTableState->partitionList);
            }
        }
#else
        checkPartitionName(stmt->partTableState->partitionList);
#endif
        SetPartitionnoForPartitionState(stmt->partTableState);
    }

    /* like clause-including reloptions: cxt.reloptions is produced by like including reloptions clause */
    /* output to stmt->options */
    if (cxt.reloptions != NIL) {
        stmt->options = list_concat(stmt->options, cxt.reloptions);
    }
    /* like clause-including oids: cxt.hasoids is produced by like including oids clause, output to stmt->options */
    if (cxt.hasoids) {
        stmt->options = lappend(stmt->options, makeDefElem("oids", (Node*)makeInteger(cxt.hasoids)));
    }
    cxt.hasoids = interpretOidsOption(stmt->options);

#ifdef PGXC
    if (cxt.distributeby != NULL) {
        stmt->distributeby = cxt.distributeby;
    } else {
        cxt.distributeby = stmt->distributeby;
    }

    if (stmt->distributeby != NULL) {
        CheckDistributionSyntax(stmt);
        /* in slice reference case we don't need to check other distribution clauses */
        if (!OidIsValid(stmt->distributeby->referenceoid)) {
            CheckListRangeDistribClause(&cxt, stmt);
        }
    }
#endif
    /*
     * transformIndexConstraints wants cxt.alist to contain only index
     * statements, so transfer anything we already have into saveAlist.
     */
    save_alist = cxt.alist;
    cxt.alist = NIL;

    AssertEreport(stmt->constraints == NIL, MOD_OPT, "");

    /*
     * Postprocess constraints that give rise to index definitions.
     */
    transformIndexConstraints(&cxt);

    /*
     * @hdfs
     * If the table is HDFS foreign table, set internal_flag to true
     * in order to create informational constraint. The primary key and
     * unique informaiotnal constraints do not build a index, but informational
     * constraint is build in DefineIndex function.
     */
    if (cxt.alist != NIL) {
        if (cxt.canInfomationalConstraint)
            setInternalFlagIndexStmt(cxt.alist);
        else
            setMemCheckFlagForIdx(cxt.alist);
    }

    /*
     * Postprocess foreign-key constraints.
     */
    transformFKConstraints(&cxt, true, false);

    /*
     * Check partial cluster key constraints
     */
    checkClusterConstraints(&cxt);

    /*
     * Check reserve column if the table is column store
     */
    if (is_create_as_col_store(stmt)) {
        checkReserveColumn(&cxt);
    }

    /*
     * Output results.
     */
    stmt->tableEltsDup = stmt->tableElts;
    stmt->tableElts = cxt.columns;
    stmt->constraints = cxt.ckconstraints;
    stmt->clusterKeys = cxt.clusterConstraints;
    if (stmt->internalData == NULL) {
        stmt->internalData = cxt.internalData;
    }
    result = lappend(cxt.blist, stmt);
    result = list_concat(result, cxt.alist);
    result = list_concat(result, save_alist);

#ifdef PGXC
    /*
     * If the user did not specify any distribution clause and there is no
     * inherits clause, try and use PK or unique index
     */
#ifdef ENABLE_MOT
    if ((!IsA(stmt, CreateForeignTableStmt) || IsSpecifiedFDW(((CreateForeignTableStmt*)stmt)->servername, MOT_FDW)) &&
        !stmt->distributeby && !stmt->inhRelations && cxt.fallback_dist_col) {
#else
    if (!IsA(stmt, CreateForeignTableStmt) && !stmt->distributeby && !stmt->inhRelations && cxt.fallback_dist_col) {
#endif
        stmt->distributeby = makeNode(DistributeBy);
        stmt->distributeby->disttype = DISTTYPE_HASH;
        stmt->distributeby->colname = cxt.fallback_dist_col;
    }
#endif

    return result;
}

static List* GetAutoIncSeqOptions(CreateStmtContext* cxt)
{
    Node* option = NULL;
    if (cxt->node != NULL && IsA(cxt->node, CreateStmt) && ((CreateStmt*)cxt->node)->autoIncStart != NULL) {
        (void)TransformAutoIncStart((CreateStmt*)cxt->node);
        option = ((CreateStmt*)cxt->node)->autoIncStart;
    } else {
        option = (Node*)makeDefElem("start", (Node*)makeInteger(1));
    }
    return list_make1(option);
}

inline Oid get_rel_colaltion(Relation rel)
{
    if (rel == NULL || rel->rd_options == NULL) {
        return InvalidOid;
    }

    StdRdOptions *opt = (StdRdOptions*)(rel->rd_options);
    return opt->collate;
}

/*
 * createSetOwnedByTable -
 *		create a set owned by table, need to add record to pg_depend.
 */
static void CreateSetOwnedByTable(CreateStmtContext* cxt, ColumnDef* column, char *colname)
{
    Oid snamespaceid;
    char* snamespace = NULL;
    char* setname = NULL;
    CreateSetStmt* setstmt = NULL;

    /*
     * get the table namespace for the set type
     */
    if (cxt->rel)
        snamespaceid = RelationGetNamespace(cxt->rel);
    else {
        snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
        RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid);
    }
    snamespace = get_namespace_name(snamespaceid);
    /*
     * create a new set type named 'relname_colname_oid' in the
     * relation's namespace.
     */
    setname = ChooseRelationName(cxt->relation->relname, colname,
                                 "set", strlen("set"), snamespaceid);

    ereport(NOTICE,
        (errmsg("%s will create implicit set \"%s\" for column \"%s.%s\"",
            cxt->stmtType,
            setname,
            cxt->relation->relname,
            colname)));

    /*
     * Build a CREATE SET command to create the entries in pg_set, and
     * add it to the list of things to be done before this CREATE/ALTER
     * TABLE.
     */
    setstmt = makeNode(CreateSetStmt);
    setstmt->typname = column->typname;

    /* get collation oid in alter table syntax */
    Oid rel_coll_oid = cxt->rel_coll_id;
    Oid coll_oid = get_rel_colaltion(cxt->rel);
    coll_oid = (coll_oid == InvalidOid) ? rel_coll_oid : coll_oid;
    setstmt->set_collation = get_column_def_collation_b_format(column, ANYSETOID, 100, false, coll_oid);

    cxt->blist = lappend(cxt->blist, setstmt);

    /* replace the set column type namespace and name for CreateStmt */
    linitial(column->typname->names) = makeString(snamespace);
    lappend(column->typname->names, makeString(setname));
}

static bool DropSetOwnedByTable(CreateStmtContext* cxt, char *colname)
{
    /* Look up the attribute type id */
    HeapTuple attTuple = SearchSysCache2(ATTNAME, ObjectIdGetDatum(RelationGetRelid(cxt->rel)),
        PointerGetDatum(colname));

    if (!HeapTupleIsValid(attTuple)) {
        /* can not find the columns, take it as not-set type */
        return false;
    }

    Oid atttypid = ((Form_pg_attribute)GETSTRUCT(attTuple))->atttypid;
    ReleaseSysCache(attTuple);

    if (type_is_set(atttypid)) {
        char *nspace = get_typenamespace(atttypid);
        char *typname = get_typename(atttypid);

        DropStmt *stmt = makeNode(DropStmt);
        stmt->removeType = OBJECT_TYPE;
        stmt->objects = list_make1(list_make1(makeTypeNameFromNameList(list_make2(makeString(nspace), makeString(typname)))));
        stmt->behavior = DROP_CASCADE;
        stmt->arguments = NIL;
        stmt->missing_ok = true;
        stmt->purge = false;
        stmt->concurrent = false;
        stmt->isProcedure = false;
        cxt->alist = lappend(cxt->alist, stmt);
        return true;
    }

    return false;
}

static void checkSeqInAlterStmt(CreateStmtContext* cxt)
{
    Relation attRelation;
    Relation rel;
    SysScanDesc scan;
    ScanKeyData key[1];
    HeapTuple tuple;
    Form_pg_attribute attrForm;
    char* columnName;
    FunctionCallInfoData funcinfo;
    char* sequenceName;
    Datum re_seq;
    Datum DaRel;
    bool isdropped = false;

    InitFunctionCallInfoData(funcinfo, NULL, 2, InvalidOid, NULL, NULL);
    attRelation = heap_open(AttributeRelationId, AccessShareLock);

    rel = heap_openrv(cxt->relation, AccessShareLock);

    DaRel = ObjectIdGetDatum(RelationGetRelid(rel));
    ScanKeyInit(&key[0], Anum_pg_attribute_attrelid, BTEqualStrategyNumber, F_OIDEQ, DaRel);

    scan = systable_beginscan(attRelation, AttributeRelidNumIndexId, true, NULL, 1, key);
    while ((tuple = systable_getnext(scan)) != NULL) {
        attrForm = (Form_pg_attribute) GETSTRUCT(tuple);
        columnName = NameStr(attrForm->attname);
        isdropped = attrForm->attisdropped;
        if (isdropped) {
            continue;
        }
        funcinfo.arg[0] = CStringGetTextDatum(cxt->relation->relname);
        funcinfo.arg[1] = CStringGetTextDatum(columnName);
        re_seq = pg_get_serial_sequence(&funcinfo);
        if (re_seq != NULL) {
            sequenceName = TextDatumGetCString(re_seq);
        } else {
            continue;
        }

        if (sequenceName != NULL && StrEndWith(sequenceName, "_seq_identity"))
            ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                                   errmsg("multiple identity specifications for column \"%s\" of table \"%s\"",
                                   columnName, cxt->relation->relname),
                                   parser_errposition(cxt->pstate, -1)));
    }
    systable_endscan(scan);
    heap_close(rel, AccessShareLock);
    heap_close(attRelation, AccessShareLock);
}

/*
 * createSeqOwnedByTable -
 *		create a sequence owned by table, need to add record to pg_depend.
 *		used in CREATE TABLE and CREATE TABLE ... LIKE
 */
static void createSeqOwnedByTable(CreateStmtContext* cxt, ColumnDef* column, bool preCheck, bool large,
    bool isAutoinc, bool forIdentity = false, List *seqOptions = nullptr)
{
    Oid snamespaceid;
    char* snamespace = NULL;
    char* sname = NULL;
    char* qstring = NULL;
    A_Const* snamenode = NULL;
    TypeCast* castnode = NULL;
    FuncCall* funccallnode = NULL;
    AutoIncrement* autoincnode = NULL;
    CreateSeqStmt* seqstmt = NULL;
    AlterSeqStmt* altseqstmt = NULL;
    List* attnamelist = NIL;
    Constraint* constraint = NULL;
    Oid typeOid = InvalidOid;

    if (column->typname) {
        typeOid = typenameTypeId(NULL, column->typname);
    }

    if (forIdentity && nodeTag(cxt->node) == T_AlterTableStmt) {
        AlterTableStmt *atstmt = (AlterTableStmt *)cxt->node;
        ListCell *lcmd = NULL;
        foreach(lcmd, atstmt->cmds) {
            AlterTableCmd* cmd = (AlterTableCmd*)lfirst(lcmd);

            switch (cmd->subtype) {
                case AT_DropPartition:
                case AT_DropSubPartition:
                case AT_TruncatePartition:
                case AT_ExchangePartition:
                case AT_TruncateSubPartition:
                case AT_DropColumn:
                    break;
                default:
                    checkSeqInAlterStmt(cxt);
                    break;
            }
        }
    }

    /*
     * Determine namespace and name to use for the sequence.
     *
     * Although we use ChooseRelationName, it's not guaranteed that the
     * selected sequence name won't conflict; given sufficiently long
     * field names, two different serial columns in the same table could
     * be assigned the same sequence name, and we'd not notice since we
     * aren't creating the sequence quite yet.	In practice this seems
     * quite unlikely to be a problem, especially since few people would
     * need two serial columns in one table.
     */
    if (cxt->rel)
        snamespaceid = RelationGetNamespace(cxt->rel);
    else {
        snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
        RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid);
    }
    snamespace = get_namespace_name(snamespaceid);

    if (forIdentity) {
        sname = ChooseRelationName(cxt->relation->relname,
                                   column->colname,
                                   "seq_identity",
                                   strlen("seq_identity"),
                                   snamespaceid);
    } else {
        sname = ChooseRelationName(cxt->relation->relname, column->colname, "seq", strlen("seq"), snamespaceid);
    }

    if (!preCheck || IS_SINGLE_NODE)
        ereport(NOTICE,
            (errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"",
                cxt->stmtType,
                sname,
                cxt->relation->relname,
                column->colname)));

    /*
     * Build a CREATE SEQUENCE command to create the sequence object, and
     * add it to the list of things to be done before this CREATE/ALTER
     * TABLE.
     */
    seqstmt = makeNode(CreateSeqStmt);
    seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
    seqstmt->options = seqOptions;
    seqstmt->is_autoinc = isAutoinc;
#ifdef PGXC
    seqstmt->is_serial = true;
#endif
    seqstmt->is_large = large;

    if (forIdentity) {
        int typmod = 0;
        if (column->typname->typmods != NULL) {
            typmod = intVal(&((A_Const*)lfirst(list_head(column->typname->typmods)))->val);
            seqstmt->options = lcons(makeDefElem("as", (Node *)makeTypeNameFromOid(typeOid, typmod)), seqstmt->options);
        } else {
            seqstmt->options = lcons(makeDefElem("as", (Node *)makeTypeNameFromOid(typeOid, -1)), seqstmt->options);
        }
    } else if (!large) {
        seqstmt->options = list_make1(makeDefElem("as", (Node *)makeTypeNameFromOid(typeOid, -1)));
    } else {
        seqstmt->options = isAutoinc? GetAutoIncSeqOptions(cxt) : NULL;
    }

    /* Assign UUID for create sequence */
    seqstmt->uuid = INVALIDSEQUUID;
#ifdef ENABLE_MUTIPLE_NODES
    if (!IS_SINGLE_NODE)
        seqstmt->uuid = gen_uuid(cxt->uuids);
#endif

    /*
     * If this is ALTER ADD COLUMN, make sure the sequence will be owned
     * by the table's owner.  The current user might be someone else
     * (perhaps a superuser, or someone who's only a member of the owning
     * role), but the SEQUENCE OWNED BY mechanisms will bleat unless table
     * and sequence have exactly the same owning role.
     */
    if (cxt->rel)
        seqstmt->ownerId = cxt->rel->rd_rel->relowner;
    else
        seqstmt->ownerId = InvalidOid;

    /*
     * When under analyzing, we may create temp sequence which has serial column,
     * but we cannot create temp sequence for now. Besides, create temp table (like t)
     * can be successfully created, but it should not happen. So here we set canCreateTempSeq
     * to true to handle this two cases.
     */
    if (u_sess->analyze_cxt.is_under_analyze || u_sess->attr.attr_common.enable_beta_features) {
        seqstmt->canCreateTempSeq = true;
    }

    cxt->blist = lappend(cxt->blist, seqstmt);

    /*
     * Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence
     * as owned by this column, and add it to the list of things to be
     * done after this CREATE/ALTER TABLE.
     */
    altseqstmt = makeNode(AlterSeqStmt);
    altseqstmt->sequence = makeRangeVar(snamespace, sname, -1);
#ifdef PGXC
    altseqstmt->is_serial = true;
#endif
    attnamelist = list_make3(makeString(snamespace), makeString(cxt->relation->relname), makeString(column->colname));
    altseqstmt->options = list_make1(makeDefElem("owned_by", (Node*)attnamelist));
    altseqstmt->is_large = large;
    altseqstmt->is_autoinc = isAutoinc;

    cxt->alist = lappend(cxt->alist, altseqstmt);

    /*
     * Create appropriate constraints for SERIAL.  We do this in full,
     * rather than shortcutting, so that we will detect any conflicting
     * constraints the user wrote (like a different DEFAULT).
     *
     * Create an expression tree representing the function call
     * nextval('sequencename').  We cannot reduce the raw tree to cooked
     * form until after the sequence is created, but there's no need to do
     * so.
     */
    qstring = quote_qualified_identifier(snamespace, sname);
    snamenode = makeNode(A_Const);
    snamenode->val.type = T_String;
    snamenode->val.val.str = qstring;
    snamenode->location = -1;
    castnode = makeNode(TypeCast);
    castnode->typname = (TypeName*)SystemTypeName("regclass");
    castnode->arg = (Node*)snamenode;
    castnode->location = -1;
    funccallnode = makeNode(FuncCall);
    funccallnode->funcname = SystemFuncName("nextval");
    funccallnode->args = list_make1(castnode);
    funccallnode->agg_order = NIL;
    funccallnode->agg_filter = NULL;
    funccallnode->agg_star = false;
    funccallnode->agg_distinct = false;
    funccallnode->func_variadic = false;
    funccallnode->over = NULL;
    funccallnode->location = -1;

    constraint = makeNode(Constraint);
    constraint->contype = CONSTR_DEFAULT;
    constraint->location = -1;
    if (isAutoinc) {
        autoincnode = makeNode(AutoIncrement);
        autoincnode->expr = (Node*)funccallnode;
        constraint->raw_expr = (Node*)autoincnode;
    } else {
        constraint->raw_expr = (Node*)funccallnode;
    }
    constraint->cooked_expr = NULL;
    column->constraints = lappend(column->constraints, constraint);
    column->raw_default = constraint->raw_expr;

    if (!isAutoinc) {
        constraint = makeNode(Constraint);
        constraint->contype = CONSTR_NOTNULL;
        constraint->location = -1;
        column->constraints = lappend(column->constraints, constraint);
    }
}

static bool isColumnEncryptionAllowed(CreateStmtContext *cxt, ColumnDef *column)
{
    if (column->is_serial) {
        ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("encrypted serial column is not implemented"),
            parser_errposition(cxt->pstate, column->typname->location)));
        return false;
    }
    return true;
}

static void AutoIncrementCheckOrientation(CreateStmtContext *cxt)
{
    const char *storeChar = IsA(cxt->node, CreateStmt) ?
        CreatestmtGetOrientation((CreateStmt*)cxt->node) :
        RelationGetOrientation(cxt->rel);
    if (pg_strcasecmp(storeChar, ORIENTATION_ROW) != 0) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-supported feature"),
                    errdetail("Orientation type %s is not supported for auto_increment", storeChar)));
    }
}

/*
 * precheckColumnTypeForSet -
 *  not support empty set type and set array.
 */
static void PrecheckColumnTypeForSet(CreateStmtContext* cxt, TypeName *typname)
{
    if (typname->arrayBounds != NIL) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("array of set is not implemented"),
                parser_errposition(cxt->pstate, typname->location)));
    }

    List *typmods = typname->typmods;
    if (typmods == NULL) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("It's not supported to create empty set column"),
                parser_errposition(cxt->pstate, typname->location)));
    }
}

/*
 * transformColumnDefinition -
 *		transform a single ColumnDef within CREATE TABLE
 *		Also used in ALTER TABLE ADD COLUMN
 */
static void transformColumnDefinition(CreateStmtContext* cxt, ColumnDef* column, bool preCheck)
{
    bool is_serial = false;
    bool is_set = false;
    bool large = false;
    ClientLogicColumnRef* clientLogicColumnRef = NULL;

    /* Check the constraint type.*/
    checkConstraint(cxt, (Node*)column);

    cxt->columns = lappend(cxt->columns, (Node*)column);

    /* Check for SERIAL pseudo-types */
    is_serial = false;
    if (column->typname && list_length(column->typname->names) == 1 && !column->typname->pct_type) {
        char* typname = strVal(linitial(column->typname->names));

        if (strcmp(typname, "smallserial") == 0 || strcmp(typname, "serial2") == 0) {
            is_serial = true;
            column->typname->names = NIL;
            column->typname->typeOid = INT2OID;
        } else if (strcmp(typname, "serial") == 0 || strcmp(typname, "serial4") == 0) {
            is_serial = true;
            column->typname->names = NIL;
            column->typname->typeOid = INT4OID;
        } else if (strcmp(typname, "bigserial") == 0 || strcmp(typname, "serial8") == 0) {
            is_serial = true;
            column->typname->names = NIL;
            column->typname->typeOid = INT8OID;
        } else if (strcmp(typname, "largeserial") == 0 || strcmp(typname, "serial16") == 0) {
#ifdef ENABLE_MULTIPLE_NODES
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("Pldebug is not supported for distribute currently.")));
#endif
            is_serial = true;
            column->typname->names = NIL;
            column->typname->typeOid = NUMERICOID;
            large = true;
#ifdef ENABLE_MULTIPLE_NODES
        } else if ((is_enc_type(typname) && IS_MAIN_COORDINATOR) ||
#else
        } else if (is_enc_type(typname) ||
#endif
            (!u_sess->attr.attr_common.enable_beta_features && strcmp(typname, "int16") == 0)) {
            ereport(ERROR,
                (errcode(ERRCODE_OPERATE_NOT_SUPPORTED),
                    errmsg("It's not supported to create %s column", typname)));
        }

        if (is_serial) {
            /*
             * We have to reject "serial[]" explicitly, because once we've set
             * typeid, LookupTypeName won't notice arrayBounds.  We don't need any
             * special coding for serial(typmod) though.
             */
            if (column->typname->arrayBounds != NIL)
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("array of serial is not implemented"),
                        parser_errposition(cxt->pstate, column->typname->location)));

            if (cxt->relation && cxt->relation->relpersistence == RELPERSISTENCE_TEMP)
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("It's not supported to create serial column on temporary table")));

            if (0 == pg_strncasecmp(cxt->stmtType, ALTER_TABLE, strlen(cxt->stmtType)))
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("It's not supported to alter table add serial column")));
        }
    }

    if (column->typname && column->typname->names && !column->typname->pct_type) {
        char* typname = strVal(llast(column->typname->names));
        if (strcmp(typname, "set") == 0) {
            PrecheckColumnTypeForSet(cxt, column->typname);

            is_set = true;
            column->typname->typeOid = InvalidOid; // pg_attribute.atttypid
            CreateSetOwnedByTable(cxt, column, column->colname);
        }
    }

    /* Do necessary work on the column type declaration. But for set type,
     * no need to check before because the type has not created yet.
     */
    if (!is_set && column->typname)
        transformColumnType(cxt, column);

    /* Special actions for SERIAL pseudo-types */
    column->is_serial = is_serial;
    if (is_serial) {
        createSeqOwnedByTable(cxt, column, preCheck, large, false);
    }

    /* Process column constraints, if any... */
    transformConstraintAttrs(cxt, column->constraints);

    TransformColumnDefinitionConstraints(cxt, column, preCheck, false);
    if (column->clientLogicColumnRef != NULL) {
#ifdef ENABLE_MULTIPLE_NODES
        if (IS_MAIN_COORDINATOR && !u_sess->attr.attr_common.enable_full_encryption) {
#else
        if (!u_sess->attr.attr_common.enable_full_encryption && t_thrd.role != SW_SENDER) {
#endif
            ereport(ERROR,
                (errcode(ERRCODE_OPERATE_NOT_SUPPORTED),
                    errmsg("Un-support to define encrypted column when client encryption is disabled.")));
        }
        if (isColumnEncryptionAllowed(cxt, column)) {
            clientLogicColumnRef = (ClientLogicColumnRef*)column->clientLogicColumnRef;
            clientLogicColumnRef->orig_typname = column->typname;
            typenameTypeIdAndMod(NULL, clientLogicColumnRef->orig_typname, &clientLogicColumnRef->orig_typname->typeOid, &clientLogicColumnRef->orig_typname->typemod);
                if (clientLogicColumnRef->dest_typname) {
                    typenameTypeIdAndMod(NULL, clientLogicColumnRef->dest_typname, &clientLogicColumnRef->dest_typname->typeOid, &clientLogicColumnRef->dest_typname->typemod);
                    column->typname =   makeTypeNameFromOid(clientLogicColumnRef->dest_typname->typeOid, clientLogicColumnRef->orig_typname->typeOid);
                }
            transformColumnType(cxt, column);
        }
    }
 
    /*
     * Generate ALTER FOREIGN TABLE ALTER COLUMN statement which adds
     * per-column foreign data wrapper options for this column.
     */
    if (column->fdwoptions != NIL) {
        AlterTableStmt* stmt = NULL;
        AlterTableCmd* cmd = NULL;

        cmd = makeNode(AlterTableCmd);
        cmd->subtype = AT_AlterColumnGenericOptions;
        cmd->name = column->colname;
        cmd->def = (Node*)column->fdwoptions;
        cmd->behavior = DROP_RESTRICT;
        cmd->missing_ok = false;

        stmt = makeNode(AlterTableStmt);
        stmt->relation = cxt->relation;
        stmt->cmds = NIL;
        stmt->relkind = OBJECT_FOREIGN_TABLE;
        stmt->cmds = lappend(stmt->cmds, cmd);

        cxt->alist = lappend(cxt->alist, stmt);
    }

    TransformColumnDefinitionOptions(cxt, column);
}

static void TransformColumnDefinitionOptions(CreateStmtContext* cxt, ColumnDef* column)
{
    ListCell *columnOption = NULL;
    foreach (columnOption, column->columnOptions) {
        void *pointer = lfirst(columnOption);
        if (IsA(pointer, CommentStmt)) {
            CommentStmt *commentStmt = (CommentStmt *)pointer;
            commentStmt->objtype = OBJECT_COLUMN;
            commentStmt->objname = list_make2(makeString(cxt->relation->relname), makeString(column->colname));
            if (cxt->relation->schemaname) {
                commentStmt->objname = lcons(makeString(cxt->relation->schemaname) , commentStmt->objname);
            }
            cxt->alist = lappend(cxt->alist, commentStmt);
            break;
        }
    }
}

static void TransformColumnDefinitionConstraints(CreateStmtContext* cxt, ColumnDef* column,
    bool preCheck, bool is_modify)
{
    bool saw_nullable = false;
    bool saw_default = false;
    bool saw_generated = false;
    Constraint* constraint = NULL;
    ListCell* clist = NULL;
    bool sawIdentity = false;

    foreach (clist, column->constraints) {
        constraint = (Constraint*)lfirst(clist);

        switch (constraint->contype) {
            case CONSTR_NULL:
                if (saw_nullable && column->is_not_null)
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
                                column->colname,
                                cxt->relation->relname),
                            parser_errposition(cxt->pstate, constraint->location)));
                column->is_not_null = FALSE;
                saw_nullable = true;
                break;

            case CONSTR_NOTNULL:
                if (saw_nullable && !column->is_not_null)
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
                                column->colname,
                                cxt->relation->relname),
                            parser_errposition(cxt->pstate, constraint->location)));
                column->is_not_null = TRUE;
                saw_nullable = true;
                break;

            case CONSTR_DEFAULT:
                if (saw_default && constraint->update_expr == NULL)
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("multiple default values specified for column \"%s\" of table \"%s\"",
                                column->colname,
                                cxt->relation->relname),
                            parser_errposition(cxt->pstate, constraint->location)));
                if (!saw_default) {
                    column->raw_default = constraint->raw_expr;
                }
                if (constraint->update_expr != NULL) {
                    column->update_default = constraint->update_expr;
                }
                AssertEreport(constraint->cooked_expr == NULL, MOD_OPT, "");
                saw_default = true;
                break;

            case CONSTR_IDENTITY:
                {
                    bool large = typenameTypeId(NULL, column->typname) == NUMERICOID;

                    if (cxt->ofType) {
                        ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                           errmsg("identity columns are not supported on typed tables")));
                    }
                    if (sawIdentity) {
                        ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                                       errmsg("multiple identity specifications for column \"%s\" of table \"%s\"",
                                       column->colname, cxt->relation->relname),
                                       parser_errposition(cxt->pstate, constraint->location)));
                    }
                    if (saw_default)
                        ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                                       errmsg("Defaults cannot be created on columns with an IDENTITY attribute."
                                       "Table '%s', column '%s'",
                                       cxt->relation->relname, column->colname),
                                       parser_errposition(cxt->pstate, constraint->location)));

                    identity_type_dmod(column);
                    createSeqOwnedByTable(cxt, column, preCheck, large, false, true, constraint->options);
                    sawIdentity = true;
                    column->is_not_null = true;
                    break;
                }

            case CONSTR_GENERATED:
                if (cxt->ofType) {
                    ereport(ERROR, (errmodule(MOD_GEN_COL), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("generated columns are not supported on typed tables")));
                }
                if (saw_generated) {
                    ereport(ERROR, (errmodule(MOD_GEN_COL), errcode(ERRCODE_SYNTAX_ERROR),
                        errmsg("multiple generation clauses specified for column \"%s\" of table \"%s\"",
                        column->colname, cxt->relation->relname),
                        parser_errposition(cxt->pstate, constraint->location)));
                }
                column->generatedCol = ATTRIBUTE_GENERATED_STORED;
                if (constraint->generated_when == ATTRIBUTE_GENERATED_PERSISTED) {
                    column->generatedCol = ATTRIBUTE_GENERATED_PERSISTED;
                }
                column->raw_default = constraint->raw_expr;
                Assert(constraint->cooked_expr == NULL);
                saw_generated = true;
                break;

            case CONSTR_CHECK:
                cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
                break;

            case CONSTR_PRIMARY:
            case CONSTR_UNIQUE:
                if (constraint->keys == NIL) {
                    IndexElem *elem = makeNode(IndexElem);
                    elem->name = pstrdup(column->colname);
                    elem->expr = NULL;
                    elem->indexcolname = NULL;
                    elem->collation = NIL;
                    elem->opclass = NIL;
                    elem->ordering = SORTBY_DEFAULT;
                    elem->nulls_ordering = SORTBY_NULLS_DEFAULT;
                    constraint->keys = list_make1((Node *)elem);
                }
                cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
                break;

            case CONSTR_EXCLUSION:
                /* grammar does not allow EXCLUDE as a column constraint */
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("column exclusion constraints are not supported")));
                break;

            case CONSTR_FOREIGN:
                if (!is_modify) {
                    /*
                     * Fill in the current attribute's name and throw it into the
                     * list of FK constraints to be processed later.
                     */
                    constraint->fk_attrs = list_make1(makeString(column->colname));
                    cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
                } else {
                    ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION),
                        errmsg("Invalid modify column operation"),
                        errdetail("modify or change column REFERENCES constraint is not supported")));
                }
                break;

            case CONSTR_ATTR_DEFERRABLE:
            case CONSTR_ATTR_NOT_DEFERRABLE:
            case CONSTR_ATTR_DEFERRED:
            case CONSTR_ATTR_IMMEDIATE:
                /* transformConstraintAttrs took care of these */
                break;
            case CONSTR_AUTO_INCREMENT:
                if (IsA(cxt->node, CreateForeignTableStmt) ||
                    (cxt->rel != NULL && (IS_FOREIGNTABLE(cxt->rel) || IS_STREAM_TABLE(cxt->rel)))) {
                    ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("Un-support feature"),
                        errdetail("auto_increment column is not supported in foreign table")));
                }
                if (column->is_serial) {
                    ereport(ERROR, (errcode(ERRCODE_OPERATE_NOT_SUPPORTED),
                        errmsg("The datatype of column '%s' does not support auto_increment", column->colname)));
                }
                if (saw_default) {
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("multiple default values specified for column \"%s\" of table \"%s\"",
                                column->colname,
                                cxt->relation->relname),
                            parser_errposition(cxt->pstate, constraint->location)));
                }
                AutoIncrementCheckOrientation(cxt);
                if (cxt->relation->relpersistence == RELPERSISTENCE_TEMP) {
                    TransformTempAutoIncrement(column, IsA(cxt->node, CreateStmt) ? (CreateStmt*)cxt->node: NULL);
                } else {
                    createSeqOwnedByTable(cxt, column, preCheck, true, true);
                    column->is_serial = true;
                }
                column->is_not_null = true;
                break;
            default:
                ereport(ERROR,
                    (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                        errmsg("unrecognized constraint type: %d", constraint->contype)));
                break;
        }
    }

    if (saw_default && saw_generated)
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("both default and generation expression specified for column \"%s\" of table \"%s\"",
                            column->colname, cxt->relation->relname),
                     parser_errposition(cxt->pstate, constraint->location)));
}

/*
 * transformTableConstraint
 *		transform a Constraint node within CREATE TABLE or ALTER TABLE
 */
static void transformTableConstraint(CreateStmtContext* cxt, Constraint* constraint)
{
    switch (constraint->contype) {
        case CONSTR_PRIMARY:
        case CONSTR_UNIQUE:
        case CONSTR_EXCLUSION:
            cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
            break;

        case CONSTR_CHECK:
            cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
            break;

        case CONSTR_CLUSTER:
            cxt->clusterConstraints = lappend(cxt->clusterConstraints, constraint);
            break;

        case CONSTR_FOREIGN:
            cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
            break;

        case CONSTR_NULL:
        case CONSTR_NOTNULL:
        case CONSTR_DEFAULT:
        case CONSTR_GENERATED:
        case CONSTR_ATTR_DEFERRABLE:
        case CONSTR_ATTR_NOT_DEFERRABLE:
        case CONSTR_ATTR_DEFERRED:
        case CONSTR_ATTR_IMMEDIATE:
        case CONSTR_AUTO_INCREMENT:
            ereport(ERROR,
                (errcode(ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION),
                    errmsg("invalid context for constraint type %d", constraint->contype)));
            break;

        default:
            ereport(ERROR,
                (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                    errmsg("unrecognized constraint type: %d", constraint->contype)));
            break;
    }

    /* Check the constraint type. */
    checkConstraint(cxt, (Node*)constraint);
}

/*
 * searchSeqidFromExpr
 *
 * search default expression for sequence oid.
 */
Oid searchSeqidFromExpr(Node* cooked_default)
{
    Const* first_arg = NULL;
    FuncExpr* nextval_expr = NULL;

    if (IsA(cooked_default, FuncExpr)) {
        if (((FuncExpr*)cooked_default)->funcid == NEXTVALFUNCOID) {
            nextval_expr = (FuncExpr*)cooked_default;
        } else {
            List* args = ((FuncExpr*)cooked_default)->args;
            if (args != NULL) {
                Node* nextval = (Node*)linitial(args);
                if (IsA(nextval, FuncExpr) && ((FuncExpr*)nextval)->funcid == NEXTVALFUNCOID) {
                    nextval_expr = (FuncExpr*)nextval;
                }
            }
        }
    }
    if (nextval_expr == NULL)
        return InvalidOid;

    first_arg = (Const*)linitial(nextval_expr->args);
    Assert(IsA(first_arg, Const));

    return DatumGetObjectId(first_arg->constvalue);
}

/*
 * checkTableLikeSequence
 *
 * Analyze default expression of table column, if default is nextval function,
 * it means the first argument of nextval function is sequence, we need to
 * check whether the sequence exists in current datanode.
 * We check sequence oid only because stringToNode in transformTableLikeFromSerialData
 * has already checked sequence name (see _readFuncExpr in readfuncs.cpp).
 * Suppose create a table like this:  CREATE TABLE t1 (id serial, a int) TO NODE GROUP ng1;
 * a sequence named t1_id_seq will be created and the sequence exists in NodeGroup ng1.
 * If create table like t1 in another NodeGroup ng2, error will be reported because t1_id_seq
 * does not exists some datanodes of NodeGroup ng2.
 */

static void checkTableLikeSequence(Node* cooked_default)
{
    char* seq_name = NULL;
    Oid seqId = searchSeqidFromExpr(cooked_default);
    if (!OidIsValid(seqId))
        return;

    seq_name = get_rel_name(seqId);
    if (seq_name == NULL) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("CREATE TABLE LIKE with column sequence "
                       "in different NodeGroup is not supported."),
                errdetail("Recommend to LIKE table with sequence in installation NodeGroup.")));
    }      
    pfree_ext(seq_name);
}

/*
 * transformTableLikeFromSerialData
 *
 * Get meta info of a table from serialized data, the serialized data come from CN.
 * The function is used for CREATE TABLE ... LIKE across node group.
 */
static void transformTableLikeFromSerialData(CreateStmtContext* cxt, TableLikeClause* table_like_clause)
{
    ListCell* cell = NULL;
    TableLikeCtx* meta_info = NULL;
    meta_info = (TableLikeCtx*)stringToNode(cxt->internalData);

    table_like_clause->options = meta_info->options;
    cxt->hasoids = meta_info->hasoids;
    cxt->columns = meta_info->columns;
    cxt->csc_partTableState = meta_info->partition;
    cxt->inh_indexes = meta_info->inh_indexes;
    cxt->clusterConstraints = meta_info->cluster_keys;
    cxt->ckconstraints = meta_info->ckconstraints;
    cxt->alist = meta_info->comments;
    cxt->reloptions = meta_info->reloptions;

    if (meta_info->temp_table) {
        table_like_clause->relation->relpersistence = RELPERSISTENCE_TEMP;
        ExecSetTempObjectIncluded();
    }
    /* Special actions for SERIAL pseudo-types */
    foreach (cell, cxt->columns) {
        ColumnDef* column = (ColumnDef*)lfirst(cell);
        if (column->is_serial) {
            bool large = (column->typname->typeOid == NUMERICOID);
            createSeqOwnedByTable(cxt, column, false, large, false);
        } else if (column->cooked_default != NULL) {
            checkTableLikeSequence(column->cooked_default);
        }
    }
}

/*
 * Get all tag for tsdb
 */

static DistributeBy* GetHideTagDistribution(TupleDesc tupleDesc)
{
    DistributeBy* distributeby = makeNode(DistributeBy);
    distributeby->disttype = DISTTYPE_HASH;
    for (int attno = 1; attno <= tupleDesc->natts; attno++) {
        Form_pg_attribute attribute = &tupleDesc->attrs[attno - 1];
        char* attributeName = NameStr(attribute->attname);
        if (attribute->attkvtype == ATT_KV_TAG) {
            distributeby->colname = lappend(distributeby->colname, makeString(attributeName));
        }
    }
    return distributeby;
}

/*
 * Support Create table like on table with subpartitions
 * In transform phase, we need fill PartitionState
 * 1. Recursively fill partitionList in PartitionState also including subpartitionList
 * 2. Recursively fill PartitionState also including SubPartitionState
 */
static PartitionState *transformTblSubpartition(Relation relation, HeapTuple partitionTuple,
                                    List* partitionList, List* subPartitionList)
{
    ListCell *lc1 = NULL;
    ListCell *lc2 = NULL;
    ListCell *lc3 = NULL;

    List *partKeyColumns = NIL;
    List *partitionDefinitions = NIL;
    PartitionState *partState = NULL;
    PartitionState *subPartState = NULL;
    Form_pg_partition tupleForm = NULL;
    Form_pg_partition partitionForm = NULL;
    Form_pg_partition subPartitionForm = NULL;

    tupleForm = (Form_pg_partition)GETSTRUCT(partitionTuple);

    /* prepare partition definitions */
    transformTableLikePartitionProperty(
        relation, partitionTuple, &partKeyColumns, partitionList, &partitionDefinitions);

    partState = makeNode(PartitionState);
    partState->partitionKey = partKeyColumns;
    partState->partitionList = partitionDefinitions;
    partState->partitionStrategy = tupleForm->partstrategy;

    partState->rowMovement = relation->rd_rel->relrowmovement ? ROWMOVEMENT_ENABLE : ROWMOVEMENT_DISABLE;

    /* prepare subpartition definitions */
    forboth(lc1, partitionList, lc2, subPartitionList) {
        List *subPartKeyColumns = NIL;
        List *subPartitionDefinitions = NIL;
        RangePartitionDefState *partitionDef = NULL;

        HeapTuple partTuple = (HeapTuple)lfirst(lc1);
        List *subPartitions = (List *)lfirst(lc2);

        HeapTuple subPartTuple = (HeapTuple)linitial(subPartitions);
        subPartitionForm = (Form_pg_partition)GETSTRUCT(subPartTuple);
        partitionForm = (Form_pg_partition)GETSTRUCT(partTuple);

        if (subPartitionForm->partstrategy != PART_STRATEGY_RANGE) {
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("Create Table like with subpartition only support range strategy.")));
        }

        Oid partOid = HeapTupleGetOid(partTuple);
        Partition part = partitionOpen(relation, partOid, AccessShareLock);
        Relation partRel = partitionGetRelation(relation, part);

        transformTableLikePartitionProperty(
            partRel, partTuple, &subPartKeyColumns, subPartitions, &subPartitionDefinitions);

        if (subPartState == NULL) {
            subPartState = makeNode(PartitionState);
            subPartState->partitionKey = subPartKeyColumns;
            subPartState->partitionList = NULL;
            subPartState->partitionStrategy = subPartitionForm->partstrategy;
        }

        /* Here do this for reserve origin subpartitions order */
        foreach(lc3, partitionDefinitions) {
            RangePartitionDefState *rightDef = (RangePartitionDefState*)lfirst(lc3);

            if (pg_strcasecmp(NameStr(partitionForm->relname), rightDef->partitionName) == 0) {
                partitionDef = rightDef;
                break;
            }
        }

        Assert(partitionDef != NULL);
        partitionDef->subPartitionDefState = subPartitionDefinitions;

        releaseDummyRelation(&partRel);
        partitionClose(relation, part, NoLock);
    }

    partState->subPartitionState = subPartState;

    return partState;
}

/*
 * transformTableLikeClause
 *
 * Change the LIKE <srctable> portion of a CREATE TABLE statement into
 * column definitions which recreate the user defined column portions of
 * <srctable>.
 */
static void transformTableLikeClause(
    CreateStmtContext* cxt, TableLikeClause* table_like_clause, bool preCheck, bool isFirstNode, bool is_row_table, TransformTableType transformType)
{
    AttrNumber parent_attno;
    Relation relation;
    TupleDesc tupleDesc;
    TupleConstr* constr = NULL;
    AttrNumber* attmap = NULL;
    AclResult aclresult;
    char* comment = NULL;
    ParseCallbackState pcbstate;
    TableLikeCtx meta_info;
    bool multi_nodegroup = false;
    errno_t rc;

    setup_parser_errposition_callback(&pcbstate, cxt->pstate, table_like_clause->relation->location);

    /*
     * We may run into a case where LIKE clause happens between two tables with different
     * node groups, we don't check validation in coordinator nodes as in cluster expansion
     * scenarios we first dump/restore table's metadata in new added DNs without sync
     * pgxc_class, then invoke LIKE command. So we have to allow a case where source table's
     * nodegroup fully include target table's
     */
    if (IS_PGXC_DATANODE) {
        RangeVar* relvar = table_like_clause->relation;
        Oid relid = RangeVarGetRelidExtended(relvar, NoLock, true, false, false, true, NULL, NULL);
        if (relid == InvalidOid) {
            if (cxt->internalData != NULL) {
                cancel_parser_errposition_callback(&pcbstate);
                transformTableLikeFromSerialData(cxt, table_like_clause);
                return;
            }

            ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_TABLE),
                    errmsg("Table %s.%s does not exist in current datanode.", relvar->schemaname, relvar->relname)));
        }
    }

    relation = relation_openrv_extended(table_like_clause->relation, AccessShareLock, false, true);

    if (relation->rd_rel->relkind != RELKIND_RELATION &&
        relation->rd_rel->relkind != RELKIND_VIEW &&
        relation->rd_rel->relkind != RELKIND_CONTQUERY &&
        relation->rd_rel->relkind != RELKIND_MATVIEW &&
        relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
        relation->rd_rel->relkind != RELKIND_STREAM &&
        relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
        ereport(ERROR,
            (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                errmsg("\"%s\" is not a table, view, composite type, or foreign table",
                    RelationGetRelationName(relation))));

    cancel_parser_errposition_callback(&pcbstate);

    // If specify 'INCLUDING ALL' for non-partitioned table, just remove the option 'INCLUDING PARTITION'.
    // Right shift 10 bits can handle both 'INCLUDING ALL' and 'INCLUDING ALL EXCLUDING option(s)'.
    // if add a new option, the number '10'(see marco 'MAX_TABLE_LIKE_OPTIONS') should be changed.
    if ((table_like_clause->options >> MAX_TABLE_LIKE_OPTIONS) && !RELATION_IS_PARTITIONED(relation) &&
        !RelationIsValuePartitioned(relation))
        table_like_clause->options = table_like_clause->options & ~CREATE_TABLE_LIKE_PARTITION;

    if (table_like_clause->options & CREATE_TABLE_LIKE_PARTITION) {
        if (RELATION_ISNOT_REGULAR_PARTITIONED(relation)) {
            ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                    errmsg("could not specify \"INCLUDING PARTITION\" for non-partitioned-table relation:\"%s\"",
                        RelationGetRelationName(relation))));
        }
        if (cxt->csc_partTableState != NULL) {
            ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                    errmsg("could not specify 2 or more \"INCLUDING PARTITION\" clauses, only one is allowed")));
        }
    }

    if (table_like_clause->options & CREATE_TABLE_LIKE_RELOPTIONS) {
        if (cxt->reloptions != NULL) {
            ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                    errmsg("could not specify 2 or more \"INCLUDING RELOPTIONS\" clauses, only one is allowed")));
        }
    }

    if (table_like_clause->options & CREATE_TABLE_LIKE_DISTRIBUTION) {
        if (cxt->distributeby != NULL) {
            ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                    errmsg("could not specify 2 or more \"INCLUDING DISTRIBUTION\" clauses, only one is allowed")));
        }
    }

    /* Initialize meta_info struct. */
    rc = memset_s(&meta_info, sizeof(meta_info), 0, sizeof(meta_info));
    securec_check_ss(rc, "\0", "\0");

#ifdef PGXC
    /*
     * Check if relation is temporary and assign correct flag.
     * This will override transaction direct commit as no 2PC
     * can be used for transactions involving temporary objects.
     */
    if (IsTempTable(RelationGetRelid(relation))) {
        table_like_clause->relation->relpersistence = RELPERSISTENCE_TEMP;
        ExecSetTempObjectIncluded();
        meta_info.temp_table = true;
    }

    /*
     * Block the creation of tables using views in their LIKE clause.
     * Views are not created on Datanodes, so this will result in an error
     * In order to fix this problem, it will be necessary to
     * transform the query string of CREATE TABLE into something not using
     * the view definition. Now openGauss only uses the raw string...
     * There is some work done with event triggers in 9.3, so it might
     * be possible to use that code to generate the SQL query to be sent to
     * remote nodes. When this is done, this error will be removed.
     */
    if (relation->rd_rel->relkind == RELKIND_VIEW || relation->rd_rel->relkind == RELKIND_CONTQUERY)
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("openGauss does not support VIEW in LIKE clauses"),
                errdetail("The feature is not currently supported")));
#endif

    /*
     *  Judge whether create table ... like in multiple node group or not.
     *  If multi_nodegroup is true, table metainfo need to append to meta_info fields.
     *  At the end of transformTableLikeClause, meta_info need to serialize to string for datanodes.
     */
    multi_nodegroup = false;
    if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
        multi_nodegroup = is_multi_nodegroup_createtbllike(cxt->subcluster, relation->rd_id);
    }

    /*
     * Check for privileges
     */
    if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) {
        aclresult = pg_type_aclcheck(relation->rd_rel->reltype, GetUserId(), ACL_USAGE);
        if (aclresult != ACLCHECK_OK)
            aclcheck_error(aclresult, ACL_KIND_TYPE, RelationGetRelationName(relation));
    } else {
        /*
         * Just return aclok when current user is superuser, although pg_class_aclcheck
         * also used superuser() function but it forbid the INSERT/DELETE/SELECT/UPDATE
         * for superuser in independent condition. Here CreateLike is no need to forbid.
         */
        if (superuser())
            aclresult = ACLCHECK_OK;
        else
            aclresult = pg_class_aclcheck(RelationGetRelid(relation), GetUserId(), ACL_SELECT);
        if (aclresult != ACLCHECK_OK)
            aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(relation));
    }

    tupleDesc = RelationGetDescr(relation);
    constr = tupleDesc->constr;

    /*
     * Initialize column number map for map_variable_attnos().  We need this
     * since dropped columns in the source table aren't copied, so the new
     * table can have different column numbers.
     */
    attmap = (AttrNumber*)palloc0(sizeof(AttrNumber) * tupleDesc->natts);
    int colCount = list_length(cxt->columns);
    for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++) {
        Form_pg_attribute attribute = &tupleDesc->attrs[parent_attno - 1];
        if (attribute->attisdropped && (!u_sess->attr.attr_sql.enable_cluster_resize || RelationIsTsStore(relation)))
            continue;
        if (attribute->attkvtype == ATT_KV_HIDE && table_like_clause->options != CREATE_TABLE_LIKE_ALL) {
            continue;
        }
        colCount++;
        attmap[parent_attno - 1] = colCount;
    }

    /*
     * Insert the copied attributes into the cxt for the new table definition.
     */
    bool hideTag = false;
    for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++) {
        Form_pg_attribute attribute = &tupleDesc->attrs[parent_attno - 1];
        char* attributeName = NameStr(attribute->attname);
        ColumnDef* def = NULL;

        /*
         * Ignore dropped columns in the parent.  attmap entry is left zero.
         */
        if (attribute->attisdropped && (!u_sess->attr.attr_sql.enable_cluster_resize || RelationIsTsStore(relation)))
            continue;
        /*
         * Ignore hide tag(tsdb)
         */
        if (attribute->attkvtype == ATT_KV_HIDE && table_like_clause->options != CREATE_TABLE_LIKE_ALL) {
            hideTag = true;
            continue;
        }

        /*  Not support copying relation with set type attribute */
        if (type_is_set(attribute->atttypid)) {
            ereport(ERROR,
            (errcode(ERRCODE_DATATYPE_MISMATCH),
                errmsg("can not use existed set type %s for column definition", format_type_be(attribute->atttypid)),
                parser_errposition(cxt->pstate, table_like_clause->relation->location)));
        }

        if (u_sess->attr.attr_sql.enable_cluster_resize && attribute->attisdropped) {
            def = makeNode(ColumnDef);
            def->type = T_ColumnDef;
            def->colname = pstrdup(attributeName);
            def->dropped_attr = (Form_pg_attribute)palloc0(sizeof(FormData_pg_attribute));
            copyDroppedAttribute(def->dropped_attr, attribute);
        } else {
            /*
             * Create a new column, which is marked as NOT inherited.
             *
             * For constraints, ONLY the NOT NULL constraint is inherited by the
             * new column definition per SQL99.
             */
            def = makeNode(ColumnDef);
            def->colname = pstrdup(attributeName);
            if (attribute->atttypid == BYTEAWITHOUTORDERCOLOID || attribute->atttypid == BYTEAWITHOUTORDERWITHEQUALCOLOID) {
                /* this is client logic column. need to rewrite */
                    HeapTuple   col_tup = SearchSysCache2(CERELIDCOUMNNAME, 
                        ObjectIdGetDatum(RelationGetRelid(relation)), PointerGetDatum (def->colname));
                    
                    if (!col_tup) {
                        ereport(ERROR, (errcode(ERRCODE_UNDEFINED_CL_COLUMN),
                            errmsg("could not find encrypted column for %s", def->colname)));
                    } else {
                        
                        Form_gs_encrypted_columns columns_rel_data = (Form_gs_encrypted_columns)GETSTRUCT(col_tup);
                        const Oid ColSettingOid  = columns_rel_data->column_key_id;
                        HeapTuple col_setting_tup  = SearchSysCache1(COLUMNSETTINGOID, ObjectIdGetDatum(ColSettingOid));
                        if(!col_setting_tup) {
                            	ReleaseSysCache(col_tup);
                                ereport(ERROR, (errcode(ERRCODE_UNDEFINED_KEY),
                                    errmsg("could not find column encryption keys for column %s", def->colname)));
                        }
                        Form_gs_column_keys column_settings_rel_data = (Form_gs_column_keys)GETSTRUCT(col_setting_tup);
                        def->clientLogicColumnRef = makeNode(ClientLogicColumnRef);
                        def->clientLogicColumnRef->column_key_name =
                                list_make1(makeString(NameStr(column_settings_rel_data->column_key_name)));
                        def->clientLogicColumnRef->columnEncryptionAlgorithmType = static_cast<EncryptionType>(columns_rel_data->encryption_type);
                        def->clientLogicColumnRef->orig_typname = makeTypeNameFromOid(columns_rel_data->data_type_original_oid,
                                                columns_rel_data->data_type_original_mod);
                        def->clientLogicColumnRef->dest_typname =
                            makeTypeNameFromOid(attribute->atttypid, attribute->atttypmod);
                        def->typname = makeTypeNameFromOid(attribute->atttypid, attribute->atttypmod);
                        ReleaseSysCache(col_tup);
                        ReleaseSysCache(col_setting_tup);
                    }
            } else {
                def->typname = makeTypeNameFromOid(attribute->atttypid, attribute->atttypmod);
            }
            def->kvtype = attribute->attkvtype;
            def->inhcount = 0;
            def->is_local = true;
            def->is_not_null = attribute->attnotnull;
            def->is_from_type = false;
            def->storage = 0;
            /* copy compression mode from source table */
            def->cmprs_mode = attribute->attcmprmode;
            /*
             * When analyzing a column-oriented table with default_statistics_target < 0, we will create a row-oriented
             * temp table. In this case, ignore the compression flag.
             */
            if (u_sess->analyze_cxt.is_under_analyze || (IsConnFromCoord() && is_row_table)) {
                if (def->cmprs_mode != ATT_CMPR_UNDEFINED) {
                    def->cmprs_mode = ATT_CMPR_NOCOMPRESS;
                }
            }
            def->raw_default = NULL;
            def->cooked_default = NULL;
            def->update_default = NULL;
            def->collClause = NULL;
            def->collOid = attribute->attcollation;
            def->constraints = NIL;
            def->dropped_attr = NULL;
        }

        /*
         * Add to column list
         */
        cxt->columns = lappend(cxt->columns, def);

        /*
         * Copy default, if present and the default has been requested
         */
        if (attribute->atthasdef) {
            Node* this_default = NULL;
            Node* update_default = NULL;
            AttrDefault* attrdef = NULL;
            int i;
            Oid seqId = InvalidOid;
            bool is_autoinc = false;
            bool has_on_update = false;

            /* Find default in constraint structure */
            Assert(constr != NULL);
            attrdef = constr->defval;
            for (i = 0; i < constr->num_defval; i++) {
                if (attrdef[i].adnum == parent_attno) {
                    this_default = (Node*)stringToNode_skip_extern_fields(attrdef[i].adbin);
                    if (attrdef[i].has_on_update) {
                        update_default = (Node*)stringToNode_skip_extern_fields(attrdef[i].adbin_on_update);
                        has_on_update = true;
                    }
                    break;
                }
            }
            Assert(this_default != NULL || update_default != NULL);
            if (!has_on_update) {
                if (IsA(this_default, AutoIncrement)) {
                    AutoIncrementCheckOrientation(cxt);
                    if (cxt->relation->relpersistence == RELPERSISTENCE_TEMP) {
                        TransformTempAutoIncrement(def, (CreateStmt*)cxt->node);
                        def->is_serial = true; /* set def->is_serial to avoid setting def->cooked_default */
                    } else {
                        this_default = ((AutoIncrement*)this_default)->expr;
                    }
                    is_autoinc = true;
                }
            }
            

            /*
             *  Whether default expr is serial type and the sequence is owned by the table.
             */
            if (!IsCreatingSeqforAnalyzeTempTable(cxt) && (table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS_SERIAL)) {
                if (this_default != NULL) {
                    seqId = searchSeqidFromExpr(this_default);
                    if (OidIsValid(seqId)) {
                        List* seqs = getOwnedSequences(relation->rd_id);
                        if (seqs != NULL && list_member_oid(seqs, DatumGetObjectId(seqId))) {
                            /* is serial type */
                            def->is_serial = true;
                            bool large = (get_rel_relkind(seqId) == RELKIND_LARGE_SEQUENCE);
                            /* Special actions for SERIAL pseudo-types */

                            createSeqOwnedByTable(cxt, def, preCheck, large, is_autoinc);
                        }
                    }
                }
            }

            if (!def->is_serial && (table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS) &&
                !GetGeneratedCol(tupleDesc, parent_attno - 1)) {
                /*
                 * If default expr could contain any vars, we'd need to fix 'em,
                 * but it can't; so default is ready to apply to child.
                 */
                def->cooked_default = this_default;
                def->update_default = update_default;
            } else if (!def->is_serial && (table_like_clause->options & CREATE_TABLE_LIKE_GENERATED) &&
                GetGeneratedCol(tupleDesc, parent_attno - 1)) {
                bool found_whole_row = false;
                if (this_default != NULL) {
                    def->cooked_default =
                        map_variable_attnos(this_default, 1, 0, attmap, tupleDesc->natts, &found_whole_row);
                } else {
                    def->cooked_default = NULL;
                }
                if (update_default != NULL) {
                    def->update_default =
                        map_variable_attnos(update_default, 1, 0, attmap, tupleDesc->natts, &found_whole_row);
                } else {
                    def->update_default = NULL;
                }
                if (found_whole_row) {
                    ereport(ERROR, (errmodule(MOD_GEN_COL), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("cannot convert whole-row table reference"),
                        errdetail(
                        "Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
                        attributeName, RelationGetRelationName(relation))));
                }
                def->generatedCol = GetGeneratedCol(tupleDesc, parent_attno - 1);
            }
        }

        /* Likewise, copy storage if requested */
        if (table_like_clause->options & CREATE_TABLE_LIKE_STORAGE)
            def->storage = attribute->attstorage;

        if (multi_nodegroup) {
            /*need to copy ColumnDef deeply because we will modify it.*/
            ColumnDef* dup = (ColumnDef*)copyObject(def);
            if (def->is_serial) {
                /* Momory will be freed when ExecutorEnd  */
                dup->constraints = NULL;
                dup->raw_default = NULL;
            }
            meta_info.columns = lappend(meta_info.columns, dup);
        }

        /* Likewise, copy comment if requested */
        if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) &&
            (comment = GetComment(attribute->attrelid, RelationRelationId, attribute->attnum)) != NULL) {
            CommentStmt* stmt = (CommentStmt*)makeNode(CommentStmt);

            stmt->objtype = OBJECT_COLUMN;
            stmt->objname = list_make3(
                makeString(cxt->relation->schemaname), makeString(cxt->relation->relname), makeString(def->colname));
            stmt->objargs = NIL;
            stmt->comment = comment;

            cxt->alist = lappend(cxt->alist, stmt);

            if (multi_nodegroup) {
                /* don't need to copy CommentStmt deeply */
                meta_info.comments = lappend(meta_info.comments, stmt);
            }
        }
    }

    /*
     * Copy CHECK constraints if requested, being careful to adjust attribute
     * numbers so they match the child.
     */
    if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) && tupleDesc->constr) {
        int ccnum;

        /* check expr constraint */
        for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++) {
            char* ccname = tupleDesc->constr->check[ccnum].ccname;
            char* ccbin = tupleDesc->constr->check[ccnum].ccbin;
            Constraint* n = makeNode(Constraint);
            Node* ccbin_node = NULL;
            bool found_whole_row = false;

            ccbin_node =
                map_variable_attnos((Node*)stringToNode(ccbin), 1, 0, attmap, tupleDesc->natts, &found_whole_row);

            /*
             * We reject whole-row variables because the whole point of LIKE
             * is that the new table's rowtype might later diverge from the
             * parent's.  So, while translation might be possible right now,
             * it wouldn't be possible to guarantee it would work in future.
             */
            if (found_whole_row)
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("cannot convert whole-row table reference"),
                        errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
                            ccname,
                            RelationGetRelationName(relation))));

            n->contype = CONSTR_CHECK;
            n->location = -1;
            n->conname = pstrdup(ccname);
            n->raw_expr = NULL;
            n->cooked_expr = nodeToString(ccbin_node);
            cxt->ckconstraints = lappend(cxt->ckconstraints, n);
            if (multi_nodegroup) {
                /* don't need to copy Constraint deeply */
                meta_info.ckconstraints = lappend(meta_info.ckconstraints, n);
            }

            /* Copy comment on constraint */
            if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) &&
                (comment = GetComment(get_relation_constraint_oid(RelationGetRelid(relation), n->conname, false),
                    ConstraintRelationId,
                    0)) != NULL) {
                CommentStmt* stmt = makeNode(CommentStmt);

                stmt->objtype = OBJECT_CONSTRAINT;
                stmt->objname = list_make3(
                    makeString(cxt->relation->schemaname), makeString(cxt->relation->relname), makeString(n->conname));
                stmt->objargs = NIL;
                stmt->comment = comment;

                cxt->alist = lappend(cxt->alist, stmt);
                if (multi_nodegroup) {
                    /* don't need to copy CommentStmt deeply */
                    meta_info.comments = lappend(meta_info.comments, stmt);
                }
            }
        }

        /* paritial cluster key constraint like */
        if (tupleDesc->constr->clusterKeyNum > 0) {
            int pckNum;
            Constraint* n = makeNode(Constraint);

            for (pckNum = 0; pckNum < tupleDesc->constr->clusterKeyNum; pckNum++) {
                AttrNumber attrNum = tupleDesc->constr->clusterKeys[pckNum];
                Form_pg_attribute attribute = &tupleDesc->attrs[attrNum - 1];
                char* attrName = NameStr(attribute->attname);

                n->contype = CONSTR_CLUSTER;
                n->location = -1;
                n->keys = lappend(n->keys, makeString(pstrdup(attrName)));
            }

            cxt->clusterConstraints = lappend(cxt->clusterConstraints, n);

            if (multi_nodegroup) {
                /* don't need to copy Constraint deeply */
                meta_info.cluster_keys = lappend(meta_info.cluster_keys, n);
            }

            /* needn't copy comment on partial cluster key constraint
             * the constraint name was not like the source, refer to primary/unique constraint
             */
        }
    }

    /*
     * Likewise, copy partition definitions if requested. Then, copy index,
     * because partitioning might have effect on how to create indexes
     */
    if (table_like_clause->options & CREATE_TABLE_LIKE_PARTITION) {
        PartitionState* n = NULL;
        HeapTuple partitionTableTuple = NULL;
        Form_pg_partition partitionForm = NULL;
        List* partitionList = NIL;
        List* subPartitionList = NIL;

        // read out partitioned table tuple, and partition tuple list
        partitionTableTuple =
            searchPgPartitionByParentIdCopy(PART_OBJ_TYPE_PARTED_TABLE, ObjectIdGetDatum(relation->rd_id));
        partitionList = searchPgPartitionByParentId(PART_OBJ_TYPE_TABLE_PARTITION, ObjectIdGetDatum(relation->rd_id));

        if (RelationIsSubPartitioned(relation)) {
            subPartitionList = searchPgSubPartitionByParentId(PART_OBJ_TYPE_TABLE_SUB_PARTITION, partitionList);
        }

        if (partitionTableTuple != NULL) {
            partitionForm = (Form_pg_partition)GETSTRUCT(partitionTableTuple);

            if (partitionForm->partstrategy == PART_STRATEGY_LIST ||
                partitionForm->partstrategy == PART_STRATEGY_HASH) {
                freePartList(partitionList);
                heap_freetuple_ext(partitionTableTuple);
                heap_close(relation, NoLock);
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("Un-support feature"),
                        errdetail("The Like feature is not supported currently for List and Hash.")));
            }
            bool value_partition_rel = (partitionForm->partstrategy == PART_STRATEGY_VALUE);

            /*
             * We only have to create PartitionState for a range partition table
             * with known partitions or a value partition table(HDFS).
             */
            if ((NIL != partitionList && subPartitionList == NIL) || value_partition_rel) {
                {
                    List* partKeyColumns = NIL;
                    List* partitionDefinitions = NIL;

                    transformTableLikePartitionProperty(
                        relation, partitionTableTuple, &partKeyColumns, partitionList, &partitionDefinitions);

                    // set PartitionState fields, 5 following
                    // (1)partition key
                    // (2)partition definition list
                    // (3)interval definition
                    // (4)partitionStrategy
                    // (5)rowMovement
                    n = makeNode(PartitionState);
                    n->partitionKey = partKeyColumns;
                    n->partitionList = partitionDefinitions;
                    n->partitionStrategy = partitionForm->partstrategy;
                    if (partitionForm->partstrategy == PART_STRATEGY_INTERVAL) {
                        n->intervalPartDef = TransformTableLikeIntervalPartitionDef(partitionTableTuple);
                    } else {
                        n->intervalPartDef = NULL;
                    }

                    n->rowMovement = relation->rd_rel->relrowmovement ? ROWMOVEMENT_ENABLE : ROWMOVEMENT_DISABLE;

                    // store the produced partition state in CreateStmtContext
                    cxt->csc_partTableState = n;

                    freePartList(partitionList);
                }
            } else if (subPartitionList != NULL) {
                n = transformTblSubpartition(relation,
                            partitionTableTuple,
                            partitionList,
                            subPartitionList);

                /* store the produced partition state in CreateStmtContext */
                cxt->csc_partTableState = n;

                freePartList(partitionList);
                freePartList(subPartitionList);
            }

            heap_freetuple_ext(partitionTableTuple);
        }
    }

    /*
     * Likewise, copy indexes if requested
     */
    if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) && relation->rd_rel->relhasindex) {
        List* parent_indexes = NIL;
        ListCell* l = NULL;
        parent_indexes = RelationGetIndexList(relation);

        foreach (l, parent_indexes) {
            Oid parent_index_oid = lfirst_oid(l);
            Relation parent_index;
            IndexStmt* index_stmt = NULL;

            parent_index = index_open(parent_index_oid, AccessShareLock);

            /* Build CREATE INDEX statement to recreate the parent_index */
            index_stmt = generateClonedIndexStmt(cxt, parent_index, attmap, tupleDesc->natts, relation, transformType);

            /* Copy comment on index, if requested */
            if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) {
                comment = GetComment(parent_index_oid, RelationRelationId, 0);

                /*
                 * We make use of IndexStmt's idxcomment option, so as not to
                 * need to know now what name the index will have.
                 */
                index_stmt->idxcomment = comment;
            }

            /* Save it in the inh_indexes list for the time being */
            cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt);

            index_close(parent_index, AccessShareLock);
        }
    }

    /*
     * Likewise, copy reloptions if requested
     */
    if (table_like_clause->options & CREATE_TABLE_LIKE_RELOPTIONS) {
        Datum reloptions = (Datum)0;
        bool isNull = false;
        HeapTuple tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relation->rd_id));

        if (!HeapTupleIsValid(tuple))
            ereport(ERROR,
                (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                    errmsg("cache lookup failed on source like relation %u for reloptions", relation->rd_id)));
        reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions, &isNull);
        if (isNull)
            reloptions = (Datum)0;
        cxt->reloptions = untransformRelOptions(reloptions);

        /* remove on_commit_delete_rows option */
        if (cxt->relation->relpersistence != RELPERSISTENCE_GLOBAL_TEMP) {
            cxt->reloptions = RemoveRelOption(cxt->reloptions, "on_commit_delete_rows", NULL);
        }

        /* remove redis options first. */
        RemoveRedisRelOptionsFromList(&(cxt->reloptions));

        meta_info.reloptions = cxt->reloptions;

        ReleaseSysCache(tuple);
    }
#ifdef PGXC
    /*
     * Likewise, copy distribution if requested
     */
    if (table_like_clause->options & CREATE_TABLE_LIKE_DISTRIBUTION) {
        cxt->distributeby = (IS_PGXC_COORDINATOR) ? 
                        (hideTag ? GetHideTagDistribution(tupleDesc) : getTableDistribution(relation->rd_id)) : 
                        getTableHBucketDistribution(relation);
    }
#endif

    /*
     * Likewise, copy oids if requested
     */
    if (table_like_clause->options & CREATE_TABLE_LIKE_OIDS) {
        cxt->hasoids = tupleDesc->tdhasoid;
    }

    if (multi_nodegroup) {
        meta_info.type = T_TableLikeCtx;
        meta_info.options = table_like_clause->options;
        meta_info.hasoids = cxt->hasoids;

        /* partition info and inh_indexes is only from transformTableLikeClause,
         *  so we don't need to copy them.
         */
        meta_info.partition = cxt->csc_partTableState;
        meta_info.inh_indexes = cxt->inh_indexes;

        cxt->internalData = nodeToString(&meta_info);

        /* Memory of meta_info will be freed when ExecutorEnd  */
    }

    if (u_sess->attr.attr_sql.enable_cluster_resize) {
         cxt->isResizing = RelationInClusterResizing(relation);
    }

    /*
     * Close the parent rel, but keep our AccessShareLock on it until xact
     * commit.	That will prevent someone else from deleting or ALTERing the
     * parent before the child is committed.
     */
    if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !isFirstNode)
        heap_close(relation, AccessShareLock);
    else
        heap_close(relation, NoLock);
}

// this function is used to output 2 list,
// one for partitionkey, a list of column ref,
// another for partiton boundary, a list of
static void transformTableLikePartitionProperty(Relation relation, HeapTuple partitionTableTuple, List** partKeyColumns,
    List* partitionList, List** partitionDefinitions)
{
    List* partKeyPosList = NIL;
    transformTableLikePartitionKeys(relation, partitionTableTuple, partKeyColumns, &partKeyPosList);
    transformTableLikePartitionBoundaries(relation, partKeyPosList, partitionList, partitionDefinitions);
}

static IntervalPartitionDefState* TransformTableLikeIntervalPartitionDef(HeapTuple partitionTableTuple)
{
    IntervalPartitionDefState* intervalPartDef = makeNode(IntervalPartitionDefState);
    Relation partitionRel = relation_open(PartitionRelationId, RowExclusiveLock);
    char* intervalStr = ReadIntervalStr(partitionTableTuple, RelationGetDescr(partitionRel));
    Assert(intervalStr != NULL);
    intervalPartDef->partInterval = makeAConst(makeString(intervalStr), -1);
    oidvector* tablespaceIdVec = ReadIntervalTablespace(partitionTableTuple, RelationGetDescr(partitionRel));
    intervalPartDef->intervalTablespaces = NULL;
    if (tablespaceIdVec != NULL && tablespaceIdVec->dim1 > 0) {
        for (int i = 0; i < tablespaceIdVec->dim1; ++i) {
            char* tablespaceName = get_tablespace_name(tablespaceIdVec->values[i]);
            if (tablespaceName == NULL) {
                ereport(ERROR,
                    (errcode(ERRCODE_UNDEFINED_OBJECT),
                        errmsg("tablespace with OID %u does not exist", tablespaceIdVec->values[i])));
            }
            intervalPartDef->intervalTablespaces =
                lappend(intervalPartDef->intervalTablespaces, makeString(tablespaceName));
        }
    }

    relation_close(partitionRel, RowExclusiveLock);
    return intervalPartDef;
}

static void transformTableLikePartitionKeys(
    Relation relation, HeapTuple partitionTableTuple, List** partKeyColumns, List** partKeyPosList)
{
    ColumnRef* c = NULL;
    Relation partitionRel = NULL;
    TupleDesc relationTupleDesc = NULL;
    FormData_pg_attribute* relationAtts = NULL;
    int relationAttNumber = 0;
    Datum partkey_raw = (Datum)0;
    ArrayType* partkey_columns = NULL;
    int16* attnums = NULL;
    bool isNull = false;
    int n_key_column, i;

    /* open pg_partition catalog */
    partitionRel = relation_open(PartitionRelationId, RowExclusiveLock);

    /* Get the raw data which contain patition key's columns */
    partkey_raw = heap_getattr(partitionTableTuple, Anum_pg_partition_partkey, RelationGetDescr(partitionRel), &isNull);
    /* if the raw value of partition key is null, then report error */
    if (isNull) {
        ereport(ERROR,
            (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                errmsg("null partition key value for relation \"%s\"", RelationGetRelationName(relation))));
    }
    /* convert Datum to ArrayType */
    partkey_columns = DatumGetArrayTypeP(partkey_raw);

    /* Get number of partition key columns from int2verctor*/
    n_key_column = ARR_DIMS(partkey_columns)[0];

    /* CHECK: the ArrayType of partition key is valid */
    if (ARR_NDIM(partkey_columns) != 1 || n_key_column < 0 || ARR_HASNULL(partkey_columns) ||
        ARR_ELEMTYPE(partkey_columns) != INT2OID) {
        ereport(ERROR,
            (errcode(ERRCODE_DATATYPE_MISMATCH),
                errmsg("partition key column's number of relation \"%s\" is not a 1-D smallint array",
                    RelationGetRelationName(relation))));
    }

    AssertEreport(n_key_column <= MAX_RANGE_PARTKEY_NUMS, MOD_OPT, "");

    /* Get int2 array of partition key column numbers */
    attnums = (int16*)ARR_DATA_PTR(partkey_columns);

    /*
     * get the partition key number,
     * make ColumnRef node from name of partition key
     */
    relationTupleDesc = relation->rd_att;
    relationAttNumber = relationTupleDesc->natts;
    relationAtts = relationTupleDesc->attrs;

    for (i = 0; i < n_key_column; i++) {
        int attnum = (int)(attnums[i]);
        if (attnum >= 1 && attnum <= relationAttNumber) {
            c = makeNode(ColumnRef);
            c->fields = list_make1(makeString(pstrdup(NameStr(relationAtts[attnum - 1].attname))));
            *partKeyColumns = lappend(*partKeyColumns, c);
            *partKeyPosList = lappend_int(*partKeyPosList, attnum - 1);
        } else {
            ereport(ERROR,
                (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                    errmsg("partition key column's number of %s not in the range of all its columns",
                        RelationGetRelationName(relation))));
        }
    }

    /* close pg_partition catalog */
    relation_close(partitionRel, RowExclusiveLock);
}

static void transformTableLikePartitionBoundaries(
    Relation relation, List* partKeyPosList, List* partitionList, List** partitionDefinitions)
{
    ListCell* partitionCell = NULL;
    List* orderedPartitionList = NIL;

    if (relation->partMap == NULL)
        return;

    // form into a new ordered list
    if (relation->partMap->type == PART_TYPE_RANGE || relation->partMap->type == PART_TYPE_INTERVAL) {
        RangePartitionMap* rangePartMap = (RangePartitionMap*)relation->partMap;
        int i;
        int rangePartitions = rangePartMap->rangeElementsNum;

        for (i = 0; i < rangePartitions; i++) {
            Oid partitionOid = rangePartMap->rangeElements[i].partitionOid;

            foreach (partitionCell, partitionList) {
                HeapTuple partitionTuple = (HeapTuple)lfirst(partitionCell);
                if (partitionOid == HeapTupleGetOid(partitionTuple)) {
                    orderedPartitionList = lappend(orderedPartitionList, partitionTuple);
                    break;
                }
            }
        }
    } else if (relation->partMap->type == PART_TYPE_LIST) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("\" including partition \" for list partitioned relation: \"%s\" not implemented yet",
                    RelationGetRelationName(relation))));
    }
    /* open pg_partition catalog */
    Relation partitionRel = relation_open(PartitionRelationId, AccessShareLock);

    foreach (partitionCell, orderedPartitionList) {
        HeapTuple partitionTuple = (HeapTuple)lfirst(partitionCell);
        Form_pg_partition partitionForm = (Form_pg_partition)GETSTRUCT(partitionTuple);
        /* no need to copy interval partition */
        if (partitionForm->partstrategy == PART_STRATEGY_INTERVAL) {
            continue;
        }
        bool attIsNull = false;
        Datum tableSpace = (Datum)0;
        Datum boundaries = (Datum)0;
        RangePartitionDefState* partitionNode = NULL;

        // in mppdb, we only support range partition by now(2014.05)
        // so here produce RangePartitionDefState node
        partitionNode = makeNode(RangePartitionDefState);

        // set RangePartitionDefState: 1.partition name
        partitionNode->partitionName = pstrdup(NameStr(partitionForm->relname));

        // set RangePartitionDefState: 2.partition tablespace
        tableSpace =
            heap_getattr(partitionTuple, Anum_pg_partition_reltablespace, RelationGetDescr(partitionRel), &attIsNull);
        if (attIsNull)
            partitionNode->tablespacename = NULL;
        else
            partitionNode->tablespacename = get_tablespace_name(DatumGetObjectId(tableSpace));

        // set RangePartitionDefState: 3.boundaries
        boundaries =
            heap_getattr(partitionTuple, Anum_pg_partition_boundaries, RelationGetDescr(partitionRel), &attIsNull);
        if (attIsNull) {
            partitionNode->boundary = NIL;
        } else {
            /* unstransform string items to Value list */
            List* boundaryValueList = NIL;
            List* resultBoundaryList = NIL;
            ListCell* boundaryCell = NULL;
            ListCell* partKeyCell = NULL;
            Value* boundaryValue = NULL;
            Datum boundaryDatum = (Datum)0;
            Node* boundaryNode = NULL;
            FormData_pg_attribute* relation_atts = NULL;
            Form_pg_attribute att = NULL;
            int partKeyPos = 0;
            int16 typlen = 0;
            bool typbyval = false;
            char typalign;
            char typdelim;
            Oid typioparam = InvalidOid;
            Oid func = InvalidOid;
            Oid typid = InvalidOid;
            Oid typelem = InvalidOid;
            Oid typcollation = InvalidOid;
            int32 typmod = -1;

            boundaryValueList = untransformPartitionBoundary(boundaries);

            // transform Value(every is string Value) node into Const node.
            // (1)the first step is transform text into datum,
            // (2)then datum into corresponding int, float or string format
            // (3)the last step is make A_Const node using int, float or string
            relation_atts = relation->rd_att->attrs;
            forboth(boundaryCell, boundaryValueList, partKeyCell, partKeyPosList)
            {
                boundaryValue = (Value*)lfirst(boundaryCell);
                partKeyPos = (int)lfirst_int(partKeyCell);
                att = &relation_atts[partKeyPos];

                /* get the oid/mod/collation/ of partition key */
                typid = att->atttypid;
                typmod = att->atttypmod;
                typcollation = att->attcollation;
                /* deal with null */
                if (!PointerIsValid(boundaryValue->val.str)) {
                    boundaryNode = (Node*)makeMaxConst(typid, typmod, typcollation);
                } else {
                    /* get the typein function's oid of current type */
                    get_type_io_data(typid, IOFunc_input, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &func);
                    typelem = get_element_type(typid);

                    /* now call the typein function with collation,string, element_type, typemod
                     * as it's parameters.
                     */
                    boundaryDatum = OidFunctionCall3Coll(func,
                        typcollation,
                        CStringGetDatum(boundaryValue->val.str),
                        ObjectIdGetDatum(typelem),
                        Int32GetDatum(typmod));

                    // produce const node
                    boundaryNode =
                        (Node*)makeConst(typid, typmod, typcollation, typlen, boundaryDatum, false, typbyval);
                }
                resultBoundaryList = lappend(resultBoundaryList, boundaryNode);
            }
            partitionNode->boundary = resultBoundaryList;
        }

        // now, append the result RangePartitionDefState node to output list
        *partitionDefinitions = lappend(*partitionDefinitions, partitionNode);
    }
    /* close pg_partition catalog */
    relation_close(partitionRel, AccessShareLock);

    // free the new ordered list
    list_free_ext(orderedPartitionList);
}

static void transformOfType(CreateStmtContext* cxt, TypeName* ofTypename)
{
    HeapTuple tuple;
    TupleDesc tupdesc;
    int i;
    Oid ofTypeId;

    AssertArg(ofTypename);

    tuple = typenameType(NULL, ofTypename, NULL);
    check_of_type(tuple);
    ofTypeId = HeapTupleGetOid(tuple);
    ofTypename->typeOid = ofTypeId; /* cached for later */

    tupdesc = lookup_rowtype_tupdesc(ofTypeId, -1);
    for (i = 0; i < tupdesc->natts; i++) {
        Form_pg_attribute attr = &tupdesc->attrs[i];
        ColumnDef* n = NULL;

        if (attr->attisdropped)
            continue;

        n = makeNode(ColumnDef);
        n->colname = pstrdup(NameStr(attr->attname));
        n->typname = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
        n->kvtype = ATT_KV_UNDEFINED;
        n->generatedCol = '\0';
        n->inhcount = 0;
        n->is_local = true;
        n->is_not_null = false;
        n->is_from_type = true;
        n->storage = 0;
        /* CREATE TYPE CANNOT provied compression feature, so the default is set. */
        n->cmprs_mode = ATT_CMPR_UNDEFINED;
        n->raw_default = NULL;
        n->update_default = NULL;
        n->cooked_default = NULL;
        n->collClause = NULL;
        n->collOid = attr->attcollation;
        n->constraints = NIL;
        cxt->columns = lappend(cxt->columns, n);
    }
    DecrTupleDescRefCount(tupdesc);

    ReleaseSysCache(tuple);
}

char* getTmptableIndexName(const char* srcSchema, const char* srcIndex)
{
    char *idxname;
    errno_t rc;
    char srcIndexName[NAMEDATALEN * 2];
    Assert(strlen(srcSchema) < NAMEDATALEN && strlen(srcIndex) < NAMEDATALEN);

    rc = memset_s(srcIndexName, NAMEDATALEN * 2, 0, NAMEDATALEN * 2);
    securec_check(rc, "", "");
    /* Generate idxname based on src schema and src index name */
    rc = strncpy_s(srcIndexName, NAMEDATALEN, srcSchema, NAMEDATALEN - 1);
    securec_check(rc, "", "");
    rc = strncpy_s(srcIndexName + NAMEDATALEN, NAMEDATALEN, srcIndex, NAMEDATALEN - 1);
    securec_check(rc, "", "");
    /* Calculates feature values based on schemas and indexes. */
    uint32 srcIndexNum = pg_checksum_block(srcIndexName, NAMEDATALEN * 2);

    /* In redistribution, idx name is "data_redis_tmp_index_srcIndexNum" */
    uint len = OIDCHARS + strlen("data_redis_tmp_index_") + 1;
    idxname = (char*)palloc0(len);
    int ret = snprintf_s(idxname, len, len -1, "data_redis_tmp_index_%u", srcIndexNum);
    securec_check_ss(ret, "", "");
    return idxname;
}

static bool* getPartIndexUsableStat(Relation indexRelation)
{
    Oid relation_oid = IndexGetRelation(indexRelation->rd_id, false);
    Relation rel = relation_open(relation_oid, NoLock);
    List* relation_oid_list = relationGetPartitionOidList(rel);
    List* partitions = indexGetPartitionList(indexRelation, ExclusiveLock);
    bool *usable = (bool *)palloc0(sizeof(bool) * list_length(partitions));
    ListCell* cell = NULL;
    int i = 0;

    foreach (cell, relation_oid_list) {
        Oid relid = lfirst_oid(cell);
        ListCell* parCell = NULL;
        bool found = false;
        foreach (parCell, partitions) {
            Partition indexPartition = (Partition)lfirst(parCell);
            if (relid == indexPartition->pd_part->indextblid) {
                usable[i++] = indexPartition->pd_part->indisusable;
                found = true;
                break;
            }
        }
        Assert(found);
    }
    releasePartitionList(indexRelation, &partitions, ExclusiveLock);
    list_free_ext(relation_oid_list);
    list_free_ext(partitions);
    relation_close(rel, NoLock);
    return usable;
}

void GenerateClonedIndexHbucketTransfer(Relation rel, IndexStmt* index, TransformTableType transformType, bool constainsExp)
{
    if (PointerIsValid(rel) && RelationInClusterResizing(rel) && RELATION_HAS_BUCKET(rel) &&
        transformType == TRANSFORM_TO_NONHASHBUCKET) {
        if (index->options != NULL && is_contain_crossbucket(index->options)) {
            index->options = list_delete_name(index->options, "crossbucket");
        }
    }

    if (PointerIsValid(rel) && RelationInClusterResizing(rel) && !RELATION_HAS_BUCKET(rel) &&
        transformType == TRANSFORM_TO_HASHBUCKET) {
        if (constainsExp || index->whereClause) {
            index->options = lappend(index->options, makeDefElem("crossbucket", (Node*)makeString("off")));
        }
    }

}
/*
 * Generate an IndexStmt node using information from an already existing index
 * "source_idx".  Attribute numbers should be adjusted according to attmap.
 */
IndexStmt* generateClonedIndexStmt(
    CreateStmtContext* cxt, Relation source_idx, const AttrNumber* attmap, int attmap_length, Relation rel, TransformTableType transformType)
{
    Oid source_relid = RelationGetRelid(source_idx);
    FormData_pg_attribute* attrs = RelationGetDescr(source_idx)->attrs;
    HeapTuple ht_idxrel;
    HeapTuple ht_idx;
    Form_pg_class idxrelrec;
    Form_pg_index idxrec;
    Form_pg_am amrec;
    oidvector* indcollation = NULL;
    oidvector* indclass = NULL;
    IndexStmt* index = NULL;
    List* indexprs = NIL;
    ListCell* indexpr_item = NULL;
    Oid indrelid;
    int keyno;
    Oid keycoltype;
    Datum datum;
    bool isnull = false;
    int indnkeyatts;

    /*
     * Fetch pg_class tuple of source index.  We can't use the copy in the
     * relcache entry because it doesn't include optional fields.
     */
    ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(source_relid));
    if (!HeapTupleIsValid(ht_idxrel))
        ereport(
            ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", source_relid)));
    idxrelrec = (Form_pg_class)GETSTRUCT(ht_idxrel);

    /* Fetch pg_index tuple for source index from relcache entry */
    ht_idx = source_idx->rd_indextuple;
    idxrec = (Form_pg_index)GETSTRUCT(ht_idx);
    indrelid = idxrec->indrelid;
    indnkeyatts = IndexRelationGetNumberOfKeyAttributes(source_idx);

    /* Fetch pg_am tuple for source index from relcache entry */
    amrec = source_idx->rd_am;

    /* Extract indcollation from the pg_index tuple */
    datum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indcollation, &isnull);
    Assert(!isnull);

    indcollation = (oidvector*)DatumGetPointer(datum);

    /* Extract indclass from the pg_index tuple */
    datum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indclass, &isnull);
    Assert(!isnull);

    indclass = (oidvector*)DatumGetPointer(datum);

    /* Begin building the IndexStmt */
    index = makeNode(IndexStmt);
    index->relation = cxt->relation;
    index->accessMethod = pstrdup(NameStr(amrec->amname));
    if (OidIsValid(idxrelrec->reltablespace))
        index->tableSpace = get_tablespace_name(idxrelrec->reltablespace);
    else
        index->tableSpace = NULL;
    index->excludeOpNames = NIL;
    index->idxcomment = NULL;
    index->indexOid = InvalidOid;
    index->oldNode = InvalidOid;
    index->oldPSortOid = InvalidOid;
    index->unique = idxrec->indisunique;
    index->primary = idxrec->indisprimary;
    index->concurrent = false;
    // mark if the resulting indexStmt is a partitioned index
    index->isPartitioned = RelationIsPartitioned(source_idx);

    /*
     * If the src table is in resizing, means we are going to do create table like for tmp table,
     * then we preserve the index name by src index.
     * Otherwise, set idxname to NULL, let DefineIndex() choose a reasonable name.
     */
    if (PointerIsValid(rel) && RelationInClusterResizing(rel)) {
        /* Generate idxname based on src index name */
        char *srcSchema = get_namespace_name(source_idx->rd_rel->relnamespace);
        char *srcIndex = NameStr(source_idx->rd_rel->relname);
        index->idxname = getTmptableIndexName(srcSchema, srcIndex);
        pfree_ext(srcSchema);
        if (index->isPartitioned) {
            index->partIndexUsable = getPartIndexUsableStat(source_idx);
        }
    } else {
        index->idxname = NULL;
    }

    /*
     * If the index is marked PRIMARY or has an exclusion condition, it's
     * certainly from a constraint; else, if it's not marked UNIQUE, it
     * certainly isn't.  If it is or might be from a constraint, we have to
     * fetch the pg_constraint record.
     */
    if (index->primary || index->unique || idxrec->indisexclusion) {
        Oid constraintId = get_index_constraint(source_relid);
        if (OidIsValid(constraintId)) {
            HeapTuple ht_constr;
            Form_pg_constraint conrec;

            ht_constr = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintId));
            if (!HeapTupleIsValid(ht_constr))
                ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                        errmodule(MOD_OPT), errmsg("cache lookup failed for constraint %u", constraintId)));
            conrec = (Form_pg_constraint)GETSTRUCT(ht_constr);

            index->isconstraint = true;
            index->deferrable = conrec->condeferrable;
            index->initdeferred = conrec->condeferred;
            index->isvalidated = conrec->convalidated;

            bool isNull = true;
            Relation pg_constraint;
            pg_constraint = heap_open(ConstraintRelationId, NoLock);
            Datum datum = heap_getattr(ht_constr, Anum_pg_constraint_condisable, RelationGetDescr(pg_constraint), &isNull);
            index->isdisable = DatumGetBool(datum);

            /* If it's an exclusion constraint, we need the operator names */
            if (idxrec->indisexclusion) {
                Datum* elems = NULL;
                int nElems;
                int i;

                Assert(conrec->contype == CONSTRAINT_EXCLUSION);
                /* Extract operator OIDs from the pg_constraint tuple */
                datum = SysCacheGetAttr(CONSTROID, ht_constr, Anum_pg_constraint_conexclop, &isnull);
                if (isnull)
                    ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
                            errmodule(MOD_OPT), errmsg("null conexclop for constraint %u", constraintId)));

                deconstruct_array(DatumGetArrayTypeP(datum), OIDOID, sizeof(Oid), true, 'i', &elems, NULL, &nElems);

                for (i = 0; i < nElems; i++) {
                    Oid operid = DatumGetObjectId(elems[i]);
                    HeapTuple opertup;
                    Form_pg_operator operform;
                    char* oprname = NULL;
                    char* nspname = NULL;
                    List* namelist = NIL;

                    opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));
                    if (!HeapTupleIsValid(opertup))
                        ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                                errmodule(MOD_OPT), errmsg("cache lookup failed for operator %u", operid)));

                    operform = (Form_pg_operator)GETSTRUCT(opertup);
                    oprname = pstrdup(NameStr(operform->oprname));
                    /* For simplicity we always schema-qualify the op name */
                    nspname = get_namespace_name(operform->oprnamespace);
                    namelist = list_make2(makeString(nspname), makeString(oprname));
                    index->excludeOpNames = lappend(index->excludeOpNames, namelist);
                    ReleaseSysCache(opertup);
                }
            }

            heap_close(pg_constraint, NoLock);
            ReleaseSysCache(ht_constr);
        } else
            index->isconstraint = false;
    } else
        index->isconstraint = false;

    /* Get the index expressions, if any */
    datum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indexprs, &isnull);
    if (!isnull) {
        char* exprsString = NULL;

        exprsString = TextDatumGetCString(datum);
        indexprs = (List*)stringToNode(exprsString);
    } else
        indexprs = NIL;

    /* Build the list of IndexElem */
    index->indexParams = NIL;
    index->indexIncludingParams = NIL;

    indexpr_item = list_head(indexprs);
    for (keyno = 0; keyno < indnkeyatts; keyno++) {
        IndexElem* iparam = NULL;
        AttrNumber attnum = idxrec->indkey.values[keyno];
        uint16 opt = (uint16)source_idx->rd_indoption[keyno];

        iparam = makeNode(IndexElem);

        if (AttributeNumberIsValid(attnum)) {
            /* Simple index column */
            char* attname = NULL;

            attname = get_relid_attribute_name(indrelid, attnum);
            keycoltype = get_atttype(indrelid, attnum);

            iparam->name = attname;
            iparam->expr = NULL;
        } else {
            /* Expressional index */
            Node* indexkey = NULL;
            bool found_whole_row = false;

            if (indexpr_item == NULL)
                ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                        errmodule(MOD_OPT), errmsg("too few entries in indexprs list")));

            indexkey = (Node*)lfirst(indexpr_item);
            indexpr_item = lnext(indexpr_item);

            /* Adjust Vars to match new table's column numbering */
            indexkey = map_variable_attnos(indexkey, 1, 0, attmap, attmap_length, &found_whole_row);

            /* As in transformTableLikeClause, reject whole-row variables */
            if (found_whole_row)
                ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("cannot convert whole-row table reference"),
                        errdetail("Index \"%s\" contains a whole-row table reference.",
                            RelationGetRelationName(source_idx))));

            iparam->name = NULL;
            iparam->expr = indexkey;

            keycoltype = exprType(indexkey);
        }

        /* Copy the original index column name */
        iparam->indexcolname = pstrdup(NameStr(attrs[keyno].attname));

        /* Add the collation name, if non-default */
        iparam->collation = get_collation(indcollation->values[keyno], keycoltype);

        /* Add the operator class name, if non-default */
        iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);

        iparam->ordering = SORTBY_DEFAULT;
        iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;

        /* Adjust options if necessary */
        if (amrec->amcanorder) {
            /*
             * If it supports sort ordering, copy DESC and NULLS opts. Don't
             * set non-default settings unnecessarily, though, so as to
             * improve the chance of recognizing equivalence to constraint
             * indexes.
             */
            if (((uint16)opt) & INDOPTION_DESC) {
                iparam->ordering = SORTBY_DESC;
                if ((((uint16)opt) & INDOPTION_NULLS_FIRST) == 0)
                    iparam->nulls_ordering = SORTBY_NULLS_LAST;
            } else {
                if (((uint16)opt) & INDOPTION_NULLS_FIRST)
                    iparam->nulls_ordering = SORTBY_NULLS_FIRST;
            }
        }

        index->indexParams = lappend(index->indexParams, iparam);
    }

    /* Handle included columns separately */
    for (int i = indnkeyatts; i < idxrec->indnatts; i++) {
        AttrNumber attnum = idxrec->indkey.values[i];
        if (attnum == TableOidAttributeNumber) {
            /* global-partition-index */
            index->isGlobal = true;
            index->isPartitioned = true;
            break;
        }
    }

    /* Copy reloptions if any */
    datum = SysCacheGetAttr(RELOID, ht_idxrel, Anum_pg_class_reloptions, &isnull);
    if (!isnull) {
        index->options = untransformRelOptions(datum);
    }

    /* If it's a partial index, decompile and append the predicate */
    datum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indpred, &isnull);
    if (!isnull) {
        char* pred_str = NULL;
        Node* pred_tree = NULL;
        bool found_whole_row = false;

        /* Convert text string to node tree */
        pred_str = TextDatumGetCString(datum);
        pred_tree = (Node*)stringToNode(pred_str);

        /* Adjust Vars to match new table's column numbering */
        pred_tree = map_variable_attnos(pred_tree, 1, 0, attmap, attmap_length, &found_whole_row);

        /* As in transformTableLikeClause, reject whole-row variables */
        if (found_whole_row)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("cannot convert whole-row table reference"),
                    errdetail(
                        "Index \"%s\" contains a whole-row table reference.", RelationGetRelationName(source_idx))));

        index->whereClause = pred_tree;
    }
    GenerateClonedIndexHbucketTransfer(rel, index, transformType, indexprs != NULL);
    /* Clean up */
    ReleaseSysCache(ht_idxrel);

    return index;
}

/*
 * get_collation		- fetch qualified name of a collation
 *
 * If collation is InvalidOid or is the default for the given actual_datatype,
 * then the return value is NIL.
 */
static List* get_collation(Oid collation, Oid actual_datatype)
{
    List* result = NIL;
    HeapTuple ht_coll;
    Form_pg_collation coll_rec;
    char* nsp_name = NULL;
    char* coll_name = NULL;

    if (!OidIsValid(collation))
        return NIL; /* easy case */
    if (collation == get_typcollation(actual_datatype))
        return NIL; /* just let it default */

    ht_coll = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
    if (!HeapTupleIsValid(ht_coll))
        ereport(ERROR,
            (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                errmodule(MOD_OPT),
                errmsg("cache lookup failed for collation %u", collation)));

    coll_rec = (Form_pg_collation)GETSTRUCT(ht_coll);

    /* For simplicity, we always schema-qualify the name */
    nsp_name = get_namespace_name(coll_rec->collnamespace);
    coll_name = pstrdup(NameStr(coll_rec->collname));
    result = list_make2(makeString(nsp_name), makeString(coll_name));

    ReleaseSysCache(ht_coll);
    return result;
}

/*
 * get_opclass			- fetch qualified name of an index operator class
 *
 * If the opclass is the default for the given actual_datatype, then
 * the return value is NIL.
 */
static List* get_opclass(Oid opclass, Oid actual_datatype)
{
    List* result = NIL;
    HeapTuple ht_opc;
    Form_pg_opclass opc_rec;

    ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
    if (!HeapTupleIsValid(ht_opc))
        ereport(ERROR,
            (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                errmodule(MOD_OPT),
                errmsg("cache lookup failed for opclass %u", opclass)));
    opc_rec = (Form_pg_opclass)GETSTRUCT(ht_opc);

    if (GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass) {
        /* For simplicity, we always schema-qualify the name */
        char* nsp_name = get_namespace_name(opc_rec->opcnamespace);
        char* opc_name = pstrdup(NameStr(opc_rec->opcname));

        result = list_make2(makeString(nsp_name), makeString(opc_name));
    }

    ReleaseSysCache(ht_opc);
    return result;
}

static bool IsPartitionKeyInParmaryKeyAndUniqueKey(const char *pkname, const List* constraintKeys)
{
    bool found = false;
    ListCell *ixcell = NULL;

    foreach (ixcell, constraintKeys) {
        char *ikname = strVal(lfirst(ixcell));

        if (!strcmp(pkname, ikname)) {
            found = true;
            break;
        }
    }

    return found;
}

static bool IsPartitionKeyAllInParmaryKeyAndUniqueKey(const List *partitionKey, const List *constraintKeys)
{
    ListCell *pkcell = NULL;
    bool found = true;

    foreach (pkcell, partitionKey) {
        ColumnRef *colref = (ColumnRef *)lfirst(pkcell);
        char *pkname = ((Value *)linitial(colref->fields))->val.str;

        found = found && IsPartitionKeyInParmaryKeyAndUniqueKey(pkname, constraintKeys);

        if (!found) {
            return false;
        }
    }

    return found;
}

/*
 * transformIndexConstraints
 *		Handle UNIQUE, PRIMARY KEY, EXCLUDE constraints, which create indexes.
 *		We also merge in any index definitions arising from
 *		LIKE ... INCLUDING INDEXES.
 */
static void transformIndexConstraints(CreateStmtContext* cxt)
{
    IndexStmt* index = NULL;
    List* indexlist = NIL;
    ListCell* lc = NULL;

    /*
     * Run through the constraints that need to generate an index. For PRIMARY
     * KEY, mark each column as NOT NULL and create an index. For UNIQUE or
     * EXCLUDE, create an index as for PRIMARY KEY, but do not insist on NOT
     * NULL.
     */
    foreach (lc, cxt->ixconstraints) {
        Constraint* constraint = (Constraint*)lfirst(lc);
        bool mustGlobal = false;

        AssertEreport(IsA(constraint, Constraint), MOD_OPT, "");
        AssertEreport(constraint->contype == CONSTR_PRIMARY || constraint->contype == CONSTR_UNIQUE ||
                          constraint->contype == CONSTR_EXCLUSION,
            MOD_OPT,
            "");

        if (cxt->ispartitioned && !cxt->isalter) {
            AssertEreport(PointerIsValid(cxt->partitionKey), MOD_OPT, "");

            /*
             * @hdfs
             * Columns of PRIMARY KEY/UNIQUE could be any columns on HDFS partition table.
             * If the partition foreign table will support real index, the following code must
             * be modified.
             */
            if (IsA(cxt->node, CreateForeignTableStmt) &&
                isObsOrHdfsTableFormSrvName(((CreateForeignTableStmt*)cxt->node)->servername)) {
                /* Do nothing */
            } else if (constraint->contype == CONSTR_EXCLUSION) {
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("Partitioned table does not support EXCLUDE index")));
            } else {
                bool checkPartitionKey = IsPartitionKeyAllInParmaryKeyAndUniqueKey(cxt->partitionKey, constraint->keys);
                if (cxt->subPartitionKey != NULL) {
                    // for subpartition table.
                    bool checkSubPartitionKey =
                        IsPartitionKeyAllInParmaryKeyAndUniqueKey(cxt->subPartitionKey, constraint->keys);
                    mustGlobal = !(checkPartitionKey && checkSubPartitionKey);
                } else {
                    // for partition table.
                    mustGlobal = !checkPartitionKey;
                }
            }
        }

        index = transformIndexConstraint(constraint, cxt, mustGlobal);

        indexlist = lappend(indexlist, index);
    }

    /* Add in any indexes defined by LIKE ... INCLUDING INDEXES */
    foreach (lc, cxt->inh_indexes) {
        index = (IndexStmt*)lfirst(lc);
        if (index->primary) {
            if (cxt->pkey != NULL)
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                        errmsg("multiple primary keys for table \"%s\" are not allowed", cxt->relation->relname)));
            cxt->pkey = index;
        }

        indexlist = lappend(indexlist, index);
    }

    /*
     * Scan the index list and remove any redundant index specifications. This
     * can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A
     * strict reading of SQL92 would suggest raising an error instead, but
     * that strikes me as too anal-retentive. - tgl 2001-02-14
     *
     * XXX in ALTER TABLE case, it'd be nice to look for duplicate
     * pre-existing indexes, too.
     */
    AssertEreport(cxt->alist == NIL, MOD_OPT, "");
    if (cxt->pkey != NULL) {
        /* Make sure we keep the PKEY index in preference to others... */
        cxt->alist = list_make1(cxt->pkey);
    }

    foreach (lc, indexlist) {
        bool keep = true;
        ListCell* k = NULL;

        index = (IndexStmt*)lfirst(lc);
        /* if it's pkey, it's already in cxt->alist */
        if (index == cxt->pkey)
            continue;

        /*
         * For create table like, if the table is resizing, don't remove redundant index,
         * because we need to keep the index totally same with origin table's indices.
         */
        if (cxt->isResizing) {
            cxt->alist = lappend(cxt->alist, index);
            continue;
        }

        foreach (k, cxt->alist) {
            IndexStmt* priorindex = (IndexStmt*)lfirst(k);
            bool accessMethodEqual = index->accessMethod == priorindex->accessMethod ||
                (index->accessMethod != NULL && priorindex->accessMethod != NULL &&
                 strcmp(index->accessMethod, priorindex->accessMethod) == 0);

            if (equal(index->indexParams, priorindex->indexParams) &&
                equal(index->indexIncludingParams, priorindex->indexIncludingParams) &&
                equal(index->whereClause, priorindex->whereClause) &&
                equal(index->excludeOpNames, priorindex->excludeOpNames) &&
                accessMethodEqual &&
                index->deferrable == priorindex->deferrable && index->initdeferred == priorindex->initdeferred) {
                priorindex->unique = priorindex->unique || index->unique;

                /*
                 * If the prior index is as yet unnamed, and this one is
                 * named, then transfer the name to the prior index. This
                 * ensures that if we have named and unnamed constraints,
                 * we'll use (at least one of) the names for the index.
                 */
                if (priorindex->idxname == NULL)
                    priorindex->idxname = index->idxname;
                keep = false;
                break;
            }
        }

        if (keep)
            cxt->alist = lappend(cxt->alist, index);
    }
    /* Check whether index constraints meet the auto_increment column requirements */
    CheckAutoIncrementIndex(cxt);
}

/*
 * If it's ALTER TABLE ADD CONSTRAINT USING INDEX,
 * verify the index is usable.
 */
static void checkConditionForTransformIndex(
    Constraint* constraint, CreateStmtContext* cxt, Oid index_oid, Relation index_rel)
{
    if (constraint == NULL || cxt == NULL || index_rel == NULL)
        return;

    char* index_name = constraint->indexname;
    Form_pg_index index_form = index_rel->rd_index;
    Relation heap_rel = cxt->rel;

    /* Check that it does not have an associated constraint already */
    if (OidIsValid(get_index_constraint(index_oid)))
        ereport(ERROR,
            (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                errmsg("index \"%s\" is already associated with a constraint", index_name),
                parser_errposition(cxt->pstate, constraint->location)));

    /* Perform validity checks on the index */
    if (index_form->indrelid != RelationGetRelid(heap_rel))
        ereport(ERROR,
            (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                errmsg("index \"%s\" does not belong to table \"%s\"", index_name, RelationGetRelationName(heap_rel)),
                parser_errposition(cxt->pstate, constraint->location)));

    if (!IndexIsValid(index_form))
        ereport(ERROR,
            (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                errmsg("index \"%s\" is not valid", index_name),
                parser_errposition(cxt->pstate, constraint->location)));

    if (!index_form->indisunique)
        ereport(ERROR,
            (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                errmsg("\"%s\" is not a unique index", index_name),
                errdetail("Cannot create a primary key or unique constraint using such an index."),
                parser_errposition(cxt->pstate, constraint->location)));

#ifdef ENABLE_MULTIPLE_NODES
    /* create a primary key or unique constraint using such an index
     * is not supported in distributed database.
     */
    if (RelationGetIndexExpressions(index_rel) != NIL)
        ereport(ERROR,
            (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                errmsg("index \"%s\" contains expressions", index_name),
                errdetail("Cannot create a primary key or unique constraint using such an index."),
                parser_errposition(cxt->pstate, constraint->location)));
#else
    if (!DB_IS_CMPT(B_FORMAT)) {
        if (RelationGetIndexExpressions(index_rel) != NIL)
            ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                    errmsg("index \"%s\" contains expressions", index_name),
                    errdetail("Cannot create a primary key or unique constraint using such an index."),
                    parser_errposition(cxt->pstate, constraint->location)));
    }
#endif

    if (RelationGetIndexPredicate(index_rel) != NIL)
        ereport(ERROR,
            (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                errmsg("\"%s\" is a partial index", index_name), 
                errdetail("Cannot create a primary key or unique constraint using such an index."),
                parser_errposition(cxt->pstate, constraint->location)));

    /*
     * It's probably unsafe to change a deferred index to non-deferred. (A
     * non-constraint index couldn't be deferred anyway, so this case
     * should never occur; no need to sweat, but let's check it.)
     */
    if (!index_form->indimmediate && !constraint->deferrable)
        ereport(ERROR,
            (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                errmsg("\"%s\" is a deferrable index", index_name),
                errdetail("Cannot create a non-deferrable constraint using a deferrable index."),
                parser_errposition(cxt->pstate, constraint->location)));

    /*
     * Insist on it being a btree.	That's the only kind that supports
     * uniqueness at the moment anyway; but we must have an index that
     * exactly matches what you'd get from plain ADD CONSTRAINT syntax,
     * else dump and reload will produce a different index (breaking
     * pg_upgrade in particular).
     */
    if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, false) &&
        index_rel->rd_rel->relam != get_am_oid(DEFAULT_USTORE_INDEX_TYPE, false))
        ereport(ERROR,
            (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                errmsg("index \"%s\" is not a btree", index_name),
                parser_errposition(cxt->pstate, constraint->location)));
}

/*
 * transformIndexConstraint
 *		Transform one UNIQUE, PRIMARY KEY, or EXCLUDE constraint for
 *		transformIndexConstraints.
 */
static IndexStmt* transformIndexConstraint(Constraint* constraint, CreateStmtContext* cxt, bool mustGlobal)
{
    IndexStmt* index = NULL;
    ListCell* lc = NULL;

    index = makeNode(IndexStmt);

    index->unique = (constraint->contype != CONSTR_EXCLUSION);
    index->primary = (constraint->contype == CONSTR_PRIMARY);
    if (index->primary) {
        if (cxt->pkey != NULL) {
            if (0 == pg_strncasecmp(cxt->stmtType, CREATE_FOREIGN_TABLE, strlen(cxt->stmtType)) ||
                0 == pg_strncasecmp(cxt->stmtType, ALTER_FOREIGN_TABLE, strlen(cxt->stmtType))) {
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                        errmsg(
                            "Multiple primary keys for foreign table \"%s\" are not allowed.", cxt->relation->relname),
                        parser_errposition(cxt->pstate, constraint->location)));
            } else {
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                        errmsg("multiple primary keys for table \"%s\" are not allowed", cxt->relation->relname),
                        parser_errposition(cxt->pstate, constraint->location)));
            }
        }
        cxt->pkey = index;

        /*
         * In ALTER TABLE case, a primary index might already exist, but
         * DefineIndex will check for it.
         */
    }
    index->isconstraint = true;
    index->deferrable = constraint->deferrable;
    index->initdeferred = constraint->initdeferred;
    index->isvalidated = constraint->initially_valid;
    index->isdisable = constraint->isdisable;

    if (constraint->conname != NULL)
        index->idxname = pstrdup(constraint->conname);
    else
        index->idxname = NULL; /* DefineIndex will choose name */

    index->relation = cxt->relation;
    index->accessMethod = constraint->access_method;
    index->options = constraint->options;
    index->tableSpace = constraint->indexspace;
    index->whereClause = constraint->where_clause;
    index->indexParams = NIL;
    index->indexIncludingParams = NIL;
    index->excludeOpNames = NIL;
    index->idxcomment = NULL;
    index->indexOid = InvalidOid;
    index->oldNode = InvalidOid;
    index->oldPSortOid = InvalidOid;
    index->concurrent = false;
    index->isGlobal = mustGlobal;

    /*
     * @hdfs
     * The foreign table dose not have index. the HDFS foreign table has informational
     * constraint which is not a index.
     * If the partition foreign table will support real index, the following code must
     * be modified.
     */
    if (0 == pg_strncasecmp(cxt->stmtType, CREATE_FOREIGN_TABLE, strlen(cxt->stmtType)) ||
        0 == pg_strncasecmp(cxt->stmtType, ALTER_FOREIGN_TABLE, strlen(cxt->stmtType))) {
        index->isPartitioned = false;
    } else {
        index->isPartitioned = cxt->ispartitioned;

    }

    index->inforConstraint = constraint->inforConstraint;

    /*
     * If it's ALTER TABLE ADD CONSTRAINT USING INDEX, look up the index and
     * verify it's usable, then extract the implied column name list.  (We
     * will not actually need the column name list at runtime, but we need it
     * now to check for duplicate column entries below.)
     */
    if (constraint->indexname != NULL) {
        char* index_name = constraint->indexname;
        Relation heap_rel = cxt->rel;
        Oid index_oid;
        Relation index_rel;
        Form_pg_index index_form;
        oidvector* indclass = NULL;
        Datum indclassDatum;
        HeapTuple tup_idx;
        List* indexprs = NIL;
        ListCell* indexpr_item = NULL;
        Datum indoptionDatum;
        int2vector* indoption = NULL;
        bool isnull = true;
        int i;
        int indnkeyatts;

        /* Grammar should not allow this with explicit column list */
        AssertEreport(constraint->keys == NIL, MOD_OPT, "");

        /* Grammar should only allow PRIMARY and UNIQUE constraints */
        AssertEreport(constraint->contype == CONSTR_PRIMARY || constraint->contype == CONSTR_UNIQUE, MOD_OPT, "");

        /* Must be ALTER, not CREATE, but grammar doesn't enforce that */
        if (!cxt->isalter)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("cannot use an existing index in CREATE TABLE"),
                    parser_errposition(cxt->pstate, constraint->location)));

        /* Look for the index in the same schema as the table */
        index_oid = get_relname_relid(index_name, RelationGetNamespace(heap_rel));

        if (!OidIsValid(index_oid))
            ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_OBJECT),
                    errmsg("index \"%s\" does not exist", index_name),
                    parser_errposition(cxt->pstate, constraint->location)));

        /* Open the index (this will throw an error if it is not an index) */
        index_rel = index_open(index_oid, AccessShareLock);
        index_form = index_rel->rd_index;
        tup_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
        if (!HeapTupleIsValid(tup_idx)) {
            ereport(ERROR,
                (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                    errmsg("cache lookup failed for index %u", index_oid)));
        }

        indoptionDatum = SysCacheGetAttr(INDEXRELID, tup_idx, Anum_pg_index_indoption, &isnull);
        AssertEreport(!isnull, MOD_OPT, "");
        indoption = (int2vector*)DatumGetPointer(indoptionDatum);

        if (!heap_attisnull(tup_idx, Anum_pg_index_indexprs, NULL)) {
            Datum exprsDatum;
            indexprs = NIL;
            char* exprsString = NULL;

            exprsDatum = SysCacheGetAttr(INDEXRELID, tup_idx, Anum_pg_index_indexprs, &isnull);
            AssertEreport(!isnull, MOD_OPT, "");
            exprsString = TextDatumGetCString(exprsDatum);
            indexprs = (List*)stringToNode(exprsString);
            pfree_ext(exprsString);
        } else {
            indexprs = NIL;
        }

        indexpr_item = list_head(indexprs);
        indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index_rel);

        /* check the conditons for this function,
         * and verify the index is usable
         */
        checkConditionForTransformIndex(constraint, cxt, index_oid, index_rel);

        /* Must get indclass the hard way */
        indclassDatum = SysCacheGetAttr(INDEXRELID, index_rel->rd_indextuple, Anum_pg_index_indclass, &isnull);
        AssertEreport(!isnull, MOD_OPT, "");
        indclass = (oidvector*)DatumGetPointer(indclassDatum);

        for (i = 0; i < index_form->indnatts; i++) {
            int2 attnum = index_form->indkey.values[i];
            Form_pg_attribute attform;
            char* attname = NULL;
            Oid defopclass;

            if (i < indnkeyatts) {
                IndexElem *idxElem = makeNode(IndexElem);
                int16 opt = indoption->values[i];

                if (attnum > 0) {
                    // simple index column
                    AssertEreport(attnum <= heap_rel->rd_att->natts, MOD_OPT, "");
                    attform = &heap_rel->rd_att->attrs[attnum - 1];
                    
                    idxElem->name = pstrdup(NameStr(attform->attname));
                    idxElem->expr = NULL;
                    idxElem->indexcolname = NULL;
                    idxElem->collation = NIL;
                    idxElem->opclass = NIL;
                    idxElem->ordering = (opt & INDOPTION_DESC) ? SORTBY_DESC : SORTBY_DEFAULT;
                    idxElem->nulls_ordering = SORTBY_NULLS_DEFAULT;
                } else if (attnum == 0) {
                    // expresional index
                    Node *indexkey = NULL;
                    attform = NULL;

                    if (indexpr_item == NULL) {
                        ereport(ERROR,
                            (errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
                                errmsg("too few entries in indexprs list")));
                    }

                    indexkey = (Node*)lfirst(indexpr_item);
                    indexpr_item = lnext(indexpr_item);

                    idxElem->name = NULL;
                    idxElem->expr = indexkey;
                    idxElem->indexcolname = NULL;
                    idxElem->collation = NIL;
                    idxElem->opclass = NIL;
                    idxElem->ordering = (opt & INDOPTION_DESC) ? SORTBY_DESC : SORTBY_DEFAULT;
                    idxElem->nulls_ordering = SORTBY_NULLS_DEFAULT;
                } else {
                    attform = SystemAttributeDefinition(attnum, heap_rel->rd_rel->relhasoids, 
                        RELATION_HAS_BUCKET(heap_rel), RELATION_HAS_UIDS(heap_rel));
                    attname = pstrdup(NameStr(attform->attname));
                }

#ifdef ENABLE_MULTIPLE_NODES
                /* create a primary key or unique constraint using such an index
                 * is not supported in distributed database.
                 */
                 if (attform == 0) {
                     ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("get pg_attribute failed.")));
                 }

                defopclass = GetDefaultOpClass(attform->atttypid, index_rel->rd_rel->relam);
                if (indclass->values[i] != defopclass || index_rel->rd_indoption[i] != 0)
                    ereport(ERROR,
                        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                            errmsg("index \"%s\" does not have default sorting behavior", index_name),
                            errdetail("Cannot create a primary key or unique constraint using such an index."),
                            parser_errposition(cxt->pstate, constraint->location)));
#else
                /*
                 * Insist on default opclass and sort options.  While the
                 * index would still work as a constraint with non-default
                 * settings, it might not provide exactly the same uniqueness
                 * semantics as you'd get from a normally-created constraint;
                 * and there's also the dump/reload problem mentioned above.
                 */
                if (!DB_IS_CMPT(B_FORMAT)) {
                    if (attform == 0) {
                        ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("get pg_attribute failed.")));
                    }

                    defopclass = GetDefaultOpClass(attform->atttypid, index_rel->rd_rel->relam);
                    if (indclass->values[i] != defopclass || index_rel->rd_indoption[i] != 0)
                        ereport(ERROR,
                            (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                errmsg("index \"%s\" does not have default sorting behavior", index_name),
                                errdetail("Cannot create a primary key or unique constraint using such an index."),
                                parser_errposition(cxt->pstate, constraint->location)));
                }
#endif

                constraint->keys = (attnum >= 0) ? 
                    lappend(constraint->keys, idxElem) : lappend(constraint->keys, makeString(attname));
            } else {
                /*
                 * We shouldn't see attnum == 0 here, since we already rejected
                 * expression indexes.	If we do, SystemAttributeDefinition will
                 * throw an error.
                 */
                if (attnum > 0) {
                    AssertEreport(attnum <= heap_rel->rd_att->natts, MOD_OPT, "");
                    attform = &heap_rel->rd_att->attrs[attnum - 1];
                } else {
                    attform = SystemAttributeDefinition(attnum, heap_rel->rd_rel->relhasoids,
                        RELATION_HAS_BUCKET(heap_rel), RELATION_HAS_UIDS(heap_rel));
                }
                attname = pstrdup(NameStr(attform->attname));
                constraint->including = lappend(constraint->including, makeString(attname));
            }

        }

        /* Close the index relation but keep the lock */
        relation_close(index_rel, NoLock);
        ReleaseSysCache(tup_idx);

        index->indexOid = index_oid;

        /* save the original index name, it wll be replace by constraint */
        DefElem *def = makeDefElem("origin_indexname", (Node*)makeString(constraint->indexname));
        index->options = lappend(index->options, def);
    }

    /*
     * If it's an EXCLUDE constraint, the grammar returns a list of pairs of
     * IndexElems and operator names.  We have to break that apart into
     * separate lists.
     */
    if (constraint->contype == CONSTR_EXCLUSION) {
        foreach (lc, constraint->exclusions) {
            List* pair = (List*)lfirst(lc);
            IndexElem* elem = NULL;
            List* opname = NIL;

            Assert(list_length(pair) == 2);
            elem = (IndexElem*)linitial(pair);
            Assert(IsA(elem, IndexElem));
            opname = (List*)lsecond(pair);
            Assert(IsA(opname, List));

            index->indexParams = lappend(index->indexParams, elem);
            index->excludeOpNames = lappend(index->excludeOpNames, opname);
        }
    } else {

        /*
         * For UNIQUE and PRIMARY KEY, we just have a list of column names.
         *
         * Make sure referenced keys exist.  If we are making a PRIMARY KEY index,
         * also make sure they are NOT NULL, if possible. (Although we could leave
         * it to DefineIndex to mark the columns NOT NULL, it's more efficient to
         * get it right the first time.)
         */
        foreach (lc, constraint->keys) {
            IndexElem* idxElem = (IndexElem*)lfirst(lc);
            bool found = false;
            ColumnDef* column = NULL;
            ListCell* columns = NULL;
            IndexElem* iparam = NULL;

            if (idxElem->name != NULL) {
                foreach (columns, cxt->columns) {
                    column = (ColumnDef*)lfirst(columns);
                    AssertEreport(IsA(column, ColumnDef), MOD_OPT, "");
                    if (strcmp(column->colname, idxElem->name) == 0) {
                        found = true;
                        break;
                    }
                }

                if (found) {
                    /* found column in the new table; force it to be NOT NULL */
                    if (constraint->contype == CONSTR_PRIMARY && !constraint->inforConstraint->nonforced)
                        column->is_not_null = TRUE;
                } else if (SystemAttributeByName(idxElem->name, cxt->hasoids) != NULL) {
                    /*
                     * column will be a system column in the new table, so accept it.
                     * System columns can't ever be null, so no need to worry about
                     * PRIMARY/NOT NULL constraint.
                     */
                    found = true;
                } else if (cxt->inhRelations != NIL) {
                    /* try inherited tables */
                    ListCell* inher = NULL;

                    foreach (inher, cxt->inhRelations) {
                        RangeVar* inh = (RangeVar*)lfirst(inher);
                        Relation rel;
                        int count;

                        AssertEreport(IsA(inh, RangeVar), MOD_OPT, "");
                        rel = heap_openrv(inh, AccessShareLock);
                        if (rel->rd_rel->relkind != RELKIND_RELATION)
                            ereport(ERROR,
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                    errmsg("inherited relation \"%s\" is not a table", inh->relname)));
                        for (count = 0; count < rel->rd_att->natts; count++) {
                            Form_pg_attribute inhattr = &rel->rd_att->attrs[count];
                            char* inhname = NameStr(inhattr->attname);

                            if (inhattr->attisdropped)
                                continue;
                            if (strcmp(idxElem->name, inhname) == 0) {
                                found = true;

                                /*
                                 * We currently have no easy way to force an inherited
                                 * column to be NOT NULL at creation, if its parent
                                 * wasn't so already. We leave it to DefineIndex to
                                 * fix things up in this case.
                                 */
                                break;
                            }
                        }
                        heap_close(rel, NoLock);
                        if (found)
                            break;
                    }
                }

                /*
                 * In the ALTER TABLE case, don't complain about index keys not
                 * created in the command; they may well exist already. DefineIndex
                 * will complain about them if not, and will also take care of marking
                 * them NOT NULL.
                 */
                if (!found && !cxt->isalter)
                    ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_COLUMN),
                            errmsg("column \"%s\" named in key does not exist", idxElem->name),
                            parser_errposition(cxt->pstate, constraint->location)));

                /* Check for PRIMARY KEY(foo, foo) */
                foreach (columns, index->indexParams) {
                    iparam = (IndexElem*)lfirst(columns);
                    if (iparam->name && strcmp(idxElem->name, iparam->name) == 0) {
                        if (index->primary)
                            ereport(ERROR,
                                (errcode(ERRCODE_DUPLICATE_COLUMN),
                                    errmsg("column \"%s\" appears twice in primary key constraint", idxElem->name),
                                    parser_errposition(cxt->pstate, constraint->location)));
                        else
                            ereport(ERROR,
                                (errcode(ERRCODE_DUPLICATE_COLUMN),
                                    errmsg("column \"%s\" appears twice in unique constraint", idxElem->name),
                                    parser_errposition(cxt->pstate, constraint->location)));
                    }
                }
            }

#ifdef PGXC
            /*
             * Set fallback distribution column.
             * If not set, set it to first column in index.
             * If primary key, we prefer that over a unique constraint.
             */
            if (index->indexParams == NIL && (index->primary || cxt->fallback_dist_col == NULL)) {
                if (cxt->fallback_dist_col != NULL) {
                    list_free_deep(cxt->fallback_dist_col);
                    cxt->fallback_dist_col = NULL;
                }

                if (idxElem->name != NULL) {
                    cxt->fallback_dist_col = lappend(cxt->fallback_dist_col, makeString(pstrdup(idxElem->name)));
                }
            }
#endif

            /* OK, add it to the index definition */
            iparam = makeNode(IndexElem);
            if (idxElem->name != NULL) {
                iparam->name = pstrdup(idxElem->name);
                iparam->expr = NULL;
                iparam->indexcolname = NULL;
                iparam->collation = NIL;
                iparam->opclass = NIL;
                if (idxElem->ordering == SORTBY_ASC || idxElem->ordering == SORTBY_DESC) {
                    iparam->ordering = idxElem->ordering;
                } else {
                    iparam->ordering = SORTBY_DEFAULT;
                }
                iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
            } else if (idxElem->expr != NULL) {
                iparam->name = NULL;
                iparam->expr = idxElem->expr;
                iparam->indexcolname = NULL;
                iparam->collation = NIL;
                iparam->opclass = NIL;
                if (idxElem->ordering == SORTBY_ASC || idxElem->ordering == SORTBY_DESC) {
                    iparam->ordering = idxElem->ordering;
                } else {
                    iparam->ordering = SORTBY_DEFAULT;
                }
                iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
            }
            
            index->indexParams = lappend(index->indexParams, iparam);
        }
    }
    /* unique/primary key constraint is considered an index */
    index->indexOptions = constraint->constraintOptions;
    /* Add included columns to index definition */
    foreach (lc, constraint->including) {
        char* key = strVal(lfirst(lc));
        bool found = false;
        ColumnDef* column = NULL;
        ListCell* columns = NULL;
        IndexElem* iparam = NULL;

        foreach (columns, cxt->columns) {
            column = lfirst_node(ColumnDef, columns);
            if (strcmp(column->colname, key) == 0) {
                found = true;
                break;
            }
        }

        if (!found) {
            if (SystemAttributeByName(key, cxt->hasoids) != NULL) {
                /*
                 * column will be a system column in the new table, so accept
                 * it. System columns can't ever be null, so no need to worry
                 * about PRIMARY/NOT NULL constraint.
                 */
                found = true;
            } else if (cxt->inhRelations) {
                /* try inherited tables */
                ListCell* inher = NULL;

                foreach (inher, cxt->inhRelations) {
                    RangeVar* inh = lfirst_node(RangeVar, inher);
                    Relation rel = NULL;
                    int count = 0;

                    rel = heap_openrv(inh, AccessShareLock);
                    /* check user requested inheritance from valid relkind */
                    if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE
                        && rel->rd_rel->relkind != RELKIND_STREAM) {
                        ereport(ERROR,
                            (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                errmsg("inherited relation \"%s\" is not a table or foreign table", inh->relname)));
                    }
                    for (count = 0; count < rel->rd_att->natts; count++) {
                        Form_pg_attribute inhattr = &rel->rd_att->attrs[count];
                        char* inhname = NameStr(inhattr->attname);

                        if (inhattr->attisdropped)
                            continue;
                        if (strcmp(key, inhname) == 0) {
                            found = true;

                            /*
                             * We currently have no easy way to force an
                             * inherited column to be NOT NULL at creation, if
                             * its parent wasn't so already. We leave it to
                             * DefineIndex to fix things up in this case.
                             */
                            break;
                        }
                    }
                    heap_close(rel, NoLock);
                    if (found)
                        break;
                }
            }
        }

        /*
         * In the ALTER TABLE case, don't complain about index keys not
         * created in the command; they may well exist already. DefineIndex
         * will complain about them if not, and will also take care of marking
         * them NOT NULL.
         */
        if (!found && !cxt->isalter)
            ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_COLUMN),
                    errmsg("column \"%s\" named in key does not exist", key),
                    parser_errposition(cxt->pstate, constraint->location)));

        /* OK, add it to the index definition */
        iparam = makeNode(IndexElem);
        iparam->name = pstrdup(key);
        iparam->expr = NULL;
        iparam->indexcolname = NULL;
        iparam->collation = NIL;
        iparam->opclass = NIL;
        index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
    }

    return index;
}

/*
 * transformFKConstraints
 *		handle FOREIGN KEY constraints
 */
static void transformFKConstraints(CreateStmtContext* cxt, bool skipValidation, bool isAddConstraint)
{
    ListCell* fkclist = NULL;

    if (cxt->fkconstraints == NIL)
        return;

    /*
     * If CREATE TABLE or adding a column with NULL default, we can safely
     * skip validation of FK constraints, and nonetheless mark them valid.
     * (This will override any user-supplied NOT VALID flag.)
     */
    if (skipValidation) {
        foreach (fkclist, cxt->fkconstraints) {
            Constraint* constraint = (Constraint*)lfirst(fkclist);

            constraint->skip_validation = true;
            constraint->initially_valid = true;
            constraint->isdisable = false;
#ifdef PGXC
            /*
             * Set fallback distribution column.
             * If not yet set, set it to first column in FK constraint
             * if it references a partitioned table
             */
            if (IS_PGXC_COORDINATOR && cxt->fallback_dist_col == NIL && list_length(constraint->pk_attrs) != 0) {
                if (list_length(constraint->pk_attrs) != list_length(constraint->fk_attrs)) {
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_FOREIGN_KEY),
                            errmsg("number of referencing and referenced columns for foreign key disagree")));
                }

                Oid pk_rel_id = RangeVarGetRelid(constraint->pktable, NoLock, false);
                RelationLocInfo* locInfo = GetRelationLocInfo(pk_rel_id);

                if (locInfo != NULL && locInfo->partAttrNum != NIL) {
                    int i = 0;
                    char* colstr = NULL;
                    ListCell *cell = NULL, *pk_cell = NULL;
                    AttrNumber attnum, pk_attnum;

                    foreach (cell, locInfo->partAttrNum) {
                        attnum = lfirst_int(cell);
                        /*This table is replication*/
                        if (0 == attnum) {
                            break;
                        }

                        i = 0;
                        foreach (pk_cell, constraint->pk_attrs) {
                            pk_attnum = get_attnum(pk_rel_id, strVal(lfirst(pk_cell)));
                            if (attnum == pk_attnum) {
                                break;
                            }
                            i++;
                        }
                        if (pk_cell == NULL) {
                            list_free_deep(cxt->fallback_dist_col);
                            cxt->fallback_dist_col = NULL;
                            break;
                        } else {
                            colstr = strdup(strVal(list_nth(constraint->fk_attrs, i)));
                            cxt->fallback_dist_col = lappend(cxt->fallback_dist_col, makeString(pstrdup(colstr)));
                        }

                        if (colstr != NULL)
                            free(colstr);
                        colstr = NULL;
                    }
                }
            }
#endif
        }
    }

    /*
     * For CREATE TABLE or ALTER TABLE ADD COLUMN, gin up an ALTER TABLE ADD
     * CONSTRAINT command to execute after the basic command is complete. (If
     * called from ADD CONSTRAINT, that routine will add the FK constraints to
     * its own subcommand list.)
     *
     * Note: the ADD CONSTRAINT command must also execute after any index
     * creation commands.  Thus, this should run after
     * transformIndexConstraints, so that the CREATE INDEX commands are
     * already in cxt->alist.
     */
    if (!isAddConstraint) {
        AlterTableStmt* alterstmt = makeNode(AlterTableStmt);

        alterstmt->relation = cxt->relation;
        alterstmt->cmds = NIL;
        alterstmt->relkind = OBJECT_TABLE;

        foreach (fkclist, cxt->fkconstraints) {
            Constraint* constraint = (Constraint*)lfirst(fkclist);
            AlterTableCmd* altercmd = makeNode(AlterTableCmd);

            altercmd->subtype = AT_ProcessedConstraint;
            altercmd->name = NULL;
            altercmd->def = (Node*)constraint;
            alterstmt->cmds = lappend(alterstmt->cmds, altercmd);
        }

        cxt->alist = lappend(cxt->alist, alterstmt);
    }
}

/*
 * transformIndexStmt - parse analysis for CREATE INDEX and ALTER TABLE
 *
 * Note: this is a no-op for an index not using either index expressions or
 * a predicate expression.	There are several code paths that create indexes
 * without bothering to call this, because they know they don't have any
 * such expressions to deal with.
 */
IndexStmt* transformIndexStmt(Oid relid, IndexStmt* stmt, const char* queryString)
{
    Relation rel;
    ParseState* pstate = NULL;
    RangeTblEntry* rte = NULL;
    ListCell* l = NULL;
    int crossbucketopt = -1; /* -1 means the SQL statement doesn't contain crossbucket option */

    /* Set up pstate */
    pstate = make_parsestate(NULL);
    pstate->p_sourcetext = queryString;

    /*
     * Put the parent table into the rtable so that the expressions can refer
     * to its fields without qualification.  Caller is responsible for locking
     * relation, but we still need to open it.
     */
    rel = relation_open(relid, NoLock);
    rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true, true, false, true);

#ifdef ENABLE_MOT
    if (RelationIsForeignTable(rel) && isMOTFromTblOid(RelationGetRelid(rel))) {
        stmt->internal_flag = true;
    }
#endif

    /* default partition index is set to Global index */
    if (RELATION_IS_PARTITIONED(rel) && !stmt->isPartitioned && DEFAULT_CREATE_GLOBAL_INDEX) {
        stmt->isGlobal = true;
    }

    /* set to crossbucket flag as necessary */
    if (RELATION_HAS_BUCKET(rel) && !stmt->crossbucket) {
        stmt->crossbucket = get_crossbucket_option(&stmt->options, stmt->isGlobal, stmt->accessMethod, &crossbucketopt);
    }

    bool isColStore = RelationIsColStore(rel);
    if (stmt->accessMethod == NULL) {
        if (!isColStore) {
            /* row store using btree index by default */
            if (!RelationIsUstoreFormat(rel)) {
                stmt->accessMethod = DEFAULT_INDEX_TYPE;
            } else {
                stmt->accessMethod = DEFAULT_USTORE_INDEX_TYPE;
            }
        } else {
            if (stmt->isGlobal) {
                if (stmt->isconstraint) {
                    ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                errmsg("Invalid PRIMARY KEY/UNIQUE constraint for column partitioned table"),
                                errdetail("Columns of PRIMARY KEY/UNIQUE constraint Must contain PARTITION KEY")));
                }
                ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 
                        errmsg("Global partition index does not support column store.")));
            }
            if (crossbucketopt > 0) {
                ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 
                        errmsg("cross-bucket index does not support column store")));
            }
            if (stmt->isconstraint && (stmt->unique || stmt->primary)) {
                /* cstore unique and primary key only support cbtree */
                stmt->accessMethod = CSTORE_BTREE_INDEX_TYPE;
            } else {
                /* column store using psort index by default */
                stmt->accessMethod = DEFAULT_CSTORE_INDEX_TYPE;
            }
        }
    } else if (stmt->isGlobal) {
        /* Global partition index only support btree index */
        if (pg_strcasecmp(stmt->accessMethod, DEFAULT_INDEX_TYPE) != 0 &&
            pg_strcasecmp(stmt->accessMethod, DEFAULT_USTORE_INDEX_TYPE) != 0) {
            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 
                    errmsg("Global partition index only support btree and ubtree.")));
        }
        if (isColStore) {
            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 
                    errmsg("Global partition index does not support column store.")));
        }
    } else if (crossbucketopt > 0) {
        if (pg_strcasecmp(stmt->accessMethod, DEFAULT_INDEX_TYPE) != 0) {
            /* report error when presenting crossbucket option with non-btree access method */
            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 
                    errmsg("cross-bucket index only supports btree")));
        }
        if (isColStore) {
            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 
                    errmsg("cross-bucket index does not support column store")));
        }
    } else {
        const bool isPsortMothed = (0 == pg_strcasecmp(stmt->accessMethod, DEFAULT_CSTORE_INDEX_TYPE));

        /* check if this is the cstore btree index */
        bool isCBtreeMethod = false;
        if (isColStore && ((0 == pg_strcasecmp(stmt->accessMethod, DEFAULT_INDEX_TYPE)) ||
                              (0 == pg_strcasecmp(stmt->accessMethod, CSTORE_BTREE_INDEX_TYPE)))) {
            stmt->accessMethod = CSTORE_BTREE_INDEX_TYPE;
            isCBtreeMethod = true;
        }

        /* check if this is the cstore gin btree index */
        bool isCGinBtreeMethod = false;
        if (isColStore && ((0 == pg_strcasecmp(stmt->accessMethod, DEFAULT_GIN_INDEX_TYPE)) ||
                              (0 == pg_strcasecmp(stmt->accessMethod, CSTORE_GINBTREE_INDEX_TYPE)))) {
            stmt->accessMethod = CSTORE_GINBTREE_INDEX_TYPE;
            isCGinBtreeMethod = true;
        }

        if (isCGinBtreeMethod && is_feature_disabled(MULTI_VALUE_COLUMN)) {
            /* cgin index is disabled */
            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Unsupport cgin index in this version")));
        }

        if (!isColStore && (0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_INDEX_TYPE)) &&
            (0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_GIN_INDEX_TYPE)) &&
            (0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_GIST_INDEX_TYPE)) &&
            (0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_IVFFLAT_INDEX_TYPE)) &&
            (0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_HNSW_INDEX_TYPE)) &&
            (0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_USTORE_INDEX_TYPE)) &&
            (0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_HASH_INDEX_TYPE)) &&
            (0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_BM25_INDEX_TYPE))) {
            /* row store only support btree/ubtree/gin/gist/hash index */
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("access method \"%s\" does not support row store", stmt->accessMethod)));
        }

        if (0 == pg_strcasecmp(stmt->accessMethod, DEFAULT_HASH_INDEX_TYPE) && 
            t_thrd.proc->workingVersionNum < SUPPORT_HASH_XLOG_VERSION_NUM) {
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("access method \"%s\" does not support row store", stmt->accessMethod)));
        }

        if (isColStore && (!isPsortMothed && !isCBtreeMethod && !isCGinBtreeMethod)) {
            /* column store support psort/cbtree/gin index */
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("access method \"%s\" does not support column store", stmt->accessMethod)));
        }

        if (u_sess->attr.attr_storage.enable_segment &&
            pg_strcasecmp(stmt->accessMethod, DEFAULT_HASH_INDEX_TYPE) == 0) {
            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 
                            errmsg("Hash index does not support when segment = on.")));    
        }
    }

    /* no to join list, yes to namespaces */
    addRTEtoQuery(pstate, rte, false, true, true);

    /* take care of the where clause */
    if (stmt->whereClause) {
        stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_INDEX_PREDICATE, "WHERE");
        /* we have to fix its collations too */
        assign_expr_collations(pstate, stmt->whereClause);
    }
    List* indexElements = NIL;
    /* take care of any index expressions */
    foreach (l, stmt->indexParams) {
        IndexElem* ielem = (IndexElem*)lfirst(l);

        if (ielem->expr) {
            /* Extract preliminary index col name before transforming expr */
            if (ielem->indexcolname == NULL)
                ielem->indexcolname = FigureIndexColname(ielem->expr);

            /* Now do parse transformation of the expression */
            ielem->expr = transformExpr(pstate, ielem->expr, EXPR_KIND_INDEX_EXPRESSION);

            /* We have to fix its collations too */
            assign_expr_collations(pstate, ielem->expr);

            /*
             * We check only that the result type is legitimate; this is for
             * consistency with what transformWhereClause() checks for the
             * predicate.  DefineIndex() will make more checks.
             */
#ifndef ENABLE_MULTIPLE_NODES
            ExcludeRownumExpr(pstate, (Node*)ielem->expr);
#endif
            if (!pstate->p_is_flt_frame) {
                if (expression_returns_set(ielem->expr))
                    ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("index expression cannot return a set")));
            }
            if (IsA(ielem->expr, PrefixKey) &&
                (0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_INDEX_TYPE)) &&
                (0 != pg_strcasecmp(stmt->accessMethod, DEFAULT_USTORE_INDEX_TYPE))) {
                ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("access method \"%s\" does not support prefix key", stmt->accessMethod)));
            }
        }

        if (IsElementExisted(indexElements, ielem)) {
            ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("duplicate column name")));
        }
        indexElements = lappend(indexElements, (IndexElem*)ielem);
    }

    list_free(indexElements);

    /*
     * Check that only the base rel is mentioned.
     */
    if (list_length(pstate->p_rtable) != 1)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                errmsg("index expressions and predicates can refer only to the table being indexed")));

    free_parsestate(pstate);

    /* Close relation */
    heap_close(rel, NoLock);

    /* check psort index compatible */
    if (0 == pg_strcasecmp(stmt->accessMethod, DEFAULT_CSTORE_INDEX_TYPE)) {
        checkPsortIndexCompatible(stmt);
    }

    /* check psort index compatible */
    if (0 == pg_strcasecmp(stmt->accessMethod, CSTORE_BTREE_INDEX_TYPE)) {
        checkCBtreeIndexCompatible(stmt);
    }

    /* check cgin btree index compatible */
    if (0 == pg_strcasecmp(stmt->accessMethod, CSTORE_GINBTREE_INDEX_TYPE)) {
        checkCGinBtreeIndexCompatible(stmt);
    }

    return stmt;
}

static bool IsElementExisted(List* indexElements, IndexElem* ielem)
{
    ListCell* lc = NULL;
    foreach (lc, indexElements) {
        IndexElem* theElement = (IndexElem*)lfirst(lc);
        if (equal(theElement, ielem)) {
            return true;
        }
    }
    return false;
}

/*
 * transformRuleStmt -
 *	  transform a CREATE RULE Statement. The action is a list of parse
 *	  trees which is transformed into a list of query trees, and we also
 *	  transform the WHERE clause if any.
 *
 * actions and whereClause are output parameters that receive the
 * transformed results.
 */
void transformRuleStmt(RuleStmt* stmt, const char* queryString, List** actions, Node** whereClause)
{
    Relation rel;
    ParseState* pstate = NULL;
    RangeTblEntry* oldrte = NULL;
    RangeTblEntry* newrte = NULL;

    /*
     * To avoid deadlock, make sure the first thing we do is grab
     * AccessExclusiveLock on the target relation.	This will be needed by
     * DefineQueryRewrite(), and we don't want to grab a lesser lock
     * beforehand.
     */
    rel = heap_openrv(stmt->relation, AccessExclusiveLock);

    if (rel->rd_rel->relkind == RELKIND_MATVIEW)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("rules on materialized views are not supported")));

    if (rel->rd_rel->relkind == RELKIND_CONTQUERY)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("rules on contqueries are not supported")));

    /* Set up pstate */
    pstate = make_parsestate(NULL);
    pstate->p_sourcetext = queryString;

    /*
     * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
     * Set up their RTEs in the main pstate for use in parsing the rule
     * qualification.
     */
    oldrte = addRangeTableEntryForRelation(pstate, rel, makeAlias("old", NIL), false, false);
    newrte = addRangeTableEntryForRelation(pstate, rel, makeAlias("new", NIL), false, false);
    /* Must override addRangeTableEntry's default access-check flags */
    oldrte->requiredPerms = 0;
    newrte->requiredPerms = 0;

    /*
     * They must be in the namespace too for lookup purposes, but only add the
     * one(s) that are relevant for the current kind of rule.  In an UPDATE
     * rule, quals must refer to OLD.field or NEW.field to be unambiguous, but
     * there's no need to be so picky for INSERT & DELETE.  We do not add them
     * to the joinlist.
     */
    switch (stmt->event) {
        case CMD_SELECT:
            addRTEtoQuery(pstate, oldrte, false, true, true);
            break;
        case CMD_UPDATE:
            addRTEtoQuery(pstate, oldrte, false, true, true);
            addRTEtoQuery(pstate, newrte, false, true, true);
            break;
        case CMD_INSERT:
            addRTEtoQuery(pstate, newrte, false, true, true);
            break;
        case CMD_DELETE:
            addRTEtoQuery(pstate, oldrte, false, true, true);
            break;
        case CMD_UTILITY:
            addRTEtoQuery(pstate, newrte, false, true, true);
            break;
        default:
            ereport(ERROR,
                (errcode(ERRCODE_CASE_NOT_FOUND),
                    errmodule(MOD_OPT),
                    errmsg("unrecognized event type: %d", (int)stmt->event)));

            break;
    }

    /* take care of the where clause */
    *whereClause = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE");
    /* we have to fix its collations too */
    assign_expr_collations(pstate, *whereClause);

    if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                errmsg("rule WHERE condition cannot contain references to other relations")));

    /* aggregates not allowed (but subselects are okay) */
    if (pstate->p_hasAggs)
        ereport(
            ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("cannot use aggregate function in rule WHERE condition")));
    if (pstate->p_hasWindowFuncs)
        ereport(
            ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("cannot use window function in rule WHERE condition")));

    /*
     * 'instead nothing' rules with a qualification need a query rangetable so
     * the rewrite handler can add the negated rule qualification to the
     * original query. We create a query with the new command type CMD_NOTHING
     * here that is treated specially by the rewrite system.
     */
    if (stmt->actions == NIL) {
        Query* nothing_qry = makeNode(Query);

        nothing_qry->commandType = CMD_NOTHING;
        nothing_qry->rtable = pstate->p_rtable;
        nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */

        *actions = list_make1(nothing_qry);
    } else {
        ListCell* l = NULL;
        List* newactions = NIL;

        /*
         * transform each statement, like parse_sub_analyze()
         */
        foreach (l, stmt->actions) {
            Node* action = (Node*)lfirst(l);
            ParseState* sub_pstate = make_parsestate(NULL);
            Query *sub_qry = NULL, *top_subqry = NULL;
            bool has_old = false, has_new = false;

#ifdef PGXC
            if (IsA(action, NotifyStmt))
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                        errmsg("Rule may not use NOTIFY, it is not yet supported")));
#endif
            /*
             * Since outer ParseState isn't parent of inner, have to pass down
             * the query text by hand.
             */
            sub_pstate->p_sourcetext = queryString;

            /*
             * Set up OLD/NEW in the rtable for this statement.  The entries
             * are added only to relnamespace, not varnamespace, because we
             * don't want them to be referred to by unqualified field names
             * nor "*" in the rule actions.  We decide later whether to put
             * them in the joinlist.
             */
            oldrte = addRangeTableEntryForRelation(sub_pstate, rel, makeAlias("old", NIL), false, false);
            newrte = addRangeTableEntryForRelation(sub_pstate, rel, makeAlias("new", NIL), false, false);
            oldrte->requiredPerms = 0;
            newrte->requiredPerms = 0;
            addRTEtoQuery(sub_pstate, oldrte, false, true, false);
            addRTEtoQuery(sub_pstate, newrte, false, true, false);

            /* Transform the rule action statement */
            top_subqry = transformStmt(sub_pstate, action);
            /*
             * We cannot support utility-statement actions (eg NOTIFY) with
             * nonempty rule WHERE conditions, because there's no way to make
             * the utility action execute conditionally.
             */
            if (top_subqry->commandType == CMD_UTILITY && *whereClause != NULL)
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                        errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions")));

            /*
             * If the action is INSERT...SELECT, OLD/NEW have been pushed down
             * into the SELECT, and that's what we need to look at. (Ugly
             * kluge ... try to fix this when we redesign querytrees.)
             */
            sub_qry = getInsertSelectQuery(top_subqry, NULL);
            /*
             * If the sub_qry is a setop, we cannot attach any qualifications
             * to it, because the planner won't notice them.  This could
             * perhaps be relaxed someday, but for now, we may as well reject
             * such a rule immediately.
             */
            if (sub_qry->setOperations != NULL && *whereClause != NULL)
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));

            /*
             * Validate action's use of OLD/NEW, qual too
             */
            has_old = rangeTableEntry_used((Node*)sub_qry, PRS2_OLD_VARNO, 0) ||
                      rangeTableEntry_used(*whereClause, PRS2_OLD_VARNO, 0);
            has_new = rangeTableEntry_used((Node*)sub_qry, PRS2_NEW_VARNO, 0) ||
                      rangeTableEntry_used(*whereClause, PRS2_NEW_VARNO, 0);

            switch (stmt->event) {
                case CMD_SELECT:
                case CMD_UTILITY:
                    if (has_old)
                        ereport(ERROR,
                            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), 
                            errmsg("ON SELECT or UTILITY rule cannot use OLD")));
                    if (has_new)
                        ereport(ERROR,
                            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), 
                            errmsg("ON SELECT or UTILITY rule cannot use NEW")));
                    break;
                case CMD_UPDATE:
                    /* both are OK */
                    break;
                case CMD_INSERT:
                    if (has_old)
                        ereport(ERROR,
                            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("ON INSERT rule cannot use OLD")));
                    break;
                case CMD_DELETE:
                    if (has_new)
                        ereport(ERROR,
                            (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("ON DELETE rule cannot use NEW")));
                    break;
                default:
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                            errmodule(MOD_OPT),
                            errmsg("unrecognized event type: %d", (int)stmt->event)));
                    break;
            }

            /*
             * OLD/NEW are not allowed in WITH queries, because they would
             * amount to outer references for the WITH, which we disallow.
             * However, they were already in the outer rangetable when we
             * analyzed the query, so we have to check.
             *
             * Note that in the INSERT...SELECT case, we need to examine the
             * CTE lists of both top_subqry and sub_qry.
             *
             * Note that we aren't digging into the body of the query looking
             * for WITHs in nested sub-SELECTs.  A WITH down there can
             * legitimately refer to OLD/NEW, because it'd be an
             * indirect-correlated outer reference.
             */
            if (rangeTableEntry_used((Node*)top_subqry->cteList, PRS2_OLD_VARNO, 0) ||
                rangeTableEntry_used((Node*)sub_qry->cteList, PRS2_OLD_VARNO, 0))
                ereport(
                    ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot refer to OLD within WITH query")));
            if (rangeTableEntry_used((Node*)top_subqry->cteList, PRS2_NEW_VARNO, 0) ||
                rangeTableEntry_used((Node*)sub_qry->cteList, PRS2_NEW_VARNO, 0))
                ereport(
                    ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot refer to NEW within WITH query")));

            /*
             * For efficiency's sake, add OLD to the rule action's jointree
             * only if it was actually referenced in the statement or qual.
             *
             * For INSERT, NEW is not really a relation (only a reference to
             * the to-be-inserted tuple) and should never be added to the
             * jointree.
             *
             * For UPDATE, we treat NEW as being another kind of reference to
             * OLD, because it represents references to *transformed* tuples
             * of the existing relation.  It would be wrong to enter NEW
             * separately in the jointree, since that would cause a double
             * join of the updated relation.  It's also wrong to fail to make
             * a jointree entry if only NEW and not OLD is mentioned.
             */
            if (has_old || (has_new && stmt->event == CMD_UPDATE)) {
                /*
                 * If sub_qry is a setop, manipulating its jointree will do no
                 * good at all, because the jointree is dummy. (This should be
                 * a can't-happen case because of prior tests.)
                 */
                if (sub_qry->setOperations != NULL)
                    ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                            errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
                /* hack so we can use addRTEtoQuery() */
                sub_pstate->p_rtable = sub_qry->rtable;
                sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
                addRTEtoQuery(sub_pstate, oldrte, true, false, false);
                sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
            }

            newactions = lappend(newactions, top_subqry);

            free_parsestate(sub_pstate);
        }

        *actions = newactions;
    }

    free_parsestate(pstate);

    /* Close relation, but keep the exclusive lock */
    heap_close(rel, NoLock);
}

/*
 * transformAlterTableStmt -
 *		parse analysis for ALTER TABLE
 *
 * Returns a List of utility commands to be done in sequence.  One of these
 * will be the transformed AlterTableStmt, but there may be additional actions
 * to be done before and after the actual AlterTable() call.
 */
List* transformAlterTableStmt(Oid relid, AlterTableStmt* stmt, const char* queryString)
{
    Relation rel;
    ParseState* pstate = NULL;
    CreateStmtContext cxt;
    List* result = NIL;
    List* save_alist = NIL;
    ListCell *lcmd = NULL, *l = NULL;
    List* newcmds = NIL;
    bool skipValidation = true;
    AlterTableCmd* newcmd = NULL;
    Node* rangePartDef = NULL;
    AddPartitionState* addDefState = NULL;
    AddSubPartitionState* addSubdefState = NULL;
    SplitPartitionState* splitDefState = NULL;
    ListCell* cell = NULL;

    /* Caller is responsible for locking the relation */
    rel = relation_open(relid, NoLock);
    if (IS_FOREIGNTABLE(rel) || IS_STREAM_TABLE(rel)) {
        /*
         * In the security mode, the useft privilege of a user must be
         * checked before the user alters a foreign table.
         */
        if (isSecurityMode && !have_useft_privilege()) {
            ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                    errmsg("permission denied to alter foreign table in security mode")));
        }
    }

    /* Set up pstate and CreateStmtContext */
    pstate = make_parsestate(NULL);
    pstate->p_sourcetext = queryString;

    cxt.pstate = pstate;
    if (stmt->relkind == OBJECT_FOREIGN_TABLE || stmt->relkind == OBJECT_STREAM) {
        cxt.stmtType = ALTER_FOREIGN_TABLE;
    } else {
        cxt.stmtType = ALTER_TABLE;
    }
    cxt.relation = stmt->relation;
    cxt.relation->relpersistence = RelationGetRelPersistence(rel);
    cxt.rel = rel;
    cxt.inhRelations = NIL;
    cxt.isalter = true;
    cxt.hasoids = false; /* need not be right */
    cxt.columns = NIL;
    cxt.ckconstraints = NIL;
    cxt.fkconstraints = NIL;
    cxt.ixconstraints = NIL;
    cxt.clusterConstraints = NIL;
    cxt.inh_indexes = NIL;
    cxt.blist = NIL;
    cxt.alist = NIL;
    cxt.pkey = NULL;
    cxt.ispartitioned = RelationIsPartitioned(rel);
    cxt.rel_coll_id = InvalidOid;

#ifdef PGXC
    cxt.fallback_dist_col = NULL;
    cxt.distributeby = NULL;
    cxt.subcluster = NULL;
#endif
    cxt.node = (Node*)stmt;
    cxt.isResizing = false;
    cxt.ofType = false;

    if (RelationIsForeignTable(rel) || RelationIsStream(rel)) {
        cxt.canInfomationalConstraint = CAN_BUILD_INFORMATIONAL_CONSTRAINT_BY_RELID(RelationGetRelid(rel));
    } else {
        cxt.canInfomationalConstraint = false;
    }

    /*
     * The only subtypes that currently require parse transformation handling
     * are ADD COLUMN and ADD CONSTRAINT.  These largely re-use code from
     * CREATE TABLE.
     */
    foreach (lcmd, stmt->cmds) {
        AlterTableCmd* cmd = (AlterTableCmd*)lfirst(lcmd);

        elog(ES_LOGLEVEL, "[transformAlterTableStmt] cmd subtype: %d", cmd->subtype);

        switch (cmd->subtype) {
            case AT_AddColumn:
            case AT_AddColumnToView: {
                ColumnDef* def = (ColumnDef*)cmd->def;

                AssertEreport(IsA(def, ColumnDef), MOD_OPT, "");
                transformColumnDefinition(&cxt, def, false);

                /*
                 * If the column has a non-null default, we can't skip
                 * validation of foreign keys.
                 */
                if (def->raw_default != NULL)
                    skipValidation = false;

                /*
                 * All constraints are processed in other ways. Remove the
                 * original list
                 */
                def->constraints = NIL;

                newcmds = lappend(newcmds, cmd);
                break;
            }
            case AT_AddConstraint:

                /*
                 * The original AddConstraint cmd node doesn't go to newcmds
                 */
                if (IsA(cmd->def, Constraint)) {
                    transformTableConstraint(&cxt, (Constraint*)cmd->def);
                    if (((Constraint*)cmd->def)->contype == CONSTR_FOREIGN) {
                        skipValidation = false;
                    }
                } else
                    ereport(ERROR,
                        (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                            errmodule(MOD_OPT),
                            errmsg("unrecognized node type: %d", (int)nodeTag(cmd->def))));
                break;

            case AT_ProcessedConstraint:

                /*
                 * Already-transformed ADD CONSTRAINT, so just make it look
                 * like the standard case.
                 */
                cmd->subtype = AT_AddConstraint;
                newcmds = lappend(newcmds, cmd);
                break;
            case AT_AddPartition:
                /* transform the boundary of range partition,
                 * this step transform it from A_Const into Const */
                addDefState = (AddPartitionState*)cmd->def;
                if (!PointerIsValid(addDefState)) {
                    ereport(ERROR,
                        (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("missing definition of adding partition")));
                }
                /* A_Const -->Const */
                foreach (cell, addDefState->partitionList) {
                    rangePartDef = (Node*)lfirst(cell);
                    transformPartitionValue(pstate, rangePartDef, true);
                }

                /* transform START/END into LESS/THAN:
                 * Put this part behind the transformPartitionValue().
                 */
                if (addDefState->isStartEnd) {
                    List* pos = NIL;
                    int32 partNum;
                    Const* lowBound = NULL;

                    if (!RELATION_IS_PARTITIONED(rel))
                        ereport(ERROR,
                            (errcode(ERRCODE_INVALID_OPERATION),
                                errmodule(MOD_OPT),
                                errmsg("can not add partition against NON-PARTITIONED table")));

                    if (rel->partMap->type != PART_TYPE_RANGE) {
                        ereport(ERROR,
                            (errcode(ERRCODE_INVALID_OPERATION),
                                errmodule(MOD_OPT),
                                errmsg("Only support add start/end partition on RANGE-PARTITIONED table")));
                    }

                    /* get partition number */
                    partNum = getNumberOfPartitions(rel);
                    if (partNum >= MAX_PARTITION_NUM)
                        ereport(ERROR,
                            (errcode(ERRCODE_INVALID_OPERATION),
                                errmodule(MOD_OPT),
                                errmsg("the current relation have already reached max number of partitions")));

                    /* get partition info */
                    get_rel_partition_info(rel, &pos, &lowBound);

                    /* entry of transform */
                    addDefState->partitionList = transformRangePartStartEndStmt(
                        pstate, addDefState->partitionList, pos, rel->rd_att->attrs, partNum, lowBound, NULL, true);
                }

                newcmds = lappend(newcmds, cmd);
                break;

            case AT_AddSubPartition:
                /* transform the boundary of subpartition,
                 * this step transform it from A_Const into Const */
                addSubdefState = (AddSubPartitionState*)cmd->def;
                if (!PointerIsValid(addSubdefState)) {
                    ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmodule(MOD_OPT),
                        errmsg("Missing definition of adding subpartition"),
                        errdetail("The AddSubPartitionState in ADD SUBPARTITION command is not found"),
                        errcause("Try ADD SUBPARTITION without subpartition defination"),
                        erraction("Please check DDL syntax for \"ADD SUBPARTITION\"")));
                }
                /* A_Const -->Const */
                foreach (cell, addSubdefState->subPartitionList) {
                    rangePartDef = (Node*)lfirst(cell);
                    transformPartitionValue(pstate, rangePartDef, true);
                }

                newcmds = lappend(newcmds, cmd);
                break;

            case AT_DropPartition:
            case AT_DropSubPartition:
            case AT_TruncatePartition:
            case AT_ExchangePartition:
            case AT_TruncateSubPartition:
                /* transform the boundary of range partition,
                 * this step transform it from A_Const into Const */
                rangePartDef = (Node*)cmd->def;
                if (PointerIsValid(rangePartDef)) {
                    transformPartitionValue(pstate, rangePartDef, false);
                }

                newcmds = lappend(newcmds, cmd);
                break;

            case AT_SplitPartition:
                if (!RELATION_IS_PARTITIONED(rel)) {
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                            errmodule(MOD_OPT),
                            errmsg("can not split partition against NON-PARTITIONED table")));
                }
                if (RelationIsSubPartitioned(rel)) {
                    ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             (errmsg("Un-support feature"),
                              errdetail("For subpartition table, split partition is not supported yet."),
                              errcause("The function is not implemented."), erraction("Use other actions instead."))));
                }
                if (rel->partMap->type == PART_TYPE_LIST || rel->partMap->type == PART_TYPE_HASH) {
                    ereport(ERROR,
                        (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("can not split LIST/HASH partition table")));
                }

                /* transform the boundary of range partition: from A_Const into Const */
                splitDefState = (SplitPartitionState*)cmd->def;
                if (!PointerIsValid(splitDefState->split_point)) {
                    foreach (cell, splitDefState->dest_partition_define_list) {
                        rangePartDef = (Node*)lfirst(cell);
                        transformPartitionValue(pstate, rangePartDef, true);
                    }
                }
                if (splitDefState->partition_for_values)
                    splitDefState->partition_for_values =
                        transformRangePartitionValueInternal(pstate, splitDefState->partition_for_values, true, true);

                /* transform the start/end into less/than */
                if (is_start_end_def_list(splitDefState->dest_partition_define_list)) {
                    List* pos = NIL;
                    int32 partNum;
                    Const* lowBound = NULL;
                    Const* upBound = NULL;
                    Oid srcPartOid = InvalidOid;

                    /* get partition number */
                    partNum = getNumberOfPartitions(rel);

                    /* get partition info */
                    get_rel_partition_info(rel, &pos, NULL);

                    /* get source partition bound */
                    srcPartOid = get_split_partition_oid(rel, splitDefState);
                    if (!OidIsValid(srcPartOid)) {
                        ereport(ERROR,
                            (errcode(ERRCODE_UNDEFINED_TABLE),
                                errmsg("split partition \"%s\" does not exist.", splitDefState->src_partition_name)));
                    }
                    get_src_partition_bound(rel, srcPartOid, &lowBound, &upBound);

                    /* entry of transform */
                    splitDefState->dest_partition_define_list = transformRangePartStartEndStmt(pstate,
                        splitDefState->dest_partition_define_list,
                        pos,
                        rel->rd_att->attrs,
                        partNum - 1,
                        lowBound,
                        upBound,
                        true);
                }

                newcmds = lappend(newcmds, cmd);
                break;

            case AT_SplitSubPartition:
                splitDefState = (SplitPartitionState*)cmd->def;
                if (splitDefState->splitType == LISTSUBPARTITIION) {
                    newcmds = lappend(newcmds, cmd);
                } else if (splitDefState->splitType == RANGESUBPARTITIION) {
                    if (!PointerIsValid(splitDefState->split_point)) {
                        foreach (cell, splitDefState->dest_partition_define_list) {
                            rangePartDef = (Node *)lfirst(cell);
                            transformPartitionValue(pstate, rangePartDef, true);
                        }
                    }
                    newcmds = lappend(newcmds, cmd);
                }
                break;
            case AT_AlterColumnType:
            {
                ColumnDef *def = (ColumnDef *)cmd->def;

                /* pre-alter column type is an set, should drop it after */
                bool oldIsSet = DropSetOwnedByTable(&cxt, cmd->name);

                /* alter the column to a new set type, should create it before */
                Value *v = (Value *)linitial(def->typname->names);
                if (strcmp(v->val.str, "set") == 0) {
                    if (oldIsSet) {
                        if (cmd->is_first || cmd->after_name != NULL) {
                            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                errmsg("Un-supported feature"),
                                    errdetail("set column is not supported for modify column first|after colname")));
                        } else {
                            ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
                                errmsg("can not alter column type to another set")));
                        }
                    } else {
                        PrecheckColumnTypeForSet(&cxt, def->typname);
                        CreateSetOwnedByTable(&cxt, def, cmd->name);
                    }
                }
                /* can NOT change column to an existed set data type */
                Type tup = LookupTypeName(pstate, def->typname, NULL);
                if (HeapTupleIsValid(tup)) {
                    Form_pg_type typform = (Form_pg_type)GETSTRUCT(tup);
                    if (typform->typtype == TYPTYPE_SET) {
                        ereport(ERROR,
                            (errcode(ERRCODE_DATATYPE_MISMATCH),
                            errmsg("can not use existed set type %s for column definition", format_type_be(HeapTupleGetOid(tup)))));
                    }
                    ReleaseSysCache(tup);
                }

                newcmds = lappend(newcmds, cmd);
                break;
            }
            case AT_DropColumn:
            {
                /* if the dropped column type is an set, should drop it after */
                DropSetOwnedByTable(&cxt, cmd->name);
                newcmds = lappend(newcmds, cmd);
                break;
            }
            case AT_ModifyColumn: {
                TransformModifyColumndef(&cxt, cmd);
                newcmds = lappend(newcmds, cmd);
                break;
            }

            default:
                newcmds = lappend(newcmds, cmd);
                break;
        }
    }

    /*
     * transformIndexConstraints wants cxt.alist to contain only index
     * statements, so transfer anything we already have into save_alist
     * immediately.
     */
    save_alist = cxt.alist;
    cxt.alist = NIL;

    /* Postprocess index and FK constraints */
    transformIndexConstraints(&cxt);

    transformFKConstraints(&cxt, skipValidation, true);

    /*
     * Check partial cluster key constraints
     */
    checkClusterConstraints(&cxt);

    /*
     * Check reserve column if the table is column store
     */
    if (RelationIsColStore(rel)) {
        checkReserveColumn(&cxt);
    }

    if ((stmt->relkind == OBJECT_FOREIGN_TABLE || stmt->relkind == OBJECT_STREAM) && cxt.alist != NIL) {
        Oid relationId;
        relationId = RelationGetRelid(rel);

#ifdef ENABLE_MOT
        if (isMOTFromTblOid(relationId) || CAN_BUILD_INFORMATIONAL_CONSTRAINT_BY_RELID(relationId)) {
#else
        if (CAN_BUILD_INFORMATIONAL_CONSTRAINT_BY_RELID(relationId)) {
#endif
            setInternalFlagIndexStmt(cxt.alist);
        }
    }

    /*
     * Push any index-creation commands into the ALTER, so that they can be
     * scheduled nicely by tablecmds.c.  Note that tablecmds.c assumes that
     * the IndexStmt attached to an AT_AddIndex or AT_AddIndexConstraint
     * subcommand has already been through transformIndexStmt.
     */
    foreach (l, cxt.alist) {
        IndexStmt* idxstmt = (IndexStmt*)lfirst(l);
        AssertEreport(IsA(idxstmt, IndexStmt), MOD_OPT, "");
        idxstmt = transformIndexStmt(relid, idxstmt, queryString);
        newcmd = makeNode(AlterTableCmd);
        newcmd->subtype = OidIsValid(idxstmt->indexOid) ? AT_AddIndexConstraint : AT_AddIndex;
        newcmd->def = (Node*)idxstmt;
        newcmds = lappend(newcmds, newcmd);
    }
    cxt.alist = NIL;

    /* Append any CHECK or FK constraints to the commands list */
    foreach (l, cxt.ckconstraints) {
        newcmd = makeNode(AlterTableCmd);
        newcmd->subtype = AT_AddConstraint;
        newcmd->def = (Node*)lfirst(l);
        newcmds = lappend(newcmds, newcmd);
    }
    foreach (l, cxt.fkconstraints) {
        newcmd = makeNode(AlterTableCmd);
        newcmd->subtype = AT_AddConstraint;
        newcmd->def = (Node*)lfirst(l);
        newcmds = lappend(newcmds, newcmd);
    }

    foreach (l, cxt.clusterConstraints) {
        newcmd = makeNode(AlterTableCmd);
        newcmd->subtype = AT_AddConstraint;
        newcmd->def = (Node*)lfirst(l);
        newcmds = lappend(newcmds, newcmd);
    }
    /* Close rel */
    relation_close(rel, NoLock);

    /*
     * Output results.
     */
    stmt->cmds = newcmds;

    result = lappend(cxt.blist, stmt);
    result = list_concat(result, cxt.alist);
    result = list_concat(result, save_alist);

    return result;
}

/*
 * Preprocess a list of column constraint clauses
 * to attach constraint attributes to their primary constraint nodes
 * and detect inconsistent/misplaced constraint attributes.
 *
 * NOTE: currently, attributes are only supported for FOREIGN KEY, UNIQUE,
 * EXCLUSION, and PRIMARY KEY constraints, but someday they ought to be
 * supported for other constraint types.
 */
static void transformConstraintAttrs(CreateStmtContext* cxt, List* constraintList)
{
    Constraint* lastprimarycon = NULL;
    bool saw_deferrability = false;
    bool saw_initially = false;
    ListCell* clist = NULL;

#define SUPPORTS_ATTRS(node)                                                                     \
    ((node) != NULL && ((node)->contype == CONSTR_PRIMARY || (node)->contype == CONSTR_UNIQUE || (node)->contype == CONSTR_CHECK || \
                           (node)->contype == CONSTR_EXCLUSION || (node)->contype == CONSTR_FOREIGN))

    foreach (clist, constraintList) {
        Constraint* con = (Constraint*)lfirst(clist);

        if (!IsA(con, Constraint))
            ereport(ERROR,
                (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized node type: %d", (int)nodeTag(con))));
        switch (con->contype) {
            case CONSTR_ATTR_DEFERRABLE:
                if (!SUPPORTS_ATTRS(lastprimarycon))
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("misplaced DEFERRABLE clause"),
                            parser_errposition(cxt->pstate, con->location)));
                if (saw_deferrability)
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"),
                            parser_errposition(cxt->pstate, con->location)));
                saw_deferrability = true;
                lastprimarycon->deferrable = true;
                break;

            case CONSTR_ATTR_NOT_DEFERRABLE:
                if (!SUPPORTS_ATTRS(lastprimarycon))
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("misplaced NOT DEFERRABLE clause"),
                            parser_errposition(cxt->pstate, con->location)));
                if (saw_deferrability)
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"),
                            parser_errposition(cxt->pstate, con->location)));
                saw_deferrability = true;
                lastprimarycon->deferrable = false;
                if (saw_initially && lastprimarycon->initdeferred)
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
                            parser_errposition(cxt->pstate, con->location)));
                break;

            case CONSTR_ATTR_DEFERRED:
                if (!SUPPORTS_ATTRS(lastprimarycon))
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("misplaced INITIALLY DEFERRED clause"),
                            parser_errposition(cxt->pstate, con->location)));
                if (saw_initially)
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"),
                            parser_errposition(cxt->pstate, con->location)));
                saw_initially = true;
                lastprimarycon->initdeferred = true;

                /*
                 * If only INITIALLY DEFERRED appears, assume DEFERRABLE
                 */
                if (!saw_deferrability)
                    lastprimarycon->deferrable = true;
                else if (!lastprimarycon->deferrable)
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
                            parser_errposition(cxt->pstate, con->location)));
                break;

            case CONSTR_ATTR_IMMEDIATE:
                if (!SUPPORTS_ATTRS(lastprimarycon))
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("misplaced INITIALLY IMMEDIATE clause"),
                            parser_errposition(cxt->pstate, con->location)));
                if (saw_initially)
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"),
                            parser_errposition(cxt->pstate, con->location)));
                saw_initially = true;
                lastprimarycon->initdeferred = false;
                break;

            default:
                /* Otherwise it's not an attribute */
                lastprimarycon = con;
                /* reset flags for new primary node */
                saw_deferrability = false;
                saw_initially = false;
                break;
        }
    }
}

/**
 * tableof type is not  supported be a column in a table
 * @param ctype ctype
 */
static void CheckColumnTableOfType(Type ctype)
{
    Form_pg_type typTup = (Form_pg_type)GETSTRUCT(ctype);
    if (typTup->typtype == TYPTYPE_TABLEOF) {
        ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmodule(MOD_PLSQL),
            errmsg("type \"%s\" is not supported as column type", NameStr(typTup->typname)),
            errdetail("\"%s\" is a nest table type", NameStr(typTup->typname)),
            errcause("feature not supported"), erraction("check type name")));
    } else if (typTup->typtype == TYPTYPE_COMPOSITE) {
        TupleDesc tupleDesc = lookup_rowtype_tupdesc_noerror(HeapTupleGetOid(ctype), typTup->typtypmod, true);
        if (tupleDesc == NULL) {
            ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                errmsg("type %u cannot get tupledesc", HeapTupleGetOid(ctype))));
        }
        for (int i = 0; i < tupleDesc->natts; i++) {
            if (tupleDesc->attrs[i].attisdropped || strcmp(NameStr(tupleDesc->attrs[i].attname), "pljson_list_data") == 0) {
                continue;
            }
            HeapTuple typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(tupleDesc->attrs[i].atttypid));
            if (!HeapTupleIsValid(typeTuple)) {
                ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                                errmsg("cache lookup failed for type %u", tupleDesc->attrs[i].atttypid)));
            }
            CheckColumnTableOfType(typeTuple);
            ReleaseSysCache(typeTuple);
        }
        ReleaseTupleDesc(tupleDesc);
    } else if (OidIsValid(typTup->typelem) && typTup->typtype == TYPTYPE_BASE) {
        HeapTuple typTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typTup->typelem));
        if (!HeapTupleIsValid(typTuple)) {
            ereport(ERROR,
                    (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", typTup->typelem)));
        }
        CheckColumnTableOfType(typTuple);
        ReleaseSysCache(typTuple);
    }
}

/*
 * Special handling of type definition for a column
 */
static void transformColumnType(CreateStmtContext* cxt, ColumnDef* column)
{
    /*
     * All we really need to do here is verify that the type is valid,
     * including any collation spec that might be present.
     */
    Type ctype = typenameType(cxt->pstate, column->typname, NULL);
    Form_pg_type typtup = (Form_pg_type)GETSTRUCT(ctype);

    if (typtup->typtype == TYPTYPE_SET) {
        ereport(ERROR,
            (errcode(ERRCODE_DATATYPE_MISMATCH),
                errmsg("can not use existed set type %s for column definition", format_type_be(HeapTupleGetOid(ctype))),
                parser_errposition(cxt->pstate, column->typname->location)));
    }

    if (!DB_IS_CMPT_BD && column->collClause) {
        LookupCollation(cxt->pstate, column->collClause->collname, column->collClause->location);
        /* Complain if COLLATE is applied to an uncollatable type */
        if (!OidIsValid(typtup->typcollation))
            ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                    errmsg("collations are not supported by type %s", format_type_be(HeapTupleGetOid(ctype))),
                    parser_errposition(cxt->pstate, column->collClause->location)));
    }

#ifndef ENABLE_MULTIPLE_NODES
    /* don't allow package or procedure type as column type */
    if (IsPackageDependType(typeTypeId(ctype), InvalidOid)) {
        ereport(ERROR,
            (errcode(ERRCODE_DATATYPE_MISMATCH),
                errmodule(MOD_PLSQL),
                errmsg("type \"%s\" is not supported as column type", TypeNameToString(column->typname)),
                errdetail("\"%s\" is a package or procedure type", TypeNameToString(column->typname)),
                errcause("feature not supported"),
                erraction("check type name")));
    }
#endif
    CheckColumnTableOfType(ctype);
    ReleaseSysCache(ctype);
}

/*
 * transformCreateSchemaStmt -
 *	  analyzes the CREATE SCHEMA statement
 *
 * Split the schema element list into individual commands and place
 * them in the result list in an order such that there are no forward
 * references (e.g. GRANT to a table created later in the list). Note
 * that the logic we use for determining forward references is
 * presently quite incomplete.
 *
 * SQL92 also allows constraints to make forward references, so thumb through
 * the table columns and move forward references to a posterior alter-table
 * command.
 *
 * The result is a list of parse nodes that still need to be analyzed ---
 * but we can't analyze the later commands until we've executed the earlier
 * ones, because of possible inter-object references.
 *
 * Note: this breaks the rules a little bit by modifying schema-name fields
 * within passed-in structs.  However, the transformation would be the same
 * if done over, so it should be all right to scribble on the input to this
 * extent.
 */
List* transformCreateSchemaStmt(CreateSchemaStmt* stmt)
{
    CreateSchemaStmtContext cxt;
    List* result = NIL;
    ListCell* elements = NULL;

    cxt.stmtType = "CREATE SCHEMA";
    cxt.schemaname = stmt->schemaname;
    cxt.authid = stmt->authid;
    cxt.sequences = NIL;
    cxt.tables = NIL;
    cxt.views = NIL;
    cxt.indexes = NIL;
    cxt.triggers = NIL;
    cxt.grants = NIL;

    /*
     * Run through each schema element in the schema element list. Separate
     * statements by type, and do preliminary analysis.
     */
    foreach (elements, stmt->schemaElts) {
        Node* element = (Node*)lfirst(elements);

        switch (nodeTag(element)) {
            case T_CreateSeqStmt: {
                CreateSeqStmt* elp = (CreateSeqStmt*)element;

                setSchemaName(cxt.schemaname, &elp->sequence->schemaname);
                cxt.sequences = lappend(cxt.sequences, element);
            } break;

            case T_CreateStmt: {
                CreateStmt* elp = (CreateStmt*)element;

                setSchemaName(cxt.schemaname, &elp->relation->schemaname);

                cxt.tables = lappend(cxt.tables, element);
            } break;

            case T_ViewStmt: {
                ViewStmt* elp = (ViewStmt*)element;

                setSchemaName(cxt.schemaname, &elp->view->schemaname);

                cxt.views = lappend(cxt.views, element);
            } break;

            case T_IndexStmt: {
                IndexStmt* elp = (IndexStmt*)element;

                setSchemaName(cxt.schemaname, &elp->relation->schemaname);
                cxt.indexes = lappend(cxt.indexes, element);
            } break;

            case T_CreateTrigStmt: {
                CreateTrigStmt* elp = (CreateTrigStmt*)element;

                setSchemaName(cxt.schemaname, &elp->relation->schemaname);
                cxt.triggers = lappend(cxt.triggers, element);
            } break;

            case T_GrantStmt:
            case T_AlterRoleStmt:
                cxt.grants = lappend(cxt.grants, element);
                break;

            default:
                ereport(ERROR,
                    (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                        errmsg("unrecognized node type: %d", (int)nodeTag(element))));
        }
    }

    result = NIL;
    result = list_concat(result, cxt.sequences);
    result = list_concat(result, cxt.tables);
    result = list_concat(result, cxt.views);
    result = list_concat(result, cxt.indexes);
    result = list_concat(result, cxt.triggers);
    result = list_concat(result, cxt.grants);

    return result;
}

/*
 * setSchemaName
 *		Set or check schema name in an element of a CREATE SCHEMA command
 */
static void setSchemaName(char* context_schema, char** stmt_schema_name)
{
    if (*stmt_schema_name == NULL)
        *stmt_schema_name = context_schema;
    else if (strcmp(context_schema, *stmt_schema_name) != 0)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_SCHEMA_DEFINITION),
                errmsg("CREATE specifies a schema (%s) "
                       "different from the one being created (%s)",
                    *stmt_schema_name,
                    context_schema)));
}

NodeTag GetPartitionStateType(char type)
{
    NodeTag partitionType = T_Invalid;
    switch (type) {
        case 'r':
            partitionType = T_RangePartitionDefState;
            break;
        case 'l':
            partitionType = T_ListPartitionDefState;
            break;
        case 'h':
            partitionType = T_HashPartitionDefState;
            break;
        default:
            ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION),
                            errmsg("unsupported subpartition type: %c", type)));
            break;
    }
    return partitionType;
}

char* GetPartitionDefStateName(Node *partitionDefState)
{
    return ((PartitionDefState *)partitionDefState)->partitionName;
}

List* GetSubPartitionDefStateList(Node *partitionDefState)
{
    if (IsA(partitionDefState, RangePartitionStartEndDefState)) {
        return NIL;
    }
    return ((PartitionDefState *)partitionDefState)->subPartitionDefState;
}

static void semtc_check_partitions_clause(CreateStmt *stmt)
{
    int part_num = stmt->partTableState->partitionsNum;
    List* part_list = stmt->partTableState->partitionList;

    if (part_num > MAX_PARTITION_NUM) {
        ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
            errmsg("Invalid number of partitions"),
            errdetail("partitions number '%d' cannot be greater than %d",
                part_num, MAX_PARTITION_NUM)));
    }
    /* have PARTITIONS num clause  */
    if (part_num > 0) {
        if (part_list != NIL) {
            if (list_length(part_list) != part_num) {
                ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                    errmsg("Invalid number of partitions"),
                    errdetail("the number of defined partitions does not match the partitions number '%d'",
                        part_num)));
            }
            return;
        }
        /* only hash partition can omit partition definition */
        if (stmt->partTableState->partitionStrategy != 'h') {
            ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("Invalid number of partitions"),
                errdetail("Partitions number is specified but partition definition is missing")));
        }
        stmt->partTableState->partitionList = semtc_generate_hash_partition_defs(stmt, NULL, part_num);
        return;
    }
    /* have no PARTITIONS num clause  */
    if (part_list == NIL) {
        /* only hash partition can omit partition definition */
        if (stmt->partTableState->partitionStrategy != 'h') {
            ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("Missing partition definition when partitions number is not specified")));
        }
        stmt->partTableState->partitionList = semtc_generate_hash_partition_defs(stmt, NULL, 1);
    }
}

static List *semtc_check_subpartitions_clause(CreateStmt *stmt, Node *partition_def_state,
    List *sub_partition_list)
{
    char* partName = GetPartitionDefStateName(partition_def_state);
    int subpart_num = stmt->partTableState->subPartitionState->partitionsNum;
    if (subpart_num == 0) {
        return sub_partition_list;
    }
    /* have SUBPARTITIONS num clause  */
    if (sub_partition_list != NIL) {
        if (sub_partition_list->length != subpart_num) {
            ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("Invalid number of partitions"),
                errdetail("The number of defined subpartitions in partition \"%s\" "
                    "does not match the subpartitions number: %d", partName, subpart_num)));
        }
        return sub_partition_list;
    }
    /* only hash partition can omit partition definition */
    if (stmt->partTableState->subPartitionState->partitionStrategy != 'h') {
        ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
            errmsg("Invalid number of partitions"),
            errdetail("Partitions number is specified but partition definition is missing")));
    }
    /* generate subpartitionDefStates */
    switch (nodeTag(partition_def_state)) {
        case T_RangePartitionDefState:
            ((RangePartitionDefState *)partition_def_state)->subPartitionDefState =
                semtc_generate_hash_partition_defs(stmt, partName, subpart_num);
            break;
        case T_ListPartitionDefState:
            ((ListPartitionDefState *)partition_def_state)->subPartitionDefState =
                semtc_generate_hash_partition_defs(stmt, partName, subpart_num);
            break;
        case T_HashPartitionDefState:
            ((HashPartitionDefState *)partition_def_state)->subPartitionDefState =
                semtc_generate_hash_partition_defs(stmt, partName, subpart_num);
            break;
        default:
            break;
    }
    return GetSubPartitionDefStateList(partition_def_state);
}

/*
 * @@GaussDB@@
 * Target		: data partition
 * Brief		: check synax for range partition defination
 * Description	:
 * Notes		:
 */
void checkPartitionSynax(CreateStmt* stmt)
{
    ListCell* cell = NULL;
    bool value_partition = false;

    /* unsupport inherits clause */
    if (stmt->inhRelations) {
        if (stmt->partTableState) {
            ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("unsupport inherits clause for partitioned table")));
        } else {
            foreach (cell, stmt->inhRelations) {
                RangeVar* inh = (RangeVar*)lfirst(cell);
                Relation rel;

                AssertEreport(IsA(inh, RangeVar), MOD_OPT, "");
                rel = heap_openrv(inh, AccessShareLock);
                /* @hdfs
                 * Deal with error mgs for foreign table, the foreign table
                 * is not inherited
                 */
                if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE || rel->rd_rel->relkind == RELKIND_STREAM) {
                    ereport(ERROR,
                        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                            errmsg("inherited relation \"%s\" is a foreign table", inh->relname),
                            errdetail("can not inherit from a foreign table")));
                } else if (rel->rd_rel->relkind != RELKIND_RELATION) {
                    ereport(ERROR,
                        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                            errmsg("inherited relation \"%s\" is not a table", inh->relname)));
                }
                if (RELATION_IS_PARTITIONED(rel)) {
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                            errmodule(MOD_OPT),
                            errmsg("inherited relation \"%s\" is a partitioned table", inh->relname),
                            errdetail("can not inherit from partitioned table")));
                }
                heap_close(rel, NoLock);
            }
        }
    }
    /* is it a partitoned table? */
    if (!stmt->partTableState) {
        return;
    }

    /* check syntax for value-partitioned table */
    if (stmt->partTableState->partitionStrategy == PART_STRATEGY_VALUE) {
        value_partition = true;

        /* do partition-key null check as part of sytax check */
        if (list_length(stmt->partTableState->partitionKey) == 0) {
            ereport(ERROR,
                (errmodule(MOD_OPT),
                    errcode(ERRCODE_SYNTAX_ERROR),
                    errmsg("Value-based partition table should have one column at least")));
        }

        /*
         * for value partitioned table we only do a simple sanity check to
         * ensure that any uncessary fileds are set with NULL
         */
        if (stmt->partTableState->intervalPartDef || stmt->partTableState->partitionList) {
            ereport(ERROR,
                (errcode(ERRCODE_INVALID_OPERATION),
                    errmsg("Value-Based partition table creation encounters unexpected data in unnecessary fields"),
                    errdetail("save context and get assistance from DB Dev team")));
        }
    }

    /* unsupport om commit clause */
    if (stmt->oncommit) {
        ereport(
            ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("ON COMMIT option is not supported for partitioned table")));
    }

    /* unsupport typed table */
    if (stmt->ofTypename) {
        ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Typed table can't not be partitioned")));
    }

    /* unsupport typed table */
    if (stmt->relation->relpersistence != RELPERSISTENCE_PERMANENT) {
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("unsupported feature with temporary/unlogged table for partitioned table")));
    }

    /* unsupport oids option */
    foreach (cell, stmt->options) {
        DefElem* def = (DefElem*)lfirst(cell);

        if (!def->defnamespace && !pg_strcasecmp(def->defname, "oids")) {
            ereport(
                ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("OIDS option is not supported for partitioned table")));
        }
    }

    /* check partition key number for none value-partition table */
    if (!value_partition && stmt->partTableState->partitionKey->length > MAX_PARTKEY_NUMS) {
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                errmsg("too many partition keys for partitioned table"),
                errhint("Partittion key columns can not be more than %d", MAX_PARTKEY_NUMS)));
    }

    /* check PARTITIONS clause */
    semtc_check_partitions_clause(stmt);

    /* check range partition number for none value-partition table */
    if (!value_partition && stmt->partTableState->partitionList->length > MAX_PARTITION_NUM) {
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                errmsg("too many partitions for partitioned table"),
                errhint("Number of partitions can not be more than %d", MAX_PARTITION_NUM)));
    }

    /* check interval sytnax */
    if (stmt->partTableState->intervalPartDef) {
#ifdef ENABLE_MULTIPLE_NODES
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                errmsg("Interval partitioned table is only supported in single-node mode.")));
#else
        if (1 < stmt->partTableState->partitionKey->length) {
            ereport(ERROR,
                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                    errmsg("Range partitioned table with INTERVAL clause has more than one column"),
                    errhint("Only support one partition key for interval partition")));
        }
        if (!IsA(stmt->partTableState->intervalPartDef->partInterval, A_Const) ||
            ((A_Const*)stmt->partTableState->intervalPartDef->partInterval)->val.type != T_String) {
            ereport(ERROR,
                (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                    errmsg("invalid input syntax for type interval")));
        }
        int32 typmod = -1;
        Interval* interval = NULL;
        A_Const* node = (A_Const*)stmt->partTableState->intervalPartDef->partInterval;
        interval = char_to_interval(node->val.val.str, typmod);
        pfree(interval);
#endif
    }

    /* check subpartition sytnax */
    if (stmt->partTableState->subPartitionState != NULL) {
        NodeTag subPartitionType = GetPartitionStateType(stmt->partTableState->subPartitionState->partitionStrategy);
        List* partitionList = stmt->partTableState->partitionList;
        ListCell* lc1 = NULL;
        ListCell* lc2 = NULL;
        if (stmt->partTableState->subPartitionState->partitionsNum > MAX_PARTITION_NUM) {
            ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("Invalid number of partitions"),
                errdetail("subpartitions number '%d' cannot be greater than %d",
                    stmt->partTableState->subPartitionState->partitionsNum, MAX_PARTITION_NUM)));
        }
        foreach (lc1, partitionList) {
            Node* partitionDefState = (Node*)lfirst(lc1);
            List* subPartitionList = GetSubPartitionDefStateList(partitionDefState);
            subPartitionList = semtc_check_subpartitions_clause(stmt, partitionDefState, subPartitionList);
            foreach (lc2, subPartitionList) {
                Node *subPartitionDefState = (Node *)lfirst(lc2);
                if ((nodeTag(subPartitionDefState) != subPartitionType)) {
                    ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION),
                                    errmsg("The syntax format of subpartition is incorrect, the declaration and "
                                           "definition of the subpartition do not match."),
                                    errdetail("The syntax format of subpartition %s is incorrect.",
                                              GetPartitionDefStateName(subPartitionDefState)),
                                    errcause("The declaration and definition of the subpartition do not match."),
                                    erraction("Consistent declaration and definition of subpartition.")));
                }
            }
        }
    } else {
        /* If there is a definition of subpartition, there must be a declaration of subpartition.
         * However, if there is a declaration of subpartition, there may not be a definition of subpartition.
         * This is because the system creates a default subpartition.
         */
        List* partitionList = stmt->partTableState->partitionList;
        ListCell* lc1 = NULL;
        foreach (lc1, partitionList) {
            Node* partitionDefState = (Node*)lfirst(lc1);
            List *subPartitionList = GetSubPartitionDefStateList(partitionDefState);
            if (subPartitionList != NIL) {
                ereport(
                    ERROR,
                    (errcode(ERRCODE_INVALID_OPERATION),
                     errmsg("The syntax format of subpartition is incorrect, missing declaration of subpartition."),
                     errdetail("N/A"), errcause("Missing declaration of subpartition."),
                     erraction("Supplements declaration of subpartition.")));
            }
        }
    }
}

/*
 * @@GaussDB@@
 * Target		: data partition
 * Brief		: check partition value
 * Description	:
 * Notes		: partition key value must be const or const-evaluable expression
 */
static void checkPartitionValue(CreateStmtContext* cxt, CreateStmt* stmt)
{
    PartitionState* partdef = NULL;
    ListCell* cell = NULL;

    partdef = stmt->partTableState;
    if (partdef == NULL) {
        return;
    }
    /* transform expression in partition definition and evaluate the expression */
    foreach (cell, partdef->partitionList) {
        Node* state = (Node*)lfirst(cell);
        transformPartitionValue(cxt->pstate, state, true);
    }
}

/*
 * check_partition_name_internal
 *  check partition name with less/than stmt.
 *
 * [IN] partitionList: partition list
 *
 * RETURN: void
 */
static void check_partition_name_internal(List* partitionList, bool isPartition)
{
    ListCell* cell = NULL;
    ListCell* lc = NULL;
    char* cur_partname = NULL;
    char* ref_partname = NULL;

    foreach (cell, partitionList) {
        lc = cell;
        ref_partname = ((PartitionDefState*)lfirst(cell))->partitionName;

        while (NULL != (lc = lnext(lc))) {
            cur_partname = ((PartitionDefState*)lfirst(lc))->partitionName;

            if (!strcmp(ref_partname, cur_partname)) {
                ereport(ERROR,
                    (errcode(ERRCODE_DUPLICATE_OBJECT),
                         errmsg("duplicate %s name: \"%s\"",
                             (isPartition ? "partition" : "slice"), ref_partname)));
            }
        }
    }
}

/*
 * check_partition_name_start_end
 *  check partition name with start/end stmt.
 *
 * [IN] partitionList: partition list
 *
 * RETURN: void
 */
static void check_partition_name_start_end(List* partitionList, bool isPartition)
{
    ListCell* cell = NULL;
    ListCell* lc = NULL;
    RangePartitionStartEndDefState* defState = NULL;
    RangePartitionStartEndDefState* lastState = NULL;

    foreach (cell, partitionList) {
        lc = cell;
        lastState = (RangePartitionStartEndDefState*)lfirst(cell);

        while ((lc = lnext(lc)) != NULL) {
            defState = (RangePartitionStartEndDefState*)lfirst(lc);
            if (!strcmp(lastState->partitionName, defState->partitionName)) {
                ereport(ERROR,
                    (errcode(ERRCODE_DUPLICATE_OBJECT),
                        errmsg("duplicate %s name: \"%s\"",
                            (isPartition ? "partition" : "slice"), defState->partitionName)));
            }
        }
    }
}

/*
 * @@GaussDB@@
 * Target		: data partition
 * Brief		: check partition name
 * Description	: duplicate partition name is not allowed
 * Notes		:
 */
void checkPartitionName(List* partitionList, bool isPartition)
{
    ListCell* cell = NULL;

    cell = list_head(partitionList);
    if (cell != NULL) {
        Node* state = (Node*)lfirst(cell);

        if (IsA(state, RangePartitionStartEndDefState)) {
            check_partition_name_start_end(partitionList, isPartition);
        } else {
            check_partition_name_internal(partitionList, isPartition);
        }
    }
}

List* GetPartitionNameList(List *partitionList)
{
    ListCell *cell = NULL;
    ListCell *lc = NULL;
    List *subPartitionDefStateList = NIL;
    List *partitionNameList = NIL;

    foreach (cell, partitionList) {
        PartitionDefState *partitionDefState = (PartitionDefState *)lfirst(cell);
        subPartitionDefStateList = partitionDefState->subPartitionDefState;
        partitionNameList = lappend(partitionNameList, partitionDefState->partitionName);
        foreach (lc, subPartitionDefStateList) {
            PartitionDefState *subpartitionDefState = (PartitionDefState *)lfirst(lc);
            partitionNameList = lappend(partitionNameList, subpartitionDefState->partitionName);
        }
    }

    return partitionNameList;
}

void checkSubPartitionName(List* partitionList)
{
    ListCell* cell = NULL;
    ListCell* lc = NULL;
    List* partitionNameList = GetPartitionNameList(partitionList);

    foreach (cell, partitionNameList) {
        char *subPartitionName1 = (char *)lfirst(cell);
        lc = cell;
        while ((lc = lnext(lc)) != NULL) {
            char *subPartitionName2 = (char *)lfirst(lc);
            if (!strcmp(subPartitionName1, subPartitionName2)) {
                list_free_ext(partitionNameList);
                ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT),
                                errmsg("duplicate subpartition name: \"%s\"", subPartitionName2)));
            }
        }
    }
    list_free_ext(partitionNameList);
}

static void CheckDistributionSyntax(CreateStmt* stmt)
{
    DistributeBy *distBy = stmt->distributeby;
    if (distBy->disttype == DISTTYPE_ROUNDROBIN) {
        if (IsA(stmt, CreateForeignTableStmt)) {
            if (IsSpecifiedFDW(((CreateForeignTableStmt*)stmt)->servername, DIST_FDW)) {
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("For foreign table ROUNDROBIN distribution type is built-in support.")));
            }
        } else {
            FEATURE_NOT_PUBLIC_ERROR("Unsupport ROUNDROBIN distribute type");
        }
    } else if (distBy->disttype == DISTTYPE_MODULO) {
        FEATURE_NOT_PUBLIC_ERROR("Unsupport MODULO distribute type");
    }
}

static void CheckSliceValue(CreateStmtContext *cxt, CreateStmt *stmt)
{
    DistributeBy *distBy = stmt->distributeby;
    DistState *distState = distBy->distState;
    ListCell *cell = NULL;

    foreach (cell, distState->sliceList) {
        Node *slice = (Node *)lfirst(cell);
        switch (nodeTag(slice)) {
            case T_RangePartitionDefState: {
                RangePartitionDefState *def = (RangePartitionDefState *)slice;
                def->boundary = transformRangePartitionValueInternal(cxt->pstate, def->boundary, true, true, false);
                break;
            }
            
            case T_RangePartitionStartEndDefState: {
                RangePartitionStartEndDefState *def = (RangePartitionStartEndDefState *)slice;
                def->startValue = transformRangePartitionValueInternal(cxt->pstate, def->startValue, true, true, false);
                def->endValue = transformRangePartitionValueInternal(cxt->pstate, def->endValue, true, true, false);
                def->everyValue = transformRangePartitionValueInternal(cxt->pstate, def->everyValue, true, true, false);
                break;
            }
            case T_ListSliceDefState: {
                ListCell* cell2 = NULL;
                List* newlist = NIL; /* a new list is necessary since boundaries is nested */
                ListSliceDefState *listSlice = (ListSliceDefState *)slice;

                foreach (cell2, listSlice->boundaries) {
                    Node *boundary = (Node *)lfirst(cell2);  /* could be multi-column(multi-values) */
                    TransformListDistributionValue(cxt->pstate, &boundary, true);
                    newlist = lappend(newlist, boundary);
                }

                listSlice->boundaries = newlist;
                break;
            }
            default:
                ereport(ERROR,
                    (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                        errmsg("unrecognized node type: %d", (int)nodeTag(slice))));
        }
    }
}

static void TransformListDistributionValue(ParseState* pstate, Node** listDistDef, bool needCheck) 
{
    *listDistDef = (Node *)transformRangePartitionValueInternal(
        pstate, (List *)*listDistDef, needCheck, true, false);
    return;
}

static List* GetDistributionkeyPos(List* partitionkeys, List* schema)
{
    if (schema == NULL) {
        return NULL;
    }

    ListCell* cell = NULL;
    ListCell* schemaCell = NULL;
    ColumnDef* schemaDef = NULL;
    int columnCount = 0;
    bool* isExist = NULL;
    char* distributionkeyName = NULL;
    List* pos = NULL;
    int len = -1;

    len = schema->length;
    isExist = (bool*)palloc(len * sizeof(bool));
    errno_t rc = EOK;
    rc = memset_s(isExist, len * sizeof(bool), 0, len * sizeof(bool));
    securec_check(rc, "\0", "\0");

    foreach (cell, partitionkeys) {
        distributionkeyName = strVal(lfirst(cell));

        foreach (schemaCell, schema) {
            schemaDef = (ColumnDef*)lfirst(schemaCell);
            if (!strcmp(distributionkeyName, schemaDef->colname)) {
                if (isExist[columnCount]) {
                    pfree_ext(isExist);
                    ereport(ERROR,
                        (errcode(ERRCODE_DUPLICATE_COLUMN),
                         errmsg("Include identical distribution column: \"%s\"", distributionkeyName)));
                }

                isExist[columnCount] = true;
                break;
            }

            columnCount++;
        }

        if (columnCount >= len) {
            pfree_ext(isExist);
            ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_COLUMN),
                    errmsg("Invalid distribution column specified")));
        }

        pos = lappend_int(pos, columnCount);
        columnCount = 0;
        distributionkeyName = NULL;
    }

    pfree_ext(isExist);
    return pos;
}

/* get storage type from CreateStmt.optitions, which is generated by WITH clause */
static char* CreatestmtGetOrientation(CreateStmt *stmt)
{
    ListCell *lc = NULL;
    foreach (lc, stmt->options) {
        DefElem* def = (DefElem*)lfirst(lc);
        if (pg_strcasecmp(def->defname, "orientation") == 0) {
#ifdef ENABLE_FINANCE_MODE
            if (defGetString(def) == ORIENTATION_COLUMN)
                ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("ORIENTATION==COLUMN is not supported on finance mode")));
#endif
            return defGetString(def);
        }
    }
    /* default orientation by row store */
    return ORIENTATION_ROW;
}

static void ConvertSliceStartEnd2LessThan(CreateStmtContext *cxt, CreateStmt *stmt)
{
    DistributeBy *distBy = stmt->distributeby;
    DistState *distState = distBy->distState;
    List *pos = NIL;
    TupleDesc desc;

    pos = GetDistributionkeyPos(distBy->colname, cxt->columns);
    char *storeChar = CreatestmtGetOrientation(stmt);
    if ((pg_strcasecmp(storeChar, ORIENTATION_ROW) != 0) && (pg_strcasecmp(storeChar, ORIENTATION_COLUMN) != 0)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-supported feature"),
                    errdetail("Orientation type %s is not supported for range distributed table.", storeChar)));
    }
    desc = BuildDescForRelation(cxt->columns, (Node *)makeString(storeChar));

    distState->sliceList = transformRangePartStartEndStmt(
        cxt->pstate, distState->sliceList, pos, desc->attrs, 0, NULL, NULL, true, false);

    list_free_ext(pos);
    pfree(desc);
}

static void CheckListSliceName(List* sliceList)
{
    ListCell* cell = NULL;
    ListCell* next = NULL;
    char* refName = NULL;
    char* curName = NULL;

    foreach (cell, sliceList) {
        next = cell;
        refName = ((ListSliceDefState *)lfirst(cell))->name;
        while ((next = lnext(next)) != NULL) {
            curName = ((ListSliceDefState *)lfirst(next))->name;
            if (strcmp(refName, curName) == 0) {
                ereport(ERROR,
                    (errcode(ERRCODE_DUPLICATE_OBJECT),
                         errmsg("duplicate slice name: \"%s\"", refName)));
            }
        }
    }

    return;
}

static void CheckSliceName(DistributeBy *distBy)
{
    if (distBy->disttype == DISTTYPE_RANGE) {
        checkPartitionName(distBy->distState->sliceList, false);
    } else {
        /* DISTTYPE_LIST */
        CheckListSliceName(distBy->distState->sliceList);
    }
}

static void CheckListRangeDistribClause(CreateStmtContext *cxt, CreateStmt *stmt)
{
    DistributeBy *distBy = stmt->distributeby;
    DistState *distState = distBy->distState;
    if (distState == NULL) {
        return;
    }

    CheckSliceValue(cxt, stmt);
    if (distBy->disttype == DISTTYPE_RANGE && is_start_end_def_list(distState->sliceList)) {
        ConvertSliceStartEnd2LessThan(cxt, stmt);
    }
    CheckSliceName(distBy);
}

/*
 * Check partial cluster key constraints
 */
static void checkClusterConstraints(CreateStmtContext* cxt)
{
    AssertEreport(cxt != NULL, MOD_OPT, "");

    if (cxt->clusterConstraints == NIL) {
        return;
    }

    ListCell* lc = NULL;
    ListCell* lc1 = NULL;
    ListCell* lc2 = NULL;

    foreach (lc, cxt->clusterConstraints) {
        Constraint* constraint = (Constraint*)lfirst(lc);

        // for each keys find out whether have same key
        foreach (lc1, constraint->keys) {
            char* key1 = strVal(lfirst(lc1));
            lc2 = lnext(lc1);

            for (; lc2 != NULL; lc2 = lnext(lc2)) {
                char* key2 = strVal(lfirst(lc2));
                if (0 == strcasecmp(key1, key2)) {
                    ereport(ERROR,
                        (errcode(ERRCODE_DUPLICATE_COLUMN),
                            errmsg("column \"%s\" appears twice in partial cluster key constraint", key1),
                            parser_errposition(cxt->pstate, constraint->location)));
                }
            }
        }
    }
}

/*
 * Check reserve column
 */
static void checkReserveColumn(CreateStmtContext* cxt)
{
    AssertEreport(cxt != NULL, MOD_OPT, "");

    if (cxt->columns == NIL) {
        return;
    }

    List* columns = cxt->columns;
    ListCell* lc = NULL;

    foreach (lc, columns) {
        ColumnDef* col = (ColumnDef*)lfirst(lc);
        AssertEreport(col != NULL, MOD_OPT, "");

        if (CHCHK_PSORT_RESERVE_COLUMN(col->colname)) {
            ereport(ERROR,
                (errcode(ERRCODE_DUPLICATE_COLUMN),
                    errmsg("column name \"%s\" conflicts with a system column name", col->colname)));
        }
    }
}

static void checkPsortIndexCompatible(IndexStmt* stmt)
{
    if (stmt->whereClause) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("access method \"psort\" does not support WHERE clause")));
    }

    /* psort index can not support index expressions */
    ListCell* lc = NULL;
    foreach (lc, stmt->indexParams) {
        IndexElem* ielem = (IndexElem*)lfirst(lc);

        if (ielem->expr) {
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("access method \"psort\" does not support index expressions")));
        }
    }
}

static void checkCBtreeIndexCompatible(IndexStmt* stmt)
{
    if (stmt->whereClause) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("access method \"cbtree\" does not support WHERE clause")));
    }

    /* psort index can not support index expressions */
    ListCell* lc = NULL;
    foreach (lc, stmt->indexParams) {
        IndexElem* ielem = (IndexElem*)lfirst(lc);

        if (ielem->expr) {
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("access method \"cbtree\" does not support index expressions")));
        }
    }
}

static void checkCGinBtreeIndexCompatible(IndexStmt* stmt)
{
    Assert(stmt);

    if (stmt->whereClause) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("access method \"cgin\" does not support WHERE clause")));
    }

    /* cgin index can not support null text search parser */
    ListCell* l = NULL;
    foreach (l, stmt->indexParams) {
        IndexElem* ielem = (IndexElem*)lfirst(l);
        Node* expr = ielem->expr;

        if (expr != NULL) {
            Assert(IsA(expr, FuncExpr));
            if (IsA(expr, FuncExpr)) {
                FuncExpr* funcexpr = (FuncExpr*)expr;
                Node* firstarg = (Node*)lfirst(funcexpr->args->head);

                if (IsA(firstarg, Const)) {
                    Const* constarg = (Const*)firstarg;
                    if (constarg->constisnull)
                        ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                errmsg("access method \"cgin\" does not support null text search parser")));
                }
            }
        }
    }
}
    
static Node* transformListPartitionRowExpr(ParseState* pstate, RowExpr* rowexpr)
{
    Node* con = NULL;
    RowExpr* result = makeNode(RowExpr);

    result->row_typeid = rowexpr->row_typeid;
    result->row_format = rowexpr->row_format;
    result->location = rowexpr->location;
    result->colnames = NIL;

    foreach_cell (cell, rowexpr->args) {
        con = transformIntoConst(pstate, EXPR_KIND_PARTITION_BOUND, (Node*)lfirst(cell));
        result->args = lappend(result->args, con);
    }
    return (Node*)result;
}

List* transformListPartitionValue(ParseState* pstate, List* boundary, bool needCheck, bool needFree)
{
    List* newValueList = NIL;
    ListCell* valueCell = NULL;
    Node* elem = NULL;
    Node* result = NULL;

    /* scan value of partition key of per partition */
    foreach (valueCell, boundary) {
        elem = (Node*)lfirst(valueCell);
        if (IsA(elem, RowExpr)) { /* for multi-keys list partition boundary */
            result = transformListPartitionRowExpr(pstate, (RowExpr*)elem);
            newValueList = lappend(newValueList, result);
            continue;
        }
        result = transformIntoConst(pstate, EXPR_KIND_PARTITION_BOUND, elem);
        if (PointerIsValid(result) && needCheck && ((Const*)result)->constisnull && !((Const*)result)->ismaxvalue) {
            ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                    errmsg("Partition key value can not be null"),
                    errdetail("partition bound element must be one of: string, datetime or interval literal, number, "
                              "or MAXVALUE(for range partition)/DEFAULT(for list partition), and not null")));
        }
        newValueList = lappend(newValueList, result);
    }

    if (needFree && boundary != NIL)
        list_free_ext(boundary); /* avoid mem leak */

    return newValueList;
}

void transformSubPartitionValue(ParseState* pstate, List* subPartitionDefStateList)
{
    if (subPartitionDefStateList == NIL) {
        return;
    }
    ListCell *cell = NULL;
    foreach (cell, subPartitionDefStateList) {
        Node *subPartitionDefState = (Node *)lfirst(cell);
        transformPartitionValue(pstate, subPartitionDefState, true);
    }
}

void transformPartitionValue(ParseState* pstate, Node* rangePartDef, bool needCheck)
{
    Assert(rangePartDef); /* never null */

    switch (rangePartDef->type) {
        case T_RangePartitionDefState: {
            RangePartitionDefState* state = (RangePartitionDefState*)rangePartDef;
            /* only one boundary need transform */
            state->boundary = transformRangePartitionValueInternal(pstate, state->boundary, needCheck, true);
            transformSubPartitionValue(pstate, state->subPartitionDefState);
            break;
        }
        case T_RangePartitionStartEndDefState: {
            RangePartitionStartEndDefState* state = (RangePartitionStartEndDefState*)rangePartDef;

            /* transform each point, null-case is also covered */
            state->startValue = transformRangePartitionValueInternal(pstate, state->startValue, needCheck, true);
            state->endValue = transformRangePartitionValueInternal(pstate, state->endValue, needCheck, true);
            state->everyValue = transformRangePartitionValueInternal(pstate, state->everyValue, needCheck, true);
            break;
        }
        case T_ListPartitionDefState: {
            ListPartitionDefState* state = (ListPartitionDefState*)rangePartDef;
            state->boundary = transformListPartitionValue(pstate, state->boundary, needCheck, true);
            transformSubPartitionValue(pstate, state->subPartitionDefState);
            break;
        }
        case T_HashPartitionDefState: {
            HashPartitionDefState* state = (HashPartitionDefState*)rangePartDef;
            /* only one boundary need transform */
            state->boundary = transformListPartitionValue(pstate, state->boundary, needCheck, true);
            transformSubPartitionValue(pstate, state->subPartitionDefState);
            break;
        }

        default:
            Assert(false); /* never happen */
    }
}

List* transformRangePartitionValueInternal(ParseState* pstate, List* boundary, bool needCheck, bool needFree, 
    bool isPartition)
{
    List* newMaxValueList = NIL;
    ListCell* valueCell = NULL;
    Node* maxElem = NULL;
    Node* result = NULL;

    /* scan max value of partition key of per partition */
    foreach (valueCell, boundary) {
        maxElem = (Node*)lfirst(valueCell);
        result = transformIntoConst(pstate, EXPR_KIND_PARTITION_BOUND, maxElem, isPartition);
        if (PointerIsValid(result) && needCheck && ((Const*)result)->constisnull && !((Const*)result)->ismaxvalue) {
            ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                    errmsg("%s key value can not be null", (isPartition ? "Partition" : "Distribution")),
                        errdetail("%s bound element must be one of: string, datetime or interval literal, number, "
                            "or MAXVALUE, and not null", (isPartition ? "partition" : "distribution"))));
        }
        newMaxValueList = lappend(newMaxValueList, result);
    }

    if (needFree && boundary != NIL)
        list_free_ext(boundary); /* avoid mem leak */

    return newMaxValueList;
}

/*
 * @@GaussDB@@
 * Target		: data partition
 * Brief		:
 * Description	:
 * Input		:
 * Output	:
 * Return		:
 * Notes		:
 */
Node* transformIntoConst(ParseState* pstate, ParseExprKind exprKind, Node* maxElem, bool isPartition)
{
    Node* result = NULL;
    FuncExpr* funcexpr = NULL;
    /* transform expression first */
    maxElem = transformExpr(pstate, maxElem, exprKind);

    /* then, evaluate expression */
    switch (nodeTag(maxElem)) {
        case T_Const:
            result = maxElem;
            break;
        /* MaxValue for Date must be a function expression(to_date) */
        case T_FuncExpr: {
            funcexpr = (FuncExpr*)maxElem;
            result = (Node*)evaluate_expr(
                (Expr*)funcexpr, exprType((Node*)funcexpr), exprTypmod((Node*)funcexpr), funcexpr->funccollid);

            /*
             * if the function expression cannot be evaluated and output a const,
             * than report error
             */
            if (T_Const != nodeTag((Node*)result)) {
                ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                        errmsg("%s key value must be const or const-evaluable expression",
                            (isPartition ? "partition" : "distribution"))));
            }
        } break;
        default: {
            ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                    errmsg("%s key value must be const or const-evaluable expression",
                        (isPartition ? "partition" : "distribution"))));
        } break;
    }
    return result;
}
/*
 * @@GaussDB@@
 * Target		: data partition
 * Brief		:
 * Description	:
 * Notes		:
 */
Oid generateClonedIndex(Relation source_idx, Relation source_relation, char* tempIndexName, Oid targetTblspcOid,
    bool skip_build, bool partitionedIndex)
{
    CreateStmtContext cxt;
    IndexStmt* index_stmt = NULL;
    AttrNumber* attmap = NULL;
    int attmap_length, i;
    Oid heap_relid;
    Relation heap_rel;
    TupleDesc tupleDesc;
    Oid source_relid = RelationGetRelid(source_idx);
    ObjectAddress ret;

    /* get the relation that the index is created on */
    heap_relid = IndexGetRelation(source_relid, false);
    heap_rel = relation_open(heap_relid, AccessShareLock);

    /* create cxt.relation */
    cxt.relation = makeRangeVar(
        get_namespace_name(RelationGetNamespace(source_relation)), RelationGetRelationName(source_relation), -1);

    /* initialize attribute array */
    tupleDesc = RelationGetDescr(heap_rel);
    attmap_length = tupleDesc->natts;
    attmap = (AttrNumber*)palloc0(sizeof(AttrNumber) * attmap_length);
    for (i = 0; i < attmap_length; i++)
        attmap[i] = i + 1;

    /* generate an index statement */
    index_stmt = generateClonedIndexStmt(&cxt, source_idx, attmap, attmap_length, NULL, TRANSFORM_INVALID);

    if (tempIndexName != NULL)
        index_stmt->idxname = tempIndexName;

    if (OidIsValid(targetTblspcOid)) {
        /* generateClonedIndexStmt() maybe set tablespace name, so free it first. */
        if (index_stmt->tableSpace) {
            pfree_ext(index_stmt->tableSpace);
        }
        /* set target tablespace's name into index_stmt */
        index_stmt->tableSpace = get_tablespace_name(targetTblspcOid);
    }

    /* set is partitioned field */
    index_stmt->isPartitioned = partitionedIndex;

    /* don't do mem check, since there's no distribution info for new added temp table */
    index_stmt->skip_mem_check = true;

    /* Run parse analysis ... */
    index_stmt = transformIndexStmt(RelationGetRelid(source_relation), index_stmt, NULL);

    /* ... and do it */
    WaitState oldStatus = pgstat_report_waitstatus(STATE_CREATE_INDEX);
    ret = DefineIndex(RelationGetRelid(source_relation),
        index_stmt,
        InvalidOid, /* no predefined OID */
        true,      /* is_alter_table */
        true,       /* check_rights */
        skip_build, /* skip_build */
        true);     /* quiet */
    (void)pgstat_report_waitstatus(oldStatus);

    /* clean up */
    pfree_ext(attmap);
    relation_close(heap_rel, AccessShareLock);

    return ret.objectId;
}

/*
 * @hdfs
 * Brief        : set informational constraint flag in IndexStmt.
 * Description  : Set indexStmt's internal_flag. This flag will be set to false
 *                if indexStmt is built by "Creat index", otherwise be set to true.
 * Input        : the IndexStmt list.
 * Output       : none.
 * Return Value : none.
 * Notes        : This function is only used for HDFS foreign table.
 */
static void setInternalFlagIndexStmt(List* IndexList)
{
    ListCell* lc = NULL;

    Assert(IndexList != NIL);
    foreach (lc, IndexList) {
        IndexStmt* index = NULL;
        index = (IndexStmt*)lfirst(lc);
        index->internal_flag = true;
    }
}

/*
 * Brief        : Check the foreign table constraint type.
 * Description  : This function checks HDFS foreign table constraint type. The supported constraint
 *                types and some useful comment are:
 *                1. Only the primary key, unique, not null and null will be supported.
 *                2. Only "NOT ENFORCED" clause is supported for HDFS foreign table informational constraint.
 *                3. Multi-column combined informational constraint is forbidden.
 * Input        : node, the node needs to be checked.
 * Output       : none.
 * Return Value : none.
 * Notes        : none.
 */
void checkInformationalConstraint(Node* node, bool isForeignTbl)
{
    if (node == NULL) {
        return;
    }

    Constraint* constr = (Constraint*)node;

    /* Common table unsupport not force Constraint. */
    if (!isForeignTbl) {
        if (constr->inforConstraint && constr->inforConstraint->nonforced) {
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("It is not allowed to support \"NOT ENFORCED\" informational constraint.")));
        }
        return;
    }

    if (constr->contype == CONSTR_NULL || constr->contype == CONSTR_NOTNULL) {
        return;
    } else if (constr->contype == CONSTR_PRIMARY || constr->contype == CONSTR_UNIQUE) {
        /* HDFS foreign table only support not enforced informational primary key and unique Constraint. */
        if (constr->inforConstraint == NULL || !constr->inforConstraint->nonforced) {
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("The foreign table only support \"NOT ENFORCED\" informational constraint.")));
        }
    } else {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Only the primary key, unique, not null and null be supported.")));
    }

    if (constr->keys != NIL && list_length(constr->keys) != 1) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Multi-column combined informational constraint is forbidden.")));
    }
}

/*
 * @Description: Check Constraint.
 * @in cxt: CreateStmtContext or AlterTableStmt struct.
 * @in node: Constraint or ColumnDef.
 */
static void checkConstraint(CreateStmtContext* cxt, Node* node)
{
    bool canBuildInfoConstraint = cxt->canInfomationalConstraint;

    /* Judge constraint is valid. */
    if (IsA(node, Constraint)) {
        checkInformationalConstraint(node, canBuildInfoConstraint);
    } else if (IsA(node, ColumnDef)) {
        List* constList = ((ColumnDef*)node)->constraints;
        ListCell* cell = NULL;

        foreach (cell, constList) {
            Node* element = (Node*)lfirst(cell);

            if (IsA(element, Constraint)) {
                checkInformationalConstraint(element, canBuildInfoConstraint);
            }
        }
    }
}

/*
 * @Description: set skip mem check flag for index stmt. If the
 *	index is created just after table creation, we will not do
 *	memory check and adaption
 * @in IndexList: index list after table creation
 */
static void setMemCheckFlagForIdx(List* IndexList)
{
    ListCell* lc = NULL;

    Assert(IndexList != NIL);
    foreach (lc, IndexList) {
        IndexStmt* index = NULL;
        index = (IndexStmt*)lfirst(lc);
        index->skip_mem_check = true;
    }
}

/*
 * add_range_partition_def_state
 * 	add one partition def state into a List
 *
 * [IN] xL: List to be appended
 * [IN] boundary: a list of the end point (list_length must be 1)
 * [IN] partName: partition name
 * [IN] tblSpaceName: tablespace name
 *
 * RETURN: the partitionDefState List
 */
static List* add_range_partition_def_state(List* xL, List* boundary, const char* partName, const char* tblSpaceName)
{
    RangePartitionDefState* addState = makeNode(RangePartitionDefState);
    addState->boundary = boundary;
    addState->partitionName = pstrdup(partName);
    addState->tablespacename = pstrdup(tblSpaceName);
    addState->curStartVal = NULL;
    addState->partitionInitName = NULL;

    return lappend(xL, addState);
}

/*
 * get_range_partition_name_prefix
 * 	get partition name's prefix
 *
 * [out] namePrefix: an array of length NAMEDATALEN to store name prefix
 * [IN] srcName: src name
 * [IN] printNotice: print notice or not, default false
 *
 * RETURN: void
 */
void get_range_partition_name_prefix(char* namePrefix, char* srcName, bool printNotice, bool isPartition)
{
    errno_t ret = EOK;
    int len;

    /* namePrefix is an array of length NAMEDATALEN, so it's safe to store string */
    Assert(namePrefix && srcName);
    ret = sprintf_s(namePrefix, NAMEDATALEN, "%s", srcName);
    securec_check_ss(ret, "\0", "\0");

    len = strlen(srcName);
    if (len > LEN_PARTITION_PREFIX) {
        int k = pg_mbcliplen(namePrefix, len, LEN_PARTITION_PREFIX);
        namePrefix[k] = '\0';
        if (printNotice)
            ereport(NOTICE,
                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                    errmsg("%s name's prefix \"%s\" will be truncated to \"%s\"",
                        (isPartition ? "Partition" : "Slice"), srcName, namePrefix)));
    }
}

/* get_rel_partition_info
 * 	get detail info of a partition rel
 *
 * [IN] partTableRel: partition relation
 * [OUT] pos: position of the partition key
 * [OUT] upBound: up boundary of last partition
 *
 * RETURN: void
 */
static void get_rel_partition_info(Relation partTableRel, List** pos, Const** upBound)
{
    RangePartitionMap* partMap = NULL;
    int2vector* partitionKey = NULL;
    int partKeyNum;

    if (!RELATION_IS_PARTITIONED(partTableRel)) {
        ereport(ERROR,
            (errmodule(MOD_OPT),
                errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                errmsg("CAN NOT get detail info from a NON-PARTITIONED relation.")));
    }

    if (pos == NULL && upBound == NULL)
        return; /* nothing to do */

    partMap = (RangePartitionMap*)partTableRel->partMap;
    partitionKey = partMap->base.partitionKey;
    partKeyNum = partMap->base.partitionKey->dim1;

    /* get position of the partition key */
    if (pos != NULL) {
        List* m_pos = NULL;
        for (int i = 0; i < partKeyNum; i++)
            m_pos = lappend_int(m_pos, partitionKey->values[i] - 1);

        *pos = m_pos;
    }

    /* get up boundary of the last partition */
    if (upBound != NULL) {
        int partNum = getNumberOfPartitions(partTableRel);
        *upBound = (Const*)copyObject(partMap->rangeElements[partNum - 1].boundary[0]);
    }
}

/* get_src_partition_bound
 * 	get detail info of a partition rel
 *
 * [IN] partTableRel: partition relation
 * [IN] srcPartOid: src partition oid
 * [OUT] lowBound: low boundary of the src partition
 * [OUT] upBound: up boundary of the src partition
 *
 * RETURN: void
 */
static void get_src_partition_bound(Relation partTableRel, Oid srcPartOid, Const** lowBound, Const** upBound)
{
    RangePartitionMap* partMap = NULL;
    int srcPartSeq;

    if (!RELATION_IS_PARTITIONED(partTableRel)) {
        ereport(ERROR,
            (errmodule(MOD_OPT),
                errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                errmsg("CAN NOT get detail info from a NON-PARTITIONED relation.")));
    }

    if (lowBound == NULL && upBound == NULL)
        return; /* nothing to do */

    if (srcPartOid == InvalidOid)
        ereport(ERROR,
            (errmodule(MOD_OPT),
                errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                errmsg("CAN NOT get detail info from a partitioned relation WITHOUT specified partition.")));

    Assert(partTableRel->partMap->type == PART_TYPE_RANGE || partTableRel->partMap->type == PART_TYPE_INTERVAL);
    partMap = (RangePartitionMap*)partTableRel->partMap;

    srcPartSeq = partOidGetPartSequence(partTableRel, srcPartOid) - 1;
    if (lowBound != NULL) {
        if (srcPartSeq > 0)
            *lowBound = (Const*)copyObject(partMap->rangeElements[srcPartSeq - 1].boundary[0]);
        else
            *lowBound = NULL;
    }

    if (upBound != NULL)
        *upBound = (Const*)copyObject(partMap->rangeElements[srcPartSeq].boundary[0]);
}

/* get_split_partition_oid
 * 	get oid of the split partition
 *
 * [IN] partTableRel: partition relation
 * [IN] splitState: split partition state
 *
 * RETURN: oid of the partition to be splitted
 */
static Oid get_split_partition_oid(Relation partTableRel, SplitPartitionState* splitState)
{
    RangePartitionMap* partMap = NULL;
    Oid srcPartOid = InvalidOid;

    if (!RELATION_IS_PARTITIONED(partTableRel)) {
        ereport(ERROR,
            (errmodule(MOD_OPT),
                errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                errmsg("CAN NOT get partition oid from a NON-PARTITIONED relation.")));
    }

    partMap = (RangePartitionMap*)partTableRel->partMap;

    if (PointerIsValid(splitState->src_partition_name)) {
        srcPartOid = PartitionNameGetPartitionOid(RelationGetRelid(partTableRel),
            splitState->src_partition_name,
            PART_OBJ_TYPE_TABLE_PARTITION,
            AccessExclusiveLock,
            false,
            false,
            NULL,
            NULL,
            NoLock);
    } else {
        Assert(PointerIsValid(splitState->partition_for_values));
        splitState->partition_for_values = transformConstIntoTargetType(
            partTableRel->rd_att->attrs, partMap->base.partitionKey, splitState->partition_for_values);
        srcPartOid = PartitionValuesGetPartitionOid(
            partTableRel, splitState->partition_for_values, AccessExclusiveLock, true, false, false);    }

    return srcPartOid;
}

#define precheck_point_value_internal(a, isPartition)                                                                 \
    do {                                                                                                              \
        Node* pexpr = (Node*)linitial(a);                      /* original value */                                   \
        Const* pval = GetPartitionValue(pos, attrs, a, false, isPartition); /* cast(ori)::int */                      \
        if (!pval->ismaxvalue) {                                                                                      \
            Const* c = (Const*)evaluate_expr((Expr*)pexpr, exprType(pexpr), exprTypmod(pexpr), exprCollation(pexpr)); \
            if (partitonKeyCompare(&pval, &c, 1) != 0)                                                                \
                ereport(ERROR,                                                                                        \
                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),                                                       \
                        errmsg("start/end/every value must be an const-integer for %s \"%s\"",                        \
                            (isPartition ? "partition" : "slice"), defState->partitionName)));                        \
        }                                                                                                             \
    } while (0)

/*
 * precheck_start_end_defstate
 *    precheck start/end value of a range partition defstate
 */
static void precheck_start_end_defstate(List* pos, FormData_pg_attribute* attrs,
    RangePartitionStartEndDefState* defState, bool isPartition)
{
    ListCell* cell = NULL;

    if (pos == NULL || attrs == NULL || defState == NULL)
        ereport(ERROR,
            (errmodule(MOD_OPT),
                errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("unexpected parameter for precheck start/end defstate.")));

    Assert(pos->length == 1); /* already been checked in caller */
    foreach (cell, pos) {
        int i = lfirst_int(cell);

        switch (attrs[i].atttypid) {
            case INT2OID:
            case INT4OID:
            case INT8OID:
                if (defState->startValue)
                    precheck_point_value_internal(defState->startValue, isPartition);
                if (defState->endValue)
                    precheck_point_value_internal(defState->endValue, isPartition);
                if (defState->everyValue)
                    precheck_point_value_internal(defState->everyValue, isPartition);
                break;

            default:
                break; /* don't check */
        }
    }

    return;
}

/* is_start_end_def_list
 * 	check the partition state and return the type of state
 * 	true: start/end stmt; false: less/than stmt
 *
 * [IN] state: partition state
 *
 * RETURN: if it is start/end stmt
 */
bool is_start_end_def_list(List* def_list)
{
    ListCell* cell = NULL;

    if (def_list == NULL)
        return false;

    /* count start/end clause */
    foreach (cell, def_list) {
        Node* defState = (Node*)lfirst(cell);

        if (!IsA(defState, RangePartitionStartEndDefState))
            return false; /* not in start/end syntax, stop here */
    }

    return true;
}

/*
 * get_partition_arg_value
 * 	Get the actual value from the expression. There are only a limited range
 * 	of cases we must cover because the parser guarantees constant input.
 *
 * [IN] node: input node expr
 * [out] isnull: indicate the NULL of result datum
 *
 * RETURN: a datum produced by node
 */
static Datum get_partition_arg_value(Node* node, bool* isnull)
{
    Const* c = NULL;

    c = (Const*)evaluate_expr((Expr*)node, exprType(node), exprTypmod(node), exprCollation(node));
    if (!IsA(c, Const))
        ereport(ERROR,
            (errmodule(MOD_OPT),
                errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("partition parameter is not constant.")));

    *isnull = c->constisnull;
    return c->constvalue;
}

/*
 * evaluate_opexpr
 * Evaluate a basic operator expression from a partitioning specification.
 * The expression will only be an op expr but the sides might contain
 * a coercion function. The underlying value will be a simple constant,
 * however.
 *
 * If restypid is non-NULL and *restypid is set to InvalidOid, we tell the
 * caller what the return type of the operator is. If it is anything but
 * InvalidOid, coerce the operation's result to that type.
 *
 * [IN] pstate: parser state
 * [IN] oprname: name of opreator which can be <, +, -, etc.
 * [IN] leftarg: left arg
 * [IN] rightarg: right arg
 * [IN/OUT] restypid: result type id, if given coerce to it, otherwise return the compute result-type.
 * [IN] location: location of the expr, not necessary
 *
 * RETURN: a datum produced by the expr: "leftarg oprname rightarg"
 */
static Datum evaluate_opexpr(
    ParseState* pstate, List* oprname, Node* leftarg, Node* rightarg, Oid* restypid, int location)
{
    Datum res = 0;
    Datum lhs = 0;
    Datum rhs = 0;
    OpExpr* opexpr = NULL;
    bool byval = false;
    int16 len;
    Oid oprcode;
    Type typ;
    bool isnull = false;

    opexpr = (OpExpr*)make_op(pstate, oprname, leftarg, rightarg, pstate->p_last_srf, location);

    oprcode = get_opcode(opexpr->opno);
    if (oprcode == InvalidOid) /* should not fail */
        ereport(ERROR,
            (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                errmodule(MOD_OPT),
                errmsg("cache lookup failed for operator %u", opexpr->opno)));

    opexpr->opfuncid = oprcode;

    /* compute result */
    lhs = get_partition_arg_value((Node*)linitial(opexpr->args), &isnull);
    if (!isnull) {
        rhs = get_partition_arg_value((Node*)lsecond(opexpr->args), &isnull);
        if (!isnull)
            res = OidFunctionCall2(opexpr->opfuncid, lhs, rhs);
    }

    /* If the caller supplied a target result type, coerce if necesssary */
    if (PointerIsValid(restypid)) {
        if (OidIsValid(*restypid)) {
            if (*restypid != opexpr->opresulttype) {
                Expr* e = NULL;
                int32 typmod;
                Const* c = NULL;
                bool isnull = false;

                typ = typeidType(opexpr->opresulttype);
                c = makeConst(opexpr->opresulttype,
                    ((Form_pg_type)GETSTRUCT(typ))->typtypmod,
                    ((Form_pg_type)GETSTRUCT(typ))->typcollation,
                    typeLen(typ),
                    res,
                    false,
                    typeByVal(typ));
                ReleaseSysCache(typ);

                typ = typeidType(*restypid);
                typmod = ((Form_pg_type)GETSTRUCT(typ))->typtypmod;
                ReleaseSysCache(typ);

                /* coerce from oprresulttype to resttypid */
                e = (Expr*)coerce_type(NULL,
                    (Node*)c,
                    opexpr->opresulttype,
                    *restypid,
                    typmod,
                    COERCION_ASSIGNMENT,
                    COERCE_IMPLICIT_CAST,
                    NULL,
                    NULL,
                    -1);

                res = get_partition_arg_value((Node*)e, &isnull);
            }
        } else {
            *restypid = opexpr->opresulttype;
        }
    } else {
        return res;
    }

    /* copy result, done */
    Assert(OidIsValid(*restypid));
    typ = typeidType(*restypid);
    byval = typeByVal(typ);
    len = typeLen(typ);
    ReleaseSysCache(typ);

    res = datumCopy(res, byval, len);
    return res;
}

/*
 * coerce_partition_arg
 * 	coerce a partition parameter (start/end/every) to targetType
 *
 * [IN] pstate: parse state
 * [IN] node: Node to be coerced
 * [IN] targetType: target type
 *
 * RETURN: a const
 */
static Const* coerce_partition_arg(ParseState* pstate, Node* node, Oid targetType)
{
    Datum res;
    Oid curtyp;
    Const* c = NULL;
    Type typ = typeidType(targetType);
    int32 typmod = ((Form_pg_type)GETSTRUCT(typ))->typtypmod;
    int16 typlen = ((Form_pg_type)GETSTRUCT(typ))->typlen;
    bool typbyval = ((Form_pg_type)GETSTRUCT(typ))->typbyval;
    Oid typcollation = ((Form_pg_type)GETSTRUCT(typ))->typcollation;
    bool isnull = false;

    ReleaseSysCache(typ);

    curtyp = exprType(node);
    Assert(OidIsValid(curtyp));

    if (curtyp != targetType && OidIsValid(targetType)) {
        node = coerce_type(pstate, node, curtyp, targetType, typmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST,
        NULL, NULL, -1);

        if (!PointerIsValid(node))
            ereport(
                ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not coerce partitioning parameter.")));
    }
    res = get_partition_arg_value(node, &isnull);
    c = makeConst(targetType, typmod, typcollation, typlen, res, isnull, typbyval);

    return c;
}

/*
 * choose_coerce_type
 * 	choose a coerce type
 * Note: this function may help us to fix ambiguous problem
 */
static Oid choose_coerce_type(Oid leftid, Oid rightid)
{
    if (leftid == FLOAT8OID && rightid == NUMERICOID)
        return NUMERICOID; /* make_op chooses function float8pl to compute "float8 + numeric" */
    else
        return InvalidOid; /* let make_op decide */
}

/* check if everyVal <= endVal - startVal, but think of overflow, we should be careful */
static bool CheckStepInRange(ParseState *pstate, Const *startVal, Const *endVal, Const *everyVal,
    Form_pg_attribute attr)
{
    List *opr_mi = list_make1(makeString("-"));
    List *opr_lt = list_make1(makeString("<"));
    List *opr_le = list_make1(makeString("<="));

    Datum res;
    Oid targetType = attr->atttypid;
    Oid targetCollation = attr->attcollation;
    int16 targetLen = attr->attlen;
    bool targetByval = attr->attbyval;
    bool targetIsVarlena = (!targetByval) && (targetLen == -1);
    int32 targetTypmod;
    bool targetIsTimetype = (targetType == DATEOID || targetType == TIMESTAMPOID || targetType == TIMESTAMPTZOID);
    if (targetIsTimetype) {
        targetTypmod = -1; /* avoid accuracy-problem of date */
    } else {
        targetTypmod = attr->atttypmod;
    }

    bool flag1 = false; /* whether startVal < 0 */
    bool flag2 = false; /* whether endVal < 0 */
    if (!targetIsTimetype) {
        Const *zeroVal = NULL;
        if (targetType == NUMERICOID) {
            zeroVal = makeConst(NUMERICOID, targetTypmod, targetCollation, targetLen,
                (Datum)DirectFunctionCall3(numeric_in, CStringGetDatum("0.0"), ObjectIdGetDatum(0), Int32GetDatum(-1)),
                false, targetByval);
        } else if (targetIsVarlena) {
            /* if it's a varlena type, we make cstring type, type cast will be done in evaluate_opexpr */
            zeroVal = makeConst(UNKNOWNOID, -1, InvalidOid, -2, CStringGetDatum(""), false, false);
        } else {
            zeroVal = makeConst(targetType, targetTypmod, targetCollation, targetLen, (Datum)0, false, targetByval);
        }

        res = evaluate_opexpr(pstate, opr_lt, (Node *)startVal, (Node *)zeroVal, NULL, -1);
        flag1 = DatumGetBool(res); /* whether startVal < 0 */
        res = evaluate_opexpr(pstate, opr_lt, (Node *)endVal, (Node *)zeroVal, NULL, -1);
        flag2 = DatumGetBool(res); /* whether endVal < 0 */
    }

    /* we've checked startVal < endVal, so if startVal >= 0, make sure endVal >= 0 too,
     * (!flag1 && flag2) can never happen */
    Assert(flag1 || !flag2);

    /* there are some errors on interval compare, because it treats one month as 30days, but one year as 12 months.
     * so for time type, we don't use interval type. */
    if ((flag1 && !flag2) || targetIsTimetype) { /* startVal < 0 && endVal >= 0 */
        /* check if startVal <= endVal - everyVal */
        res = evaluate_opexpr(pstate, opr_mi, (Node *)endVal, (Node *)everyVal, &targetType, -1);
        Const *secheck = makeConst(targetType, targetTypmod, targetCollation, targetLen, res, false, targetByval);
        res = evaluate_opexpr(pstate, opr_le, (Node *)startVal, (Node *)secheck, NULL, -1);
    } else { /* startVal < 0 && endVal < 0, or startVal >= 0 && endVal >= 0 */
        /* check if everyVal <= endVal - startVal */
        res = evaluate_opexpr(pstate, opr_mi, (Node *)endVal, (Node *)startVal, &everyVal->consttype, -1);
        Const *secheck =
            makeConst(everyVal->consttype, targetTypmod, targetCollation, targetLen, res, false, targetByval);
        res = evaluate_opexpr(pstate, opr_le, (Node *)everyVal, (Node *)secheck, NULL, -1);
    }
    return DatumGetBool(res);
}

/*
 * divide_start_end_every_internal
 * 	internal implementaion for dividing an interval indicated by any-datatype
 * 	for example:
 * 	-- start(1) end(100) every(30)
 * 	-- start(123.01) end(345.09) every(111.99)
 * 	-- start('12-01-2012') end('12-05-2018') every('1 year')
 *
 * If (end-start) is divided by every with a remainder, then last partition is smaller
 * than others.
 *
 * [IN] pstate: parse state
 * [IN] partName: partition name
 * [IN] attr: pg_attribute of the target type
 * [IN] startVal: start value
 * [IN] endVal: end value
 * [IN] everyVal: interval value
 * [OUT] numPart: number of partitions
 * [IN] maxNum: max partition number allowed
 * [IN] isinterval: if EVERY is a interval value
 *
 * RETURN: end points of all sub-intervals
 */
static List* divide_start_end_every_internal(ParseState* pstate, char* partName, Form_pg_attribute attr,
    Const* startVal, Const* endVal, Node* everyExpr, int* numPart,
    int maxNum, bool isinterval, bool needCheck, bool isPartition)
{
    List* result = NIL;
    List* opr_pl = NIL;
    List* opr_mi = NIL;
    List* opr_lt = NIL;
    List* opr_le = NIL;
    List* opr_mul = NIL;
    List* opr_eq = NIL;
    Datum res;
    Const* pnt = NULL;
    Oid restypid;
    Const* curpnt = NULL;
    int32 nPart;
    Const* everyVal = NULL;
    Oid targetType;
    bool targetByval = false;
    int16 targetLen;
    int32 targetTypmod;
    Oid targetCollation;
    const char* partTypeName = (isPartition) ? "partition" : "slice";

    Assert(maxNum > 0 && maxNum <= MAX_PARTITION_NUM);

    opr_pl = list_make1(makeString("+"));
    opr_mi = list_make1(makeString("-"));
    opr_lt = list_make1(makeString("<"));
    opr_le = list_make1(makeString("<="));
    opr_mul = list_make1(makeString("*"));
    opr_eq = list_make1(makeString("="));

    /* get target type info */
    targetType = attr->atttypid;
    targetByval = attr->attbyval;
    targetCollation = attr->attcollation;
    if (targetType == DATEOID || targetType == TIMESTAMPOID || targetType == TIMESTAMPTZOID) {
        targetTypmod = -1; /* avoid accuracy-problem of date */
    } else {
        targetTypmod = attr->atttypmod;
    }
    targetLen = attr->attlen;

    /*
     * cast everyExpr to targetType
     * Note: everyExpr goes through transformExpr and transformIntoConst already.
     */
    everyVal = (Const*)GetTargetValue(attr, (Const*)everyExpr, isinterval);

    /* first compare start/end value */
    res = evaluate_opexpr(pstate, opr_le, (Node*)endVal, (Node*)startVal, NULL, -1);
    if (DatumGetBool(res)) {
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("start value must be less than end value for %s \"%s\".",
                    partTypeName, partName)));
    }

    /* second, we should make sure that everyVal <= endVal - startVal */
    if (!CheckStepInRange(pstate, startVal, endVal, everyVal, attr)) {
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("%s step is too big for %s \"%s\".", partTypeName, partTypeName, partName)));
    }

    /* build result */
    curpnt = startVal;
    nPart = 0;
    while (true) {
        /* if too many partitions, report error */
        if (nPart >= maxNum) {
            pfree_ext(pnt);
            ereport(ERROR,
                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                    errmsg("too many %ss after split %s \"%s\".", partTypeName, partTypeName, partName),
                    errhint("number of %ss can not be more than %d, MINVALUE will be auto-included if not assigned.",
                        partTypeName, MAX_PARTITION_NUM)));
        }

        /* compute curpnt + everyval, but if curpnt + everyVal > endVal, we use endVal instead, 
         * so we can make sure that pnt <= endVal */
        if (!CheckStepInRange(pstate, curpnt, endVal, everyVal, attr)) {
            pnt = (Const*)copyObject(endVal);
        } else {
            res = evaluate_opexpr(pstate, opr_pl, (Node*)curpnt, (Node*)everyVal, &targetType, -1);
            pnt = makeConst(targetType, targetTypmod, targetCollation, targetLen, res, false, targetByval);
            pnt = (Const*)GetTargetValue(attr, (Const*)pnt, false);
        }

        /* necessary check in first pass */
        if (nPart == 0) {
            /*
             * check ambiguous partition rule
             *
             * 1. start(1) end (1.00007) every(0.00001)  -- for float4 datatype
             *      cast(1 + 0.00001 as real)  !=  (1 + 0.00001)::numeric
             * This rule is ambiguous, error out.
             */
            if (needCheck) {
                Const* c = NULL;
                Const* uncast = NULL;
                Type typ;

                /* get every value, uncast */
                restypid = exprType(everyExpr);
                c = coerce_partition_arg(pstate, everyExpr, restypid);

                /* calculate start+every cast to proper type */
                restypid = choose_coerce_type(targetType, restypid);
                res = evaluate_opexpr(pstate, opr_pl, (Node*)startVal, (Node*)c, &restypid, -1);
                typ = typeidType(restypid);
                uncast = makeConst(restypid,
                    ((Form_pg_type)GETSTRUCT(typ))->typtypmod,
                    ((Form_pg_type)GETSTRUCT(typ))->typcollation,
                    typeLen(typ),
                    res,
                    false,
                    typeByVal(typ));
                ReleaseSysCache(typ);

                res = evaluate_opexpr(pstate, opr_eq, (Node*)pnt, (Node*)uncast, NULL, -1);
                if (!DatumGetBool(res))
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                            errmsg((isPartition ? 
                                "ambiguous partition rule is raised by EVERY parameter in partition \"%s\"." : 
                                "ambiguous distribution rule is raised by EVERY parameter in slice \"%s\"."),
                                partName)));
            }

            /* check partition step */
            res = evaluate_opexpr(pstate, opr_le, (Node*)pnt, (Node*)startVal, NULL, -1);
            if (DatumGetBool(res))
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                        errmsg("%s step is too small for %s \"%s\".", partTypeName, partTypeName, partName)));
        }

        /* pnt must be less than or equal to endVal */
        Assert(DatumGetBool(evaluate_opexpr(pstate, opr_le, (Node*)pnt, (Node*)endVal, NULL, -1)));
        result = lappend(result, pnt);
        nPart++;

        /* check to determine if it is the final partition */
        res = evaluate_opexpr(pstate, opr_eq, (Node*)pnt, (Node*)endVal, NULL, -1);
        if (DatumGetBool(res)) {
            break;
        }
        curpnt = pnt;
    }

    /* done */
    Assert(result && result->length == nPart);
    if (numPart != NULL) {
        *numPart = nPart;
    }

    return result;
}

template <bool isTimestampz> void CheckTIMESTAMPISFinite(Const* startVal, Const* endVal, char* partName) 
{
    if (isTimestampz) {
        TimestampTz t1 = DatumGetTimestampTz(startVal->constvalue);
        TimestampTz t2 = DatumGetTimestampTz(endVal->constvalue);
        if (TIMESTAMP_NOT_FINITE(t1) || TIMESTAMP_NOT_FINITE(t2))
            ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                    errmsg("partition \"%s\" is invalid.", partName),
                    errhint("INF can not appear in a (START, END, EVERY) clause.")));
    } else {
        Timestamp t1 = DatumGetTimestamp(startVal->constvalue);
        Timestamp t2 = DatumGetTimestamp(endVal->constvalue);
        if (TIMESTAMP_NOT_FINITE(t1) || TIMESTAMP_NOT_FINITE(t2))
            ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                    errmsg("partition \"%s\" is invalid.", partName),
                    errhint("INF can not appear in a (START, END, EVERY) clause.")));
    }
}

/*
 * DividePartitionStartEndInterval
 * 	divide the partition interval of start/end into specified sub-intervals
 *
 * [IN] pstate: parse state
 * [IN] attr: pg_attribute
 * [IN] partName: partition name
 * [IN] startVal: start value
 * [IN] endVal: end value
 * [IN] everyVal: interval value
 * [OUT] numPart: number of partitions
 * [IN] maxNum: max partition number allowed
 *
 * RETURN: end points of all sub-intervals
 */
static List* DividePartitionStartEndInterval(ParseState* pstate, Form_pg_attribute attr, char* partName,
    Const* startVal, Const* endVal, Const* everyVal, Node* everyExpr, int* numPart, int maxNum, bool isPartition)
{
    List* result = NIL;
    const char* partTypeName = (isPartition) ? "partition" : "slice";

    Assert(maxNum > 0 && maxNum <= MAX_PARTITION_NUM);
    Assert(attr != NULL);

    /* maxvalue is not allowed in start/end stmt */
    Assert(startVal && IsA(startVal, Const) && !startVal->ismaxvalue);
    Assert(endVal && IsA(endVal, Const) && !endVal->ismaxvalue);
    Assert(everyVal && IsA(everyVal, Const) && !everyVal->ismaxvalue);

    /* Form each partition node const */
    switch (attr->atttypid) {
        case NUMERICOID: {
            Numeric v1 = DatumGetNumeric(startVal->constvalue);
            Numeric v2 = DatumGetNumeric(endVal->constvalue);
            Numeric d = DatumGetNumeric(everyVal->constvalue);
            /* NAN is not allowed */
            if (NUMERIC_IS_NAN(v1) || NUMERIC_IS_NAN(v2) || NUMERIC_IS_NAN(d))
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                        errmsg("%s \"%s\" is invalid.",
                            partTypeName, partName),
                        errhint("NaN can not appear in a (START, END, EVERY) clause.")));

            result = divide_start_end_every_internal(
                pstate, partName, attr, startVal, endVal, everyExpr, numPart, maxNum, false, true, isPartition);
            break;
        }

        case FLOAT4OID: {
            float4 v1 = DatumGetFloat4(startVal->constvalue);
            float4 v2 = DatumGetFloat4(endVal->constvalue);
            float4 d = DatumGetFloat4(everyVal->constvalue);
            /* INF is not allowed */
            if (isinf(d) || isinf(v1) || isinf(v2))
                ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                        errmsg("%s \"%s\" is invalid.",
                            partTypeName, partName),
                        errhint("INF can not appear in a (START, END, EVERY) clause.")));

            result = divide_start_end_every_internal(
                pstate, partName, attr, startVal, endVal, everyExpr, numPart, maxNum, false, true, isPartition);
            break;
        }

        case FLOAT8OID: {
            float8 v1 = DatumGetFloat8(startVal->constvalue);
            float8 v2 = DatumGetFloat8(endVal->constvalue);
            float8 d = DatumGetFloat8(everyVal->constvalue);
            /* INF is not allowed */
            if (isinf(d) || isinf(v1) || isinf(v2))
                ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                        errmsg("%s \"%s\" is invalid.",
                            partTypeName, partName),
                        errhint("INF can not appear in a (START, END, EVERY) clause.")));

            result = divide_start_end_every_internal(
                pstate, partName, attr, startVal, endVal, everyExpr, numPart, maxNum, false, true, isPartition);
            break;
        }

        case INT2OID:
        case INT4OID:
        case INT8OID: {
            result = divide_start_end_every_internal(
                pstate, partName, attr, startVal, endVal, everyExpr, numPart, maxNum, false, false, isPartition);
            break;
        }

        case DATEOID:
        case TIMESTAMPOID: {
            Timestamp t1 = DatumGetTimestamp(startVal->constvalue);
            Timestamp t2 = DatumGetTimestamp(endVal->constvalue);
            if (TIMESTAMP_NOT_FINITE(t1) || TIMESTAMP_NOT_FINITE(t2))
                ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                        errmsg("%s \"%s\" is invalid.",
                            partTypeName, partName),
                        errhint("INF can not appear in a (START, END, EVERY) clause.")));
            result = divide_start_end_every_internal(
                pstate, partName, attr, startVal, endVal, everyExpr, numPart, maxNum, true, false, isPartition);
            break;
        }

        case TIMESTAMPTZOID: {
            TimestampTz t1 = DatumGetTimestampTz(startVal->constvalue);
            TimestampTz t2 = DatumGetTimestampTz(endVal->constvalue);
            if (TIMESTAMP_NOT_FINITE(t1) || TIMESTAMP_NOT_FINITE(t2))
                ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                        errmsg("%s \"%s\" is invalid.",
                            partTypeName, partName),
                        errhint("INF can not appear in a (START, END, EVERY) clause.")));
            result = divide_start_end_every_internal(
                pstate, partName, attr, startVal, endVal, everyExpr, numPart, maxNum, true, false, isPartition);
            break;
        }

        default:
            ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                    errmsg("unsupported datatype served as a %s key in the start/end clause.",
                        (isPartition ? "partition" : "distribution")),
                    errhint("Valid datatypes are: smallint, int, bigint, float4/real, float8/double, numeric, date and "
                            "timestamp [with time zone].")));
    }

    return result;
}

#define add_last_single_start_partition                                                                              \
    do {                                                                                                             \
        Const* laststart = NULL;                                                                                     \
        /* last DefState is a single START, so add the last partition here */                                        \
        Assert(lastState->startValue && !lastState->endValue);                                                       \
        pnt = (Const*)copyObject(startVal);                                                                          \
        boundary = list_make1(pnt);                                                                                  \
        laststart = GetPartitionValue(pos, attrs, lastState->startValue, false, isPartition);                        \
        if (partitonKeyCompare(&laststart, &startVal, 1) >= 0)                                                       \
            ereport(ERROR,                                                                                           \
                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),                                                          \
                    errmsg("start value of %s \"%s\" is too low.",                                                   \
                        (isPartition ? "partition" : "slice"), defState->partitionName),                             \
                    errhint("%s gap or overlapping is not allowed.",                                                 \
                                     (isPartition ? "partition" : "slice"))));                                       \
        if (lowBound == NULL && curDefState == 1) {                                                                  \
            /* last single START is the first DefState and MINVALUE is included */                                   \
            get_range_partition_name_prefix(namePrefix, lastState->partitionName, false, isPartition);               \
            ret = sprintf_s(partName, sizeof(partName), "%s_%d", namePrefix, 1);                                     \
            securec_check_ss(ret, "\0", "\0");                                                                       \
            newPartList = add_range_partition_def_state(newPartList, boundary, partName, lastState->tableSpaceName); \
            totalPart++;                                                                                             \
        } else {                                                                                                     \
            newPartList = add_range_partition_def_state(                                                             \
                newPartList, boundary, lastState->partitionName, lastState->tableSpaceName);                         \
            totalPart++;                                                                                             \
        }                                                                                                            \
        pfree_ext(laststart);                                                                                        \
    } while (0)

/*
 * transformRangePartStartEndStmt
 * 	entry of transform range partition which is defined by "start/end" syntax
 *
 * [IN] pstate: parse state
 * [IN] partitionList: partition list to be rewrote
 * [IN] attrs: pg_attribute item
 * [IN] pos: position of partition key in ColDef
 * [IN] existPartNum: number of partitions already exists
 * [IN] lowBound: low-boundary of all paritions
 * [IN] upBound: up-boundary of all partitions
 * [IN] needFree: free input partitionList or not, true: free, false: not
 * [IN] isPartition: true when checking partition definition, false when checking list/range distribution definition.
 *
 * lowBound/upBound rules:
 *
 *                                lowBound                       upBound
 *   not-NULL:    check SP == lowBound      check EP == upBound
 *                                                        (START) include upBound
 *
 *         NULL:         include MINVALUE      (START) include MAXVALUE
 * SP: first start point of the def; EP: final end point of the def
 * (START) include xxx: for a single start as final clause, include xxx.
 *
 * -- CREATE TABLE PARTITION: lowBound=NULL, upBound=NULL
 * -- ADD PARTITION: lowBound=ExistUpBound, upBound=NULL
 * -- SPLIT PARTITION: lowBound=CurrentPartLowBound, upBound=CurrentPartUpBound
 *
 * RETURN: a new partition list (wrote by "less/than" syntax).
 */
List* transformRangePartStartEndStmt(ParseState* pstate, List* partitionList, List* pos, FormData_pg_attribute* attrs,
    int32 existPartNum, Const* lowBound, Const* upBound, bool needFree, bool isPartition)
{
    ListCell* cell = NULL;
    int i, j;
    Oid target_type = InvalidOid;
    List* newPartList = NIL;
    char partName[NAMEDATALEN] = {0};
    char namePrefix[NAMEDATALEN] = {0};
    errno_t ret = EOK;
    Const* startVal = NULL;
    Const* endVal = NULL;
    Const* everyVal = NULL;
    Const* lastVal = NULL;
    int totalPart = 0;
    int numPart = 0;
    List* resList = NIL;
    List* boundary = NIL;
    Const* pnt = NULL;
    RangePartitionStartEndDefState* defState = NULL;
    RangePartitionStartEndDefState* lastState = NULL;
    int curDefState;
    int kc;
    ListCell* lc = NULL;
    char* curName = NULL;
    char* preName = NULL;
    bool isinterval = false;
    Form_pg_attribute attr = NULL;
    const char *partTypeName = (isPartition) ? "partition" : "slice";

    if (partitionList == NULL || list_length(partitionList) == 0 || attrs == NULL || pos == NULL)
        return partitionList; /* untouched */

    Assert(existPartNum >= 0 && existPartNum <= MAX_PARTITION_NUM);

    /* only one partition key is allowed */
    if (pos->length != 1) {
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                errmsg((isPartition ? "partitioned table has too many partition keys." : 
                    "distributed table has too many distribution keys.")),
                errhint((isPartition ? "start/end syntax requires a partitioned table with only one partition key." : 
                    "start/end syntax requires a distributed table with only one distribution key."))));
    }

    /*
     * Now, it is start/end stmt, check following key-points:
     *
     * - mixture of "start/end" and "less/than" is forbidden
     * - only one partition key is given
     * - datatype of partition key
     * - continuity of partitions
     * - number of partitions <= MAX_PARTITION_NUM
     * - validation of partition namePrefix
     */
    foreach (cell, partitionList) {
        RangePartitionStartEndDefState* defState = (RangePartitionStartEndDefState*)lfirst(cell);
        if ((defState->startValue && defState->startValue->length != 1) ||
            (defState->endValue && defState->endValue->length != 1) ||
            (defState->everyValue && defState->everyValue->length != 1))
            ereport(ERROR,
                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                    errmsg((isPartition ? "too many partition keys for partition \"%s\"." : 
                        "too many distribution keys for slice \"%s\"."), defState->partitionName),
                            errhint(isPartition ? "only one partition key is allowed in start/end clause." : 
                                "only one distribution key is allowed in start/end clause.")));
    }

    /* check partition name */
    check_partition_name_start_end(partitionList, isPartition);

    /* check: datatype of partition key */
    foreach (cell, pos) {
        i = lfirst_int(cell);
        attr = &attrs[i];
        target_type = attr->atttypid;

        switch (target_type) {
            case INT2OID:
            case INT4OID:
            case INT8OID:
            case NUMERICOID:
            case FLOAT4OID:
            case FLOAT8OID:
                isinterval = false;
                break;

            case DATEOID:
            case TIMESTAMPOID:
            case TIMESTAMPTZOID:
                isinterval = true;
                break;

            default:
                ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                        errmsg("datatype of column \"%s\" is unsupported for %s key in start/end clause.",
                            NameStr(attrs[i].attname), (isPartition ? "partition" : "distribution")),
                        errhint("Valid datatypes are: smallint, int, bigint, float4/real, float8/double, numeric, date "
                                "and timestamp [with time zone].")));
                break;
        }
    }

    /* check exist partition number */
    if (existPartNum >= MAX_PARTITION_NUM)
        ereport(ERROR,
            (errcode(ERRCODE_DATATYPE_MISMATCH),
                errmsg("can not add more %ss as %s number is already at its maximum.", partTypeName, partTypeName)));

    /*
     * Start transform (including check)
     *
     * Recall the syntax:
     *   start_end_item [, ...]
     *
     * where start_end_item:
     * 	{ start(a) end (b) [every(d)] }  |  start(a)  |  end (b)
     */
    curDefState = 0;
    totalPart = existPartNum;
    lastState = NULL;
    defState = NULL;
    lastVal = NULL;
    foreach (cell, partitionList) {
        lastState = defState;
        defState = (RangePartitionStartEndDefState*)lfirst(cell);
        Assert(defState);

        /* precheck defstate */
        precheck_start_end_defstate(pos, attrs, defState, isPartition);

        /* type-1: start + end + every */
        if (defState->startValue && defState->endValue && defState->everyValue) {
            Node* everyExpr = (Node*)linitial(defState->everyValue);
            startVal = GetPartitionValue(pos, attrs, defState->startValue, false, isPartition);
            endVal = GetPartitionValue(pos, attrs, defState->endValue, false, isPartition);
            everyVal = GetPartitionValue(pos, attrs, defState->everyValue, isinterval, isPartition);

            /* check value */
            if (startVal->ismaxvalue || endVal->ismaxvalue || everyVal->ismaxvalue)
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                        errmsg("%s \"%s\" is invalid.", partTypeName, defState->partitionName),
                        errhint("MAXVALUE can not appear in a (START, END, EVERY) clause.")));
            if (partitonKeyCompare(&startVal, &endVal, 1) >= 0)
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                        errmsg(
                            "start value must be less than end value for %s \"%s\".",
                            partTypeName, defState->partitionName)));

            if (lastVal != NULL) {
                if (lastVal->ismaxvalue)
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("%s \"%s\" is not allowed behind MAXVALUE.",
                                partTypeName, defState->partitionName)));

                kc = partitonKeyCompare(&lastVal, &startVal, 1);
                if (kc > 0)
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("start value of %s \"%s\" is too low.",
                                partTypeName, defState->partitionName),
                            errhint("%s gap or overlapping is not allowed.",
                                partTypeName)));
                if (kc < 0)
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("start value of %s \"%s\" is too high.",
                                partTypeName, defState->partitionName),
                            errhint("%s gap or overlapping is not allowed.",
                                partTypeName)));
            }

            /* build necessary MINVALUE, check lowBound,  append for last single START, etc. */
            if (lastVal == NULL) {
                if (lastState != NULL) {
                    /* last DefState is a single START */
                    add_last_single_start_partition;
                } else {
                    /* this is the first DefState (START, END, EVERY) */
                    if (lowBound == NULL) {
                        /* this is the first DefState (START, END, EVERY), add MINVALUE */
                        pnt = (Const*)copyObject(startVal);
                        boundary = list_make1(pnt);
                        get_range_partition_name_prefix(namePrefix, defState->partitionName, false, isPartition);
                        ret = sprintf_s(partName, sizeof(partName), "%s_%d", namePrefix, 0);
                        securec_check_ss(ret, "\0", "\0");
                        newPartList =
                            add_range_partition_def_state(newPartList, boundary, partName, defState->tableSpaceName);
                        totalPart++;
                    } else {
                        /* this is the first DefState (START, END, EVERY), but do not include MINVALUE */
                        /* check SP: case for ADD_PARTITION, SPLIT_PARTITION */
                        /* ignore: case for ADD_PARTITION, check SP: SPLIT_PARTITION */
                        if (NULL != lowBound && NULL != upBound) {
                            if (partitonKeyCompare(&lowBound, &startVal, 1) != 0)
                                ereport(ERROR,
                                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                        errmsg(
                                            "start value of %s \"%s\" NOT EQUAL up-boundary of last %s.",
                                            partTypeName, defState->partitionName, partTypeName)));
                        }
                    }
                }
            }

            /* add current DefState */
            get_range_partition_name_prefix(namePrefix, defState->partitionName, true, isPartition);
            Assert(totalPart < MAX_PARTITION_NUM);
            Assert(everyExpr);
            resList = DividePartitionStartEndInterval(pstate,
                attr,
                defState->partitionName,
                startVal,
                endVal,
                everyVal,
                everyExpr,
                &numPart,
                MAX_PARTITION_NUM - totalPart,
                isPartition);
            Assert(resList && numPart == resList->length);

            j = 1;
            foreach (lc, resList) {
                ret = sprintf_s(partName, sizeof(partName), "%s_%d", namePrefix, j);
                securec_check_ss(ret, "\0", "\0");
                boundary = list_make1(lfirst(lc));
                newPartList = add_range_partition_def_state(newPartList, boundary, partName, defState->tableSpaceName);

                if (j == 1) {
                    ((RangePartitionDefState*)llast(newPartList))->curStartVal = (Const*)copyObject(startVal);
                    ((RangePartitionDefState*)llast(newPartList))->partitionInitName = pstrdup(defState->partitionName);
                }

                j++;
            }
            list_free_ext(resList); /* can not be freed deeply */

            totalPart += numPart;

            /* update lastVal */
            pfree_ext(everyVal);
            if (NULL != lastVal)
                pfree_ext(lastVal);
            lastVal = endVal;
        } else if (defState->startValue && defState->endValue) {
            startVal = GetPartitionValue(pos, attrs, defState->startValue, false, isPartition);
            endVal = GetPartitionValue(pos, attrs, defState->endValue, false, isPartition);
            Assert(startVal != NULL && endVal != NULL);

            /* check value */
            if (startVal->ismaxvalue)
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                        errmsg("start value can not be MAXVALUE for %s \"%s\".", 
                            partTypeName, defState->partitionName)));

            if (partitonKeyCompare(&startVal, &endVal, 1) >= 0)
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                        errmsg(
                            "start value must be less than end value for %s \"%s\".", 
                            partTypeName, defState->partitionName)));

            if (lastVal != NULL) {
                if (lastVal->ismaxvalue)
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("%s \"%s\" is not allowed behind MAXVALUE.", 
                                partTypeName, defState->partitionName)));

                kc = partitonKeyCompare(&lastVal, &startVal, 1);
                if (kc > 0)
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("start value of %s \"%s\" is too low.", partTypeName, defState->partitionName),
                            errhint("%s gap or overlapping is not allowed.", partTypeName)));
                if (kc < 0)
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("start value of %s \"%s\" is too high.", partTypeName, defState->partitionName),
                            errhint("%s gap or overlapping is not allowed.", partTypeName)));
            }

            /* build less than defstate */
            if (lastVal != NULL) {
                /* last DefState is (START END EVERY) or (START END) or (END) */
                pnt = (Const*)copyObject(endVal);
                boundary = list_make1(pnt);
                newPartList = add_range_partition_def_state(
                    newPartList, boundary, defState->partitionName, defState->tableSpaceName);
                totalPart++;
            } else {
                if (lastState != NULL) {
                    /* last DefState is a single START */
                    add_last_single_start_partition;

                    /* add current DefState */
                    pnt = (Const*)copyObject(endVal);
                    boundary = list_make1(pnt);
                    newPartList = add_range_partition_def_state(
                        newPartList, boundary, defState->partitionName, defState->tableSpaceName);
                    totalPart++;
                } else if (lowBound == NULL) {
                    /* this is the first DefState (START, END), and MINVALUE will be included */
                    get_range_partition_name_prefix(namePrefix, defState->partitionName, false, isPartition);

                    /* MINVALUE */
                    pnt = (Const*)copyObject(startVal);
                    boundary = list_make1(pnt);
                    ret = sprintf_s(partName, sizeof(partName), "%s_%d", namePrefix, 0);
                    securec_check_ss(ret, "\0", "\0");
                    newPartList = add_range_partition_def_state(newPartList, boundary, partName, defState->tableSpaceName);
                    totalPart++;

                    pnt = (Const*)copyObject(endVal);
                    boundary = list_make1(pnt);
                    ret = sprintf_s(partName, sizeof(partName), "%s_%d", namePrefix, 1);
                    securec_check_ss(ret, "\0", "\0");
                    newPartList = add_range_partition_def_state(newPartList, boundary, partName, defState->tableSpaceName);
                    totalPart++;
                } else {
                    /* this is first DefState (START, END), but do not include MINVALUE */
                    /* check SP: case for ADD_PARTITION, SPLIT_PARTITION */
                    /* ignore: case for ADD_PARTITION, check SP: SPLIT_PARTITION */
                    if (NULL != lowBound && NULL != upBound) {
                        if (partitonKeyCompare(&lowBound, &startVal, 1) != 0)
                            ereport(ERROR,
                                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                    errmsg("start value of %s \"%s\" NOT EQUAL up-boundary of last %s.",
                                        partTypeName, defState->partitionName, partTypeName)));
                    }

                    /* add endVal as a pnt */
                    pnt = (Const*)copyObject(endVal);
                    boundary = list_make1(pnt);
                    newPartList = add_range_partition_def_state(
                        newPartList, boundary, defState->partitionName, defState->tableSpaceName);
                    if (NULL != newPartList) {
                        ((RangePartitionDefState*)llast(newPartList))->curStartVal = (Const*)copyObject(startVal);
                    }

                    totalPart++;
                }
            }

            if (NULL != lastVal)
                pfree_ext(lastVal);
            lastVal = endVal;
        } else if (defState->startValue) {
            startVal = GetPartitionValue(pos, attrs, defState->startValue, false, isPartition);
            Assert(startVal != NULL);

            /* check value */
            if (startVal->ismaxvalue)
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                        errmsg("start value can not be MAXVALUE for %s \"%s\".", 
                            partTypeName, defState->partitionName)));

            if (lastVal != NULL) {
                if (lastVal->ismaxvalue)
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("%s \"%s\" is not allowed behind MAXVALUE.", 
                                partTypeName, defState->partitionName)));

                kc = partitonKeyCompare(&lastVal, &startVal, 1);
                if (kc > 0)
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("start value of %s \"%s\" is too low.", partTypeName, defState->partitionName),
                            errhint("%s gap or overlapping is not allowed.", partTypeName)));
                if (kc < 0)
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("start value of %s \"%s\" is too high.", partTypeName, defState->partitionName),
                            errhint("%s gap or overlapping is not allowed.", partTypeName)));
            }

            /* build less than defstate */
            if (lastVal == NULL) {
                if (lastState != NULL) {
                    /* last DefState is a single START */
                    add_last_single_start_partition;
                } else {
                    /* this is the first DefState */
                    if (lowBound == NULL) {
                        /* this is the first DefState, and MINVALUE will be included */
                        get_range_partition_name_prefix(namePrefix, defState->partitionName, false, isPartition);
                        pnt = (Const*)copyObject(startVal);
                        boundary = list_make1(pnt);
                        ret = sprintf_s(partName, sizeof(partName), "%s_%d", namePrefix, 0);
                        securec_check_ss(ret, "\0", "\0");

                        /* add MINVALUE here, the other partition will be added in next DefState because the endVal is
                         * unknown right now */
                        newPartList =
                            add_range_partition_def_state(newPartList, boundary, partName, defState->tableSpaceName);
                        totalPart++;
                    } else {
                        /* this is the first DefState, do not include MINVALUE */
                        /* check SP: case for ADD_PARTITION, SPLIT_PARTITION */
                        /* ignore: case for ADD_PARTITION, check SP: SPLIT_PARTITION */
                        if (NULL != lowBound && NULL != upBound) {
                            if (partitonKeyCompare(&lowBound, &startVal, 1) != 0)
                                ereport(ERROR,
                                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                        errmsg(
                                            "start value of %s \"%s\" NOT EQUAL up-boundary of last %s.",
                                            partTypeName, defState->partitionName, partTypeName)));
                        }
                    }
                }
            }

            if (NULL != lastVal)
                pfree_ext(lastVal);
            lastVal = NULL;
        } else if (defState->endValue) {
            endVal = GetPartitionValue(pos, attrs, defState->endValue, false, isPartition);
            Assert(endVal != NULL);

            /* check value */
            if (lastVal != NULL) {
                if (lastVal->ismaxvalue)
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("%s \"%s\" is not allowed behind MAXVALUE.", 
                                partTypeName, defState->partitionName)));

                if (partitonKeyCompare(&lastVal, &endVal, 1) >= 0) {
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("end value of %s \"%s\" is too low.", partTypeName, defState->partitionName),
                            errhint("%s gap or overlapping is not allowed.", partTypeName)));
                }
            }

            /* build a less than defState: we need a last partition, or it is a first partition here */
            if (lastVal == NULL) {
                if (lastState != NULL) {
                    /* last def is a single START, invalid definition */
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                            errmsg("%s \"%s\" is an invalid definition clause.", partTypeName, defState->partitionName),
                            errhint("Do not use a single END after a single START.")));
                } else {
                    /* this is the first def state END, check lowBound if any */
                    /* case for ADD_PARTITION, SPLIT_PARTITION */
                    if (lowBound && partitonKeyCompare(&lowBound, &endVal, 1) >= 0)
                        ereport(ERROR,
                            (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                errmsg(
                                    "end value of %s \"%s\" MUST be greater than up-boundary of last %s.",
                                    partTypeName, defState->partitionName, partTypeName)));
                }
            }
            pnt = (Const*)copyObject(endVal);
            boundary = list_make1(pnt);
            newPartList =
                add_range_partition_def_state(newPartList, boundary, defState->partitionName, defState->tableSpaceName);
            totalPart++;

            if (lastVal != NULL) {
                pfree_ext(lastVal); 
            }
            lastVal = endVal;
            startVal = NULL;
        } else {
            Assert(false); /* unexpected syntax */
        }

        /* -- */
        /* check partition numbers */
        if (totalPart >= MAX_PARTITION_NUM) {
            if (totalPart == MAX_PARTITION_NUM && !lnext(cell)) {
                break;
            } else {
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                        errmsg("too many %ss after split %s \"%s\".",
                            partTypeName, partTypeName, defState->partitionName),
                        errhint("number of %ss can not be more than %d, MINVALUE will be auto-included if not "
                                "assigned.",
                            partTypeName, MAX_PARTITION_NUM)));
            }
        }

        curDefState++;
    }

    /* Final stage: add upBound for a single START at last */
    if (!defState->endValue) {
        /* this is a single START */
        Assert(defState->startValue);

        /* first check upBound */
        if (upBound == NULL) {
            /* no upBound, means up-Boundary is MAXVALUE: case for CREATE, ADD_PARTITION */
            pnt = makeNode(Const);
            pnt->ismaxvalue = true;
            boundary = list_make1(pnt);
        } else {
            /* have upBound: case for SPLIT PARTITION */
            if (partitonKeyCompare(&upBound, &startVal, 1) <= 0)
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                        errmsg("start value of %s \"%s\" MUST be less than up-boundary of the %s to be "
                               "splitted.",
                            partTypeName, defState->partitionName, partTypeName)));

            pnt = (Const*)copyObject(upBound);
            boundary = list_make1(pnt);
        }

        /* second check lowBound */
        if (lowBound == NULL && curDefState == 1) {
            /* we have no lowBound, and this is a first def, so MINVALUE already been added */
            get_range_partition_name_prefix(namePrefix, defState->partitionName, false, isPartition);
            ret = sprintf_s(partName, sizeof(partName), "%s_%d", namePrefix, 1);
            securec_check_ss(ret, "\0", "\0");
            newPartList = add_range_partition_def_state(newPartList, boundary, partName, defState->tableSpaceName);
        } else {
            newPartList =
                add_range_partition_def_state(newPartList, boundary, defState->partitionName, defState->tableSpaceName);
            if (NULL != newPartList && NULL != defState->startValue) {
                ((RangePartitionDefState*)llast(newPartList))->curStartVal =
                    (Const*)copyObject(linitial(defState->startValue));
            }
        }
        totalPart++;
    } else {
        /* final def has endVal, just check upBound if any, case for SPLIT_PARTITION */
        if (upBound && partitonKeyCompare(&upBound, &endVal, 1) != 0) {
            ereport(ERROR,
                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                    errmsg("end value of %s \"%s\" NOT EQUAL up-boundary of the %s to be splitted.",
                        partTypeName, defState->partitionName, partTypeName)));
        }
    }

    /* necessary check */
    if (totalPart > MAX_PARTITION_NUM) {
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                errmsg("too many %ss after split partition \"%s\".", partTypeName, defState->partitionName),
                errhint("number of %ss can not be more than %d, MINVALUE will be auto-included if not assigned.",
                    partTypeName, MAX_PARTITION_NUM)));
    }

    /* since splitting partition is done, check partition name again */
    foreach (cell, newPartList) {
        lc = cell;
        preName = ((RangePartitionDefState*)lfirst(cell))->partitionName;
        while (NULL != (lc = lnext(lc))) {
            curName = ((RangePartitionDefState*)lfirst(lc))->partitionName;
            if (!strcmp(curName, preName)) {
                ereport(ERROR,
                    (errcode(ERRCODE_DUPLICATE_OBJECT),
                        errmsg("duplicate %s name: \"%s\".", partTypeName, curName),
                            errhint("%ss defined by (START, END, EVERY) are named as \"%sName_x\" where x is "
                                "an integer and starts from 0 or 1.", partTypeName, partTypeName)));
            }
        }
    }

    /* it's ok, done */
    Assert(newPartList && newPartList->length == totalPart - existPartNum);
    if (needFree) {
        list_free_deep(partitionList); /* deep free is ok */
        partitionList = NULL;
    }

    if (NULL != startVal)
        pfree_ext(startVal);
    if (NULL != lastVal)
        pfree_ext(lastVal);

    return newPartList;
}

/*
 * Check if CreateStmt contains TableLikeClause, and the table to be defined is
 * on different nodegrop with the parent table.
 *
 * CreateStmt: the Stmt need check.
 */
bool check_contains_tbllike_in_multi_nodegroup(CreateStmt* stmt)
{
    ListCell* elements = NULL;
    Relation relation = NULL;
    foreach (elements, stmt->tableElts) {
        if (IsA(lfirst(elements), TableLikeClause)) {
            TableLikeClause* clause = (TableLikeClause*)lfirst(elements);
            relation = relation_openrv(clause->relation, AccessShareLock);

            if (is_multi_nodegroup_createtbllike(stmt->subcluster, relation->rd_id)) {
                heap_close(relation, AccessShareLock);
                return true;
            }

            heap_close(relation, AccessShareLock);
        }
    }

    return false;
}

/*
 * Check if the parent table and the table to be define in the same cluseter.
 * oid : the parent Table OID
 * subcluster: the new table where to create
 */
bool is_multi_nodegroup_createtbllike(PGXCSubCluster* subcluster, Oid oid)
#ifdef ENABLE_MULTIPLE_NODES
{
    Oid like_group_oid;
    bool multi_nodegroup = false;
    char* group_name = NULL;
    Oid new_group_oid = ng_get_installation_group_oid();

    if (subcluster != NULL) {
        ListCell* lc = NULL;
        foreach (lc, subcluster->members) {
            group_name = strVal(lfirst(lc));
        }
        if (group_name != NULL)
            new_group_oid = get_pgxc_groupoid(group_name);
    }

    like_group_oid = get_pgxc_class_groupoid(oid);
    multi_nodegroup = (new_group_oid != like_group_oid);

    return multi_nodegroup;
}
#else
{
    DISTRIBUTED_FEATURE_NOT_SUPPORTED();
    return false;
}
#endif

static bool IsCreatingSeqforAnalyzeTempTable(CreateStmtContext* cxt)
{
    if (cxt->relation->relpersistence != RELPERSISTENCE_TEMP) {
            return false;
    }
    bool isUnderAnalyze = false;
    if (IS_PGXC_COORDINATOR || IS_SINGLE_NODE) {
        isUnderAnalyze = u_sess->analyze_cxt.is_under_analyze;
    } else if (IS_PGXC_DATANODE) {
        /*
         * cannot send 'u_sess->analyze_cxt.is_under_analyze' to dn, so we have to compare the relname with 'pg_analyze'
         * to determine the analyze state of dn.
         */
        isUnderAnalyze = (strncmp(cxt->relation->relname, ANALYZE_TEMP_TABLE_PREFIX,
                                 strlen(ANALYZE_TEMP_TABLE_PREFIX)) == 0);
    }
    return isUnderAnalyze;
}

static void CheckAutoIncrementIndex(CreateStmtContext *cxt)
{
    ListCell *clc = NULL;
    ListCell *ilc = NULL;
    bool has_autoinc = false;
    foreach (clc, cxt->columns) {
        ColumnDef* column = (ColumnDef*)lfirst(clc);
        if (!column->raw_default || !IsA(column->raw_default, AutoIncrement)) {
            continue;
        }
        if (has_autoinc) {
            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                (errmsg("Incorrect table definition, there can be only one auto_increment column"))));
        }
        has_autoinc = true;

        bool has_found = false;
        foreach (ilc, cxt->ixconstraints) {
            Constraint* cons =  (Constraint *)lfirst(ilc);
            if (cons->contype != CONSTR_PRIMARY && cons->contype != CONSTR_UNIQUE) {
                continue;
            }
            Assert(cons->keys->length > 0);
            IndexElem *elem = (IndexElem *)lfirst(cons->keys->head);
            if (elem->name != NULL && strcasecmp(elem->name, column->colname) == 0) {
                has_found = true;
                break;
            }
        }

        if (has_found) {
            continue;
        }

        foreach (ilc, cxt->inh_indexes) {
            IndexStmt* index =  (IndexStmt *)lfirst(ilc);
            if (!index->unique && !index->primary) {
                continue;
            }
            Assert(index->indexParams->length > 0);
            IndexElem *elem = (IndexElem *)lfirst(index->indexParams->head);
            if (elem->name != NULL && strcasecmp(elem->name, column->colname) == 0) {
                has_found = true;
                break;
            }
        }

        if (!has_found) {
            /* AUTO_INCREMENT will be checked in CheckRelAutoIncrementIndex after executing alter table. */
            if (cxt->node != NULL && IsA(cxt->node, AlterTableStmt)) {
                continue;
            }
            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                (errmsg("Incorrect table definition, auto_increment column must be defined as a key"))));
        }
    }
}

static int128 TransformAutoIncStart(CreateStmt* stmt)
{
    int128 autoinc;
    DefElem* defel = (DefElem*)stmt->autoIncStart;
    if (defel == NULL) {
        return (int128)1;
    }
    if (IsA(defel->arg, Integer)) {
        autoinc = (int128)intVal(defel->arg);
    } else { /* T_Float */
        autoinc = DatumGetInt128(DirectFunctionCall1(int16in, CStringGetDatum(strVal(defel->arg))));
    }
    if (autoinc <= 0) {
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("auto_increment value must be greater than 0")));
    }
    return autoinc;
}

static void TransformTempAutoIncrement(ColumnDef* column, CreateStmt* stmt)
{
    int128 autoinc;
    Const* constnode = NULL;
    AutoIncrement* autoincnode = NULL;
    Constraint* constraint = NULL;

    if (stmt) {
        autoinc = TransformAutoIncStart(stmt);
    } else {
        autoinc = (int128)1;
    }
    /* build default expression */
    constnode = makeConst(INT16OID, -1, InvalidOid, sizeof(int128), Int128GetDatum(autoinc), false, false);
    autoincnode = makeNode(AutoIncrement);
    autoincnode->expr = (Node*)constnode;
    constraint = makeNode(Constraint);
    constraint->contype = CONSTR_DEFAULT;
    constraint->location = -1;
    constraint->raw_expr = (Node*)autoincnode;
    constraint->cooked_expr = NULL;
    column->constraints = lappend(column->constraints, constraint);
    column->raw_default = constraint->raw_expr;
}

/*
 * semtc_generate_hash_partition_defs
 *     Generate a specified number of partition definitions.
 * prefix_name: parent partition name for subpartition, used as a prefix for subpartition names to be generated
 * part_count: number of partitions to be generated
 */
static List* semtc_generate_hash_partition_defs(CreateStmt* stmt, const char* prefix_name, int part_count)
{
    char namebuf[NAMEDATALEN] = {0};
    List* result = NULL;
    HashPartitionDefState *def = NULL;
    A_Const *con = NULL;
    uint32 prefix_len = prefix_name ? (uint32)strlen(prefix_name) : 0;
    uint32 len;
    errno_t rc = EOK;

    for (int i = 0; i < part_count; i++) {
        def = makeNode(HashPartitionDefState);
        if (prefix_name) {
            rc = snprintf_s(namebuf, sizeof(namebuf), sizeof(namebuf) - 1, "sp%d", i);
        } else {
            rc = snprintf_s(namebuf, sizeof(namebuf), sizeof(namebuf) - 1, "p%d", i);
        }
        securec_check_ss(rc, "", "");

        len = (uint32)strlen(namebuf) + prefix_len;
        def->partitionName = (char*)palloc0(len + 1);
        if (prefix_name) {
            rc = memcpy_s(def->partitionName, len + 1, prefix_name, prefix_len);
            securec_check(rc, "\0", "\0");
        }
        rc = strncpy_s(def->partitionName + prefix_len, len + 1 - prefix_len, namebuf, len - prefix_len);
        securec_check(rc, "\0", "\0");
        if (len >= NAMEDATALEN) {
            ereport(ERROR, (errcode(ERRCODE_NAME_TOO_LONG),
                errmsg("identifier too long"),
                errdetail("The %s name \"%s\" is too long",
                    prefix_name ? "subpartition" : "partition", def->partitionName)));
        }
        con = makeNode(A_Const);
        con->val.type = T_Integer;
        con->val.val.ival = i;
        con->location = -1;
        def->boundary = list_make1(con);
        def->tablespacename = stmt->tablespacename;
        def->subPartitionDefState = NULL;
        result = lappend(result, def);
    }
    return result;
}

static void TransformModifyColumnDatatype(CreateStmtContext* cxt, AlterTableCmd* cmd)
{
    ColumnDef *def = (ColumnDef *)cmd->def;
    bool new_set = false;
    /* pre-alter column type is an set, should drop it after */
    bool old_set = DropSetOwnedByTable(cxt, cmd->name);
 
    if (def->typname && list_length(def->typname->names) == 1 && !def->typname->pct_type) {
        char* tname = strVal(linitial(def->typname->names));
        if (strcmp(tname, "set") == 0) {
            if (old_set) {
                ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
                    errmsg("can not alter column type to another set")));
            } else { /* alter the column to a new set type, should create it before */
                PrecheckColumnTypeForSet(cxt, def->typname);
                new_set = true;
                def->typname->typeOid = InvalidOid; // pg_attribute.atttypid
                cxt->rel_coll_id = InvalidOid;
                CreateSetOwnedByTable(cxt, def, def->colname);
            }
        } else if (strcmp(tname, "smallserial") == 0 || strcmp(tname, "serial2") == 0 || strcmp(tname, "serial") == 0 ||
            strcmp(tname, "serial4") == 0 || strcmp(tname, "bigserial") == 0 || strcmp(tname, "serial8") == 0 ||
            strcmp(tname, "largeserial") == 0 || strcmp(tname, "serial16") == 0) {
            ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION),
                errmsg("Invalid modify column operation"),
                errdetail("cannot modify or change column to type '%s'", tname)));
        }
    }
 
    /* can NOT change column to an existed set data type */
    Type tup = LookupTypeName(cxt->pstate, def->typname, NULL);
    if (HeapTupleIsValid(tup)) {
        Form_pg_type typform = (Form_pg_type)GETSTRUCT(tup);
        if (typform->typtype == TYPTYPE_SET) {
            ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                errmsg("can not use existed set type %s for column definition",
                    format_type_be(HeapTupleGetOid(tup)))));
        }
        ReleaseSysCache(tup);
    }
 
    /* Do necessary work on the column type declaration. But for set type,
     * no need to check before because the type has not created yet.
     */
    if (!new_set && def->typname) {
        transformColumnType(cxt, def);
    }
}
 
static void DropModifyColumnAutoIncrement(CreateStmtContext* cxt, Relation rel, const char* colname)
{
    AttrNumber attnum = get_attnum(rel->rd_id, colname);
    if (attnum <= 0 || attnum != RelAutoIncAttrNum(rel)) {
        return;
    }
    char* seqname = get_rel_name(RelAutoIncSeqOid(rel));
    if (!seqname) { /* shouldn't happen */
        return;
    }
    DropStmt *drop = makeNode(DropStmt);
    drop->removeType = OBJECT_LARGE_SEQUENCE;
    drop->missing_ok = true;
    drop->objects = list_make1(list_make1(makeString(seqname)));
    drop->arguments = NIL;
    drop->behavior = DROP_RESTRICT;
    drop->concurrent = false;
    drop->purge = false;
 
    cxt->alist = lappend(cxt->alist, drop);
}
 
static void TransformModifyColumndef(CreateStmtContext* cxt, AlterTableCmd* cmd)
{
    ColumnDef *def = (ColumnDef *)cmd->def;
    /* check constraints type */
    checkConstraint(cxt, (Node*)def);
    // check datatype
    TransformModifyColumnDatatype(cxt, cmd);
    // check attr constraints
    transformConstraintAttrs(cxt, def->constraints);
    cxt->columns = lappend(cxt->columns, (Node*)def);
    TransformColumnDefinitionConstraints(cxt, def, false, true);
    if (def->clientLogicColumnRef != NULL) {
        ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION),
            errmsg("Invalid modify column operation"),
            errdetail("modify or change column to encrypted column is not supported")));
    }
    /* for column options like comment clause */
    TransformColumnDefinitionOptions(cxt, def);
    // drop old auto_increment
    DropModifyColumnAutoIncrement(cxt, cxt->rel, cmd->name);
    /* for CHANGE column */
    if (strcmp(cmd->name, def->colname) != 0) {
        RenameStmt *rename = makeNode(RenameStmt);
        rename->renameType = OBJECT_COLUMN;
        rename->relationType = OBJECT_TABLE;
        rename->relation = cxt->relation;
        rename->subname = cmd->name;
        rename->newname = def->colname;
        rename->missing_ok = false;
        rename->is_modifycolumn = true;
        cxt->blist = lappend(cxt->blist, rename);
    }
}

static int identity_only(ColumnDef* column)
{
    ListCell* clist = NULL;
    Constraint* constraint = NULL;
    int result = 0;
    foreach (clist, column->constraints) {
        constraint = (Constraint*)lfirst(clist);

        if (constraint->contype == CONSTR_IDENTITY) {
            result += 1;
        }
    }
    return result;
}

/*
 * Get the column type which is constrained by identity in D mode.
 * Check the type is expected or not, and report error for invalid type.
 */
static void identity_type_dmod(ColumnDef* column)
{
    int s = 0;
    Oid newtypid = typenameTypeId(NULL, column->typname);
    if (newtypid == NUMERICOID &&
        column->typname->typmods != NULL &&
        list_length(column->typname->typmods) == 2) {
            s = intVal(&((A_Const*)lfirst(list_tail(column->typname->typmods)))->val);
    }

    /* invalid type check */
    if (newtypid != INT2OID &&
        newtypid != INT4OID &&
        newtypid != INT8OID &&
        newtypid != INT1OID &&
        newtypid != INT16OID &&
        (newtypid != NUMERICOID || (newtypid == NUMERICOID && s != 0)))
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("Identity column '%s' data type invalid.",
                     column->colname)));
}