/* -------------------------------------------------------------------------
 *
 * analyze.cpp
 *	  transform the raw parse tree into a query tree
 *
 * For optimizable statements, we are careful to obtain a suitable lock on
 * each referenced table, and other modules of the backend preserve or
 * re-obtain these locks before depending on the results.  It is therefore
 * okay to do significant semantic analysis of these statements.  For
 * utility commands, no locks are obtained here (and if they were, we could
 * not be sure we'd still have them at execution).  Hence the general rule
 * for utility commands is to just dump them into a Query node untransformed.
 * DECLARE CURSOR, EXPLAIN, and CREATE TABLE AS are exceptions because they
 * contain optimizable statements, which we should transform.
 *
 *
 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 * Portions Copyright (c) 2021, openGauss Contributors
 *
 *	src/common/backend/parser/analyze.cpp
 *
 * -------------------------------------------------------------------------
 */

#include "postgres.h"
#include "knl/knl_variable.h"

#include "access/sysattr.h"
#ifdef PGXC
#include "catalog/pg_inherits.h"
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type_fn.h"
#include "catalog/pgxc_class.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "commands/typecmds.h"
#include "utils/date.h"
#include "utils/fmgroids.h"
#include "utils/snapmgr.h"
#endif
#include "catalog/pg_auth_history.h"
#include "catalog/pg_type.h"
#include "executor/node/nodeModifyTable.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_cte.h"
#include "parser/parse_hint.h"
#include "parser/parse_merge.h"
#include "parser/parse_oper.h"
#include "parser/parse_param.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "parser/parse_expr.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteRlsPolicy.h"
#include "rewrite/rewriteHandler.h"
#ifdef PGXC
#include "miscadmin.h"
#include "pgxc/pgxc.h"
#include "pgxc/pgxcnode.h"
#include "access/gtm.h"
#include "utils/distribute_test.h"
#include "utils/lsyscache.h"
#include "optimizer/pgxcplan.h"
#include "tcop/tcopprot.h"
#include "nodes/nodes.h"
#include "pgxc/poolmgr.h"
#include "catalog/pgxc_node.h"
#include "access/xact.h"
#include "utils/distribute_test.h"
#include "tcop/utility.h"
#endif
#include "utils/rel.h"
#include "utils/rel_gs.h"
#include "utils/acl.h"
#include "utils/typcache.h"
#include "commands/explain.h"
#include "commands/sec_rls_cmds.h"
#include "commands/sequence.h"
#include "streaming/streaming_catalog.h"
#include "instruments/instr_unique_sql.h"
#include "streaming/init.h"

#include "db4ai/aifuncs.h"
#include "db4ai/create_model.h"
#include "db4ai/hyperparameter_validation.h"

#ifdef ENABLE_MOT
#include "storage/mot/jit_exec.h"
#endif

#ifndef ENABLE_MULTIPLE_NODES
#include "optimizer/clauses.h"
#endif
/* Hook for plugins to get control at end of parse analysis */
THR_LOCAL post_parse_analyze_hook_type post_parse_analyze_hook = NULL;
static const int MILLISECONDS_PER_SECONDS = 1000;

static Query* transformDeleteStmt(ParseState* pstate, DeleteStmt* stmt);
static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt);
static void checkUpsertTargetlist(Relation targetTable, List* updateTlist);
static UpsertExpr* transformUpsertClause(ParseState* pstate, UpsertClause* upsertClause, List* resultRelations);
static int count_rowexpr_columns(ParseState* pstate, Node* expr);
static void transformVariableSetStmt(ParseState* pstate, VariableSetStmt* stmt);
static Query* transformVariableMutiSetStmt(ParseState* pstate, VariableMultiSetStmt* muti_stmt);
static Query* transformSelectStmt(
    ParseState* pstate, SelectStmt* stmt, bool isFirstNode = true, bool isCreateView = false);
static Query *transformUnrotateStmt(ParseState *pstate, SelectStmt *stmt);
static Query* transformValuesClause(ParseState* pstate, SelectStmt* stmt);
static Query* transformSetOperationStmt(ParseState* pstate, SelectStmt* stmt);
static Node* transformSetOperationTree(ParseState* pstate, SelectStmt* stmt, bool isTopLevel, List** targetlist);
static void determineRecursiveColTypes(ParseState* pstate, Node* larg, List* nrtargetlist);
static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt);
static List* transformUpdateTargetList(ParseState* pstate, List* qryTlist, List* origTlist, List* resultRelations);
static List* transformReturningList(ParseState* pstate, List* returningList);
static Query* transformDeclareCursorStmt(ParseState* pstate, DeclareCursorStmt* stmt);
static Query* transformExplainStmt(ParseState* pstate, ExplainStmt* stmt);
static Query* transformCreateTableAsStmt(ParseState* pstate, CreateTableAsStmt* stmt);
static void CheckDeleteRelation(Relation targetrel);
static void CheckUpdateRelation(Relation targetrel);
static void transformVariableSetValueStmt(ParseState* pstate, VariableSetStmt* stmt);
static bool ContainColStoreWalker(Node* node, Oid targetOid);
static bool ContainAStoreWalker(Node* node, Oid targetOid);
#ifdef PGXC
static Query* transformExecDirectStmt(ParseState* pstate, ExecDirectStmt* stmt);
static bool IsExecDirectUtilityStmt(const Node* node);
static bool is_relation_child(RangeTblEntry* child_rte, List* rtable);
static bool is_rel_child_of_rel(RangeTblEntry* child_rte, RangeTblEntry* parent_rte);
#endif

static List* remove_update_redundant_relation(List* resultRelations, List* target_rangetblentry);
static void transformLockingClause(ParseState* pstate, Query* qry, LockingClause* lc, bool pushedDown);
static bool IsFindHDFSForeignTbl(Query* qry);

static bool checkForeignTableExist(List* rte_table_list);
static void set_subquery_is_under_insert(ParseState* subParseState);
static void set_ancestor_ps_contain_foreigntbl(ParseState* subParseState);
static bool include_groupingset(Node* groupClause);
static void transformGroupConstToColumn(ParseState* pstate, Node* groupClause, List* targetList);
static bool checkAllowedTableCombination(ParseState* pstate);

static bool IsValuesCanTransformDirectly(ParseState* pstate, InsertStmt* stmt);
static List* GetTargetColumnAttrs(ParseState* pstate, List* cols, int exprCnt);
static void GenerateTargetList(Query* query, RangeTblEntry* rte, int rtindex, List* targetColsAttrs);
static void CheckInsertTargetRelation(ParseState* pstate, InsertStmt* stmt, Relation targetrel, bool isRelationNullOk);
static void CheckColumnExprsConsistency(ParseState* pstate, List* cols, List* firstRowList, int finalTargetCnt);
static void GetColumnTypeAttrs(List* targetColsAttrs, ColumnTypeForm* typeItems);
static Node* TransformAconstToTarget(A_Const* con, ColumnTypeForm& typeItem, bool hasIgnore);
static List* TransformAllValuesDirectly(ParseState* pstate, SelectStmt* selectStmt, List* targetColsAttrs);
static Query* TryTransformInsertDirectly(ParseState* pstate, InsertStmt* stmt);

#ifdef ENABLE_MULTIPLE_NODES
static bool ContainSubLinkWalker(Node* node, void* context);
static bool ContainSubLink(Node* clause);
#endif /* ENABLE_MULTIPLE_NODES */

#ifndef ENABLE_MULTIPLE_NODES
static const char* NOKEYUPDATE_KEYSHARE_ERRMSG = "/NO KEY UPDATE/KEY SHARE";
#else
static const char* NOKEYUPDATE_KEYSHARE_ERRMSG = "";
#endif

/*
 * parse_analyze
 *		Analyze a raw parse tree and transform it to Query form.
 *
 * Optionally, information about $n parameter types can be supplied.
 * References to $n indexes not defined by paramTypes[] are disallowed.
 *
 * The result is a Query node.	Optimizable statements require considerable
 * transformation, while utility-type statements are simply hung off
 * a dummy CMD_UTILITY Query node.
 */
Query* parse_analyze(
    Node* parseTree, const char* sourceText, Oid* paramTypes, int numParams, bool isFirstNode, bool isCreateView, ParseState* parent_pstate)
{
    ParseState* pstate = make_parsestate(parent_pstate);
    Query* query = NULL;
    if (u_sess) { // make sure to initial status.
        u_sess->parser_cxt.fmt_str = NULL;
        u_sess->parser_cxt.nls_fmt_str = NULL;
    }

    /* required as of 8.4 */
    AssertEreport(sourceText != NULL, MOD_OPT, "para cannot be NULL");

    pstate->p_sourcetext = sourceText;

    if (numParams > 0) {
        parse_fixed_parameters(pstate, paramTypes, numParams);
    }

    PUSH_SKIP_UNIQUE_SQL_HOOK();
    query = transformTopLevelStmt(pstate, parseTree, isFirstNode, isCreateView);
    POP_SKIP_UNIQUE_SQL_HOOK();

    /* it's unsafe to deal with plugins hooks as dynamic lib may be released */
    if (post_parse_analyze_hook && !(g_instance.status > NoShutdown)) {
        (*post_parse_analyze_hook)(pstate, query);
    }

    // should free at parent if parent_pstate is not null
    if (parent_pstate == NULL) {
        pfree_ext(pstate->p_ref_hook_state);
    }
    pstate->rightRefState = nullptr;
    free_parsestate(pstate);

    /* For plpy CTAS query. CTAS is a recursive call. CREATE query is the first rewrited.
     * thd 2nd rewrited query is INSERT SELECT. Without this attribute, DB will have
     * an error that has no idea about $x when INSERT SELECT query is analyzed.
     */
    query->fixed_paramTypes = paramTypes;
    query->fixed_numParams = numParams;

    return query;
}

/*
 * parse_analyze_varparams
 *
 * This variant is used when it's okay to deduce information about $n
 * symbol datatypes from context.  The passed-in paramTypes[] array can
 * be modified or enlarged (via repalloc).
 */

Query* parse_analyze_varparams(Node* parseTree, const char* sourceText, Oid** paramTypes, int* numParams)
{
    ParseState* pstate = make_parsestate(NULL);
    Query* query = NULL;

    /* required as of 8.4 */
    AssertEreport(sourceText != NULL, MOD_OPT, "para cannot be NULL");

    pstate->p_sourcetext = sourceText;

    parse_variable_parameters(pstate, paramTypes, numParams);

    query = transformTopLevelStmt(pstate, parseTree);

    /* make sure all is well with parameter types */
    check_variable_parameters(pstate, query);

    /* it's unsafe to deal with plugins hooks as dynamic lib may be released */
    if (post_parse_analyze_hook && !(g_instance.status > NoShutdown)) {
        (*post_parse_analyze_hook)(pstate, query);
    }

    pfree_ext(pstate->p_ref_hook_state);
    free_parsestate(pstate);

    /* For plpy CTAS query. CTAS is a recursive call. CREATE query is the first rewrited.
     * thd 2nd rewrited query is INSERT SELECT. Without this attribute, DB will have
     * an error that has no idea about $x when INSERT SELECT query is analyzed.
     */
    Assert(PointerIsValid(query));
    query->fixed_paramTypes = *paramTypes;
    query->fixed_numParams = *numParams;

    return query;
}

/*
 * parse_sub_analyze
 *		Entry point for recursively analyzing a sub-statement.
 */
Query* parse_sub_analyze(Node* parseTree, ParseState* parentParseState, CommonTableExpr* parentCTE,
    bool locked_from_parent, bool resolve_unknowns)
{
    ParseState* pstate = make_parsestate(parentParseState);
    Query* query = NULL;

    pstate->p_parent_cte = parentCTE;
    pstate->p_locked_from_parent = locked_from_parent;
    pstate->p_resolve_unknowns = resolve_unknowns;
    if (u_sess->attr.attr_sql.td_compatible_truncation && u_sess->attr.attr_sql.sql_compatibility == C_FORMAT)
        set_subquery_is_under_insert(pstate); /* Set p_is_in_insert for parse state.*/

    query = transformStmt(pstate, parseTree);

    free_parsestate(pstate);

    return query;
}

Node* parse_into_claues(Node* parseTree, IntoClause* intoClause)
{
    if (intoClause->userVarList) {
        UserSetElem* uset = makeNode(UserSetElem);
        uset->name = intoClause->userVarList;

        SubLink* sl = makeNode(SubLink);
        sl->subLinkType = EXPR_SUBLINK;
        sl->testexpr = NULL;
        sl->operName = NIL;
        sl->subselect = parseTree;
        sl->location = -1;

        SelectIntoVarList *sis = makeNode(SelectIntoVarList);
        sis->sublink = sl;
        sis->userVarList = uset->name;

        uset->val = (Expr *)sis;

        VariableSetStmt* vss = makeNode(VariableSetStmt);
        vss->kind = VAR_SET_DEFINED;
        vss->name = "SELECT INTO VARLIST";
        vss->defined_args = list_make1((Node *)uset);
        vss->is_local = false;
        vss->is_multiset = true;

        VariableMultiSetStmt* vmss = makeNode(VariableMultiSetStmt);
        vmss->args = list_make1((Node *)vss);
        return (Node *)vmss;
    } else if (intoClause->filename) {
        CopyStmt* cn = makeNode(CopyStmt);
        cn->relation = NULL;
        cn->attlist = NIL;
        cn->is_from = false;
        cn->options = intoClause->copyOption;
        cn->filename = intoClause->filename;
        cn->filetype = intoClause->is_outfile ? S_OUTFILE : S_DUMPFILE;
        
        cn->query = parseTree;
        return (Node*)cn;
    } else {
        CreateTableAsStmt* ctas = makeNode(CreateTableAsStmt);
        ctas->query = parseTree;
        ctas->into = intoClause;
        ctas->relkind = OBJECT_TABLE;
        ctas->is_select_into = true;
        return (Node*)ctas;
    }
}

/*
 * transformTopLevelStmt -
 *	  transform a Parse tree into a Query tree.
 *
 * The only thing we do here that we don't do in transformStmt() is to
 * convert SELECT ... INTO into CREATE TABLE AS.  Since utility statements
 * aren't allowed within larger statements, this is only allowed at the top
 * of the parse tree, and so we only try it before entering the recursive
 * transformStmt() processing.
 */
Query* transformTopLevelStmt(ParseState* pstate, Node* parseTree, bool isFirstNode, bool isCreateView)
{
    if (IsA(parseTree, SelectStmt)) {
        SelectStmt* stmt = (SelectStmt*)parseTree;

        /* If it's a set-operation tree, drill down to leftmost SelectStmt */
        while (stmt != NULL && stmt->op != SETOP_NONE)
            stmt = stmt->larg;
        AssertEreport(stmt && IsA(stmt, SelectStmt) && stmt->larg == NULL, MOD_OPT, "failure to check parseTree");

        if (stmt->intoClause) {
            parseTree = parse_into_claues(parseTree, stmt->intoClause);
            /*
            * Remove the intoClause from the SelectStmt.  This makes it safe
            * for lexical_select_stmt to complain if it finds intoClause set
            * (implying that the INTO appeared in a disallowed place).
            */
            stmt->intoClause = NULL;
        }
    }

    if (u_sess->hook_cxt.transformStmtHook != NULL) {
        return
            ((transformStmtFunc)(u_sess->hook_cxt.transformStmtHook))(pstate, parseTree, isFirstNode, isCreateView);
    }
    return transformStmt(pstate, parseTree, isFirstNode, isCreateView);
}


Query* transformCreateModelStmt(ParseState* pstate, CreateModelStmt* stmt)
{
    SelectStmt* select_stmt = (SelectStmt*) stmt->select_query;

    stmt->algorithm     = get_algorithm_ml(stmt->architecture);
    if (stmt->algorithm == INVALID_ALGORITHM_ML) {
        ereport(ERROR, (errmodule(MOD_DB4AI), errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("Non recognized ML model architecture definition %s", stmt->architecture)));
    }
    if (SearchSysCacheExists1(DB4AI_MODEL, CStringGetDatum(stmt->model))) {
        ereport(ERROR, (errmodule(MOD_DB4AI), errcode(ERRCODE_INVALID_PARAMETER_VALUE),
            errmsg("The model name \"%s\" already exists in gs_model_warehouse.", stmt->model)));
    }

    // Create the projection for the AI operator in the query plan
    // If the algorithm is supervised, the target is always the first element of the list
    if (is_supervised(stmt->algorithm)) {
        if (list_length(stmt->model_features) == 0) {
            ereport(ERROR, (errmodule(MOD_DB4AI), errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("Supervised ML algorithms require FEATURES clause")));
        }else if (list_length(stmt->model_target) == 0) {
            ereport(ERROR, (errmodule(MOD_DB4AI), errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("Supervised ML algorithms require TARGET clause")));
        }else if (list_length(stmt->model_target) > 1) {
            ereport(ERROR, (errmodule(MOD_DB4AI), errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("Target clause only supports one expression")));
        }
    }else{
        if (list_length(stmt->model_target) > 0) {
            ereport(ERROR, (errmodule(MOD_DB4AI), errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("Unsupervised ML algorithms cannot have TARGET clause")));
        }
    }

    select_stmt->targetList = NULL;
    foreach_cell(it, stmt->model_target) {
        select_stmt->targetList = lappend(select_stmt->targetList, lfirst(it));
    }

    if (list_length(stmt->model_features) > 0) { // User given projection
        foreach_cell(it, stmt->model_features) {
            select_stmt->targetList = lappend(select_stmt->targetList, lfirst(it));
        }

    } else { // No projection
        ResTarget *rt = makeNode(ResTarget);
        ColumnRef *cr = makeNode(ColumnRef);

        cr->fields = list_make1(makeNode(A_Star));
        cr->location = -1;

        rt->name = NULL;
        rt->indirection = NIL;
        rt->val = (Node *)cr;
        rt->location = -1;
        select_stmt->targetList = lappend(select_stmt->targetList, rt);
    }

    // Transform the select query that we prepared for the training operator
    Query* select_query = transformStmt(pstate, (Node*) select_stmt);
    stmt->select_query  = (Node*) select_query;

    /* represent the command as a utility Query */
    Query* result = makeNode(Query);
    result->commandType = CMD_UTILITY;
    result->utilityStmt = (Node*)stmt;

    return result;
}

Query* transformVariableCreateEventStmt(ParseState* pstate, CreateEventStmt* stmt)
{
    Query* result = makeNode(Query);
    result->commandType = CMD_UTILITY;
    Node* old_time_expr = NULL;
    Node* new_time_expr = NULL;
    if (stmt->start_time_expr) {
        old_time_expr = stmt->start_time_expr;
        new_time_expr = transformExprRecurse(pstate, old_time_expr);
        stmt->start_time_expr = new_time_expr;
    }
    if (stmt->end_time_expr) {
        old_time_expr = stmt->end_time_expr;
        new_time_expr = transformExprRecurse(pstate, old_time_expr);
        stmt->end_time_expr = new_time_expr;
    }
    result->utilityStmt = (Node*)stmt;
    return result;
}
 
Query* transformVariableAlterEventStmt(ParseState* pstate, AlterEventStmt* stmt)
{
    Query* result = makeNode(Query);
    result->commandType = CMD_UTILITY;
    Node* old_time_expr = NULL;
    Node* new_time_node = NULL;
    DefElem* new_time_expr = NULL;
    if (stmt->start_time_expr) {
        old_time_expr = stmt->start_time_expr->arg;
        new_time_node = transformExprRecurse(pstate, old_time_expr);
        if (new_time_node) {
            new_time_expr = makeDefElem("start_date", new_time_node);
        } else {
            new_time_expr = NULL;
        }
        stmt->start_time_expr = new_time_expr;
    }
    if (stmt->end_time_expr) {
        old_time_expr = stmt->end_time_expr->arg;
        new_time_node = transformExprRecurse(pstate, old_time_expr);
        if (new_time_node) {
            new_time_expr = makeDefElem("start_date", new_time_node);
        } else {
            new_time_expr = NULL;
        }
        stmt->end_time_expr = new_time_expr;
    }
    result->utilityStmt = (Node*)stmt;
    return result;
}

static Query* TransformCompositeTypeStmt(ParseState* pstate, CompositeTypeStmt* stmt)
{
    Query* result = makeNode(Query);
    /* object type handle replace by itself */
    if (stmt->typekind == TYPE_COMPOSITE_OBJECT_TYPE || stmt->typekind == TYPE_COMPOSITE_OBJECT_TYPE_BODY) {
        result->utilityStmt = (Node*)stmt;
        result->commandType = CMD_UTILITY;
        return result;
    }
    result->commandType = CMD_UTILITY;

    Oid old_type_oid = InvalidOid;
    Oid typeNamespace = InvalidOid;
    RangeVar* typevar = stmt->typevar;

    typeNamespace = RangeVarGetAndCheckCreationNamespace(typevar, NoLock, NULL, RELKIND_COMPOSITE_TYPE);
    RangeVarAdjustRelationPersistence(typevar, typeNamespace);
    old_type_oid = GetSysCacheOid2(TYPENAMENSP, CStringGetDatum(typevar->relname), ObjectIdGetDatum(typeNamespace));

    if (OidIsValid(old_type_oid) && stmt->replace) {
        HeapTuple typtuple = NULL;
        Form_pg_type typform = NULL;
        typtuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(old_type_oid));
        if (!HeapTupleIsValid(typtuple)) {
            ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), 
                errmsg("cache lookup failed for type %u", old_type_oid)));
        }
        typform = (Form_pg_type)GETSTRUCT(typtuple);
        
        /* shell type or autogenerated array type */
        if (moveArrayTypeName(old_type_oid, NameStr(typform->typname), typeNamespace)) {
            ReleaseSysCache(typtuple);
            result->utilityStmt = (Node*)stmt;
            return result;
        }

        if (TYPTYPE_COMPOSITE != typform->typtype) {
            ReleaseSysCache(typtuple);
            ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                errmsg("type already exists but not a composite type")));
        }

        ObjectAddress address;
        ObjectAddressSet(address, TypeRelationId, old_type_oid);
        /* Check if any table/function depends on this type */
        ReplaceTypeCheckRef(&address);

        TupleDesc tupledesc = lookup_rowtype_tupdesc(old_type_oid, typform->typtypmod);

        AlterTableStmt* alterStmt = makeNode(AlterTableStmt);
        alterStmt->fromReplace = true;
        alterStmt->relation = stmt->typevar;
        alterStmt->relkind = OBJECT_TYPE;
        /* lappend DROP attribute stmt */
        for (int i = 0; i < tupledesc->natts; i++) {
            Form_pg_attribute oldAttribute = &tupledesc->attrs[i];
            if (oldAttribute->attisdropped) {
                continue;
            }
            AlterTableCmd *n = makeNode(AlterTableCmd);
            n->subtype = AT_DropColumn;
            n->name = NameStr(oldAttribute->attname);
            n->behavior = DROP_RESTRICT;
            n->missing_ok = FALSE;
            alterStmt->cmds = lappend(alterStmt->cmds, n);
        }
        /* lappend ADD attribute stmt */
        ListCell* lc;
        foreach(lc, stmt->coldeflist) {
            ColumnDef* newAttr = (ColumnDef*)lfirst(lc);
            AlterTableCmd *n = makeNode(AlterTableCmd);
            n->subtype = AT_AddColumn;
            n->def = (Node*)newAttr;
            n->behavior = DROP_RESTRICT;
            alterStmt->cmds = lappend(alterStmt->cmds, n);
        }
        ReleaseTupleDesc(tupledesc);
        ReleaseSysCache(typtuple);

        result->utilityStmt = (Node*)alterStmt;
    } else {
        result->utilityStmt = (Node*)stmt;
    }

    return result;
}

/*
 * transformStmt -
 *	  recursively transform a Parse tree into a Query tree.
 */
Query* transformStmt(ParseState* pstate, Node* parseTree, bool isFirstNode, bool isCreateView)
{
    Query* result = NULL;
    AnalyzerRoutine *analyzerRoutineHook = (AnalyzerRoutine*)u_sess->hook_cxt.analyzerRoutineHook;

    if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) {
        pstate->p_is_flt_frame = true;
    }

    switch (nodeTag(parseTree)) {
            /*
             * Optimizable statements
             */
        case T_InsertStmt: {
            InsertStmt* insertStmt = (InsertStmt*)parseTree;
            result = TryTransformInsertDirectly(pstate, insertStmt);
            if (!result) {
                result = transformInsertStmt(pstate, insertStmt);
            }
            break;
        }
        case T_DeleteStmt:
            result = transformDeleteStmt(pstate, (DeleteStmt*)parseTree);
            break;

        case T_UpdateStmt:
            result = transformUpdateStmt(pstate, (UpdateStmt*)parseTree);
            break;

        case T_MergeStmt:
            result = transformMergeStmt(pstate, (MergeStmt*)parseTree);
            break;

        case T_SelectStmt: {
            SelectStmt* n = (SelectStmt*)parseTree;
            if (n->valuesLists) {
                result = transformValuesClause(pstate, n);
            } else if (n->op == SETOP_NONE) {
                if (analyzerRoutineHook == NULL || analyzerRoutineHook->transSelect == NULL) {
                    result = transformSelectStmt(pstate, n, isFirstNode, isCreateView);
                } else {
                    result = analyzerRoutineHook->transSelect(pstate, n, isFirstNode, isCreateView);
                }
            } else {
                result = transformSetOperationStmt(pstate, n);
            }
        } break;

            /*
             * Special cases
             */
        case T_DeclareCursorStmt:
            result = transformDeclareCursorStmt(pstate, (DeclareCursorStmt*)parseTree);
            break;

        case T_ExplainStmt:
            result = transformExplainStmt(pstate, (ExplainStmt*)parseTree);
            break;

#ifdef PGXC
        case T_ExecDirectStmt:
            result = transformExecDirectStmt(pstate, (ExecDirectStmt*)parseTree);
            break;
#endif

        case T_CreateTableAsStmt:
            result = transformCreateTableAsStmt(pstate, (CreateTableAsStmt*)parseTree);
            break;

        case T_CreateModelStmt:
            result = transformCreateModelStmt(pstate, (CreateModelStmt*) parseTree);
            break;

        case T_PrepareStmt: {
            PrepareStmt* n = (PrepareStmt *)parseTree;
            if (IsA(n->query, UserVar)) {
                Node *uvar = transformExpr(pstate, n->query, EXPR_KIND_OTHER);
                n->query = (Node *)copyObject((UserVar *)uvar);
            }
            result = makeNode(Query);
            result->commandType = CMD_UTILITY;
            result->utilityStmt = (Node*)parseTree;
        } break;

        case T_VariableSetStmt: {
                VariableSetStmt* stmt = (VariableSetStmt*)parseTree;

                if (DB_IS_CMPT(B_FORMAT) && stmt->kind == VAR_SET_VALUE &&
                    (u_sess->attr.attr_common.enable_set_variable_b_format || ENABLE_SET_VARIABLES)) {
                    transformVariableSetValueStmt(pstate, stmt);
                }
                result = makeNode(Query);
                result->commandType = CMD_UTILITY;
                result->utilityStmt = (Node*)parseTree;
            } break;

        case T_VariableMultiSetStmt: 
            result = transformVariableMutiSetStmt(pstate, (VariableMultiSetStmt*)parseTree);
            break;

        case T_CreateEventStmt:
            result = transformVariableCreateEventStmt(pstate, (CreateEventStmt*) parseTree);
            break;

        case T_AlterEventStmt:
            result = transformVariableAlterEventStmt(pstate, (AlterEventStmt*) parseTree);
            break;

        case T_CompositeTypeStmt:
            result = TransformCompositeTypeStmt(pstate, (CompositeTypeStmt*) parseTree);
            break;

        default:

            /*
             * other statements don't require any transformation; just return
             * the original parsetree with a Query node plastered on top.
             */
            result = makeNode(Query);
            result->commandType = CMD_UTILITY;
            result->utilityStmt = (Node*)parseTree;
            break;
    }

    /* To be compatible before multi-relation modification supported. */
    result->resultRelation = linitial2_int(result->resultRelations);
    /* Mark as original query until we learn differently */
    result->querySource = QSRC_ORIGINAL;
    result->canSetTag = true;
    result->has_uservar = pstate->has_uservar;

    /* Mark whether synonym object is in rtables or not. */
    result->hasSynonyms = pstate->p_hasSynonyms;

    result->is_flt_frame = pstate->p_is_flt_frame && !IS_ENABLE_RIGHT_REF(pstate->rightRefState);

    if (nodeTag(parseTree) != T_InsertStmt) {
        result->rightRefState = nullptr;
    }

    PreventCommandDuringSSOndemandRedo(parseTree);
    return result;
}

/*
 * analyze_requires_snapshot
 *		Returns true if a snapshot must be set before doing parse analysis
 *		on the given raw parse tree.
 *
 * Classification here should match transformStmt(); but we also have to
 * allow a NULL input (for Parse/Bind of an empty query string).
 */
bool analyze_requires_snapshot(Node* parseTree)
{
    bool result = false;

    if (parseTree == NULL)
        return false;

    switch (nodeTag(parseTree)) {
            /*
             * Optimizable statements
             */
        case T_InsertStmt:
        case T_DeleteStmt:
        case T_UpdateStmt:
        case T_MergeStmt:
        case T_SelectStmt:
            result = true;
            break;

            /*
             * Special cases
             */
        case T_DeclareCursorStmt:
            /* yes, because it's analyzed just like SELECT */
            result = true;
            break;

        case T_ExplainStmt:
        case T_CreateTableAsStmt:
            /* yes, because we must analyze the contained statement */
            result = true;
            break;

#ifdef PGXC
        case T_ExecDirectStmt:

            /*
             * We will parse/analyze/plan inner query, which probably will
             * need a snapshot. Ensure it is set.
             */
            result = true;
            break;
#endif
        case T_RefreshMatViewStmt:
            /* yes, because the SELECT from pg_rewrite must be analyzed */
            if ((!((RefreshMatViewStmt *)parseTree)->incremental) || IS_PGXC_DATANODE) {
                result = true;
            }
            break;

        case T_VariableMultiSetStmt:
            /* user-defined variables support sublink */
                result = true;
            break;

        default:
            /* other utility statements don't have any real parse analysis */
            result = false;
            break;
    }

    return result;
}

/*
 * Description: check if the delete stmt is for 'plan_table'. If delete mutil relations, report err.
 * Parameters:
 * @in relations: the delete tables list.
 * Return: ture if the object name is plan_table
 */
static bool checkDeleteStmtForPlanTable(List* relations)
{
    OnlyDeleteFromPlanTable = false;
    const char* target_rel = V_PLAN_TABLE;
    ListCell *lc = NULL;
    Oid plan_table_data_oid = RelnameGetRelid(T_PLAN_TABLE_DATA);
    foreach (lc, relations) {
        RangeVar* rv = (RangeVar*)lfirst(lc);
        if (strcasecmp(rv->relname, target_rel) == 0 && plan_table_data_oid != InvalidOid) {
            if (list_length(relations) > 1) {
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                            errmsg("plan_table cannot be deleted in mutiple tables delete")));
            } else {
                return true;
            }
        }
    }
    return false;
}

/*
 * Description: make SessionIdExpr for WhereClause.
 * Parameters:
 * @in cur_location: current location.
 * @out lexpr: the left expr.
 * @out rexpr: the right expr.
 * @out op_loc: operation location.
 * Return: void
 */
static void makeSessionIdExpr(Node** lexpr, Node** rexpr, int* cur_location, int* op_loc)
{
    /* make filter condition for session_id */
    char* lcolname = "session_id";
    ColumnRef* c = makeNode(ColumnRef);
    c->fields = list_make1(makeString(lcolname));
    c->location = *cur_location;

    *cur_location = *cur_location + strlen("session_id=");
    *op_loc = *cur_location - 1;

    char* s_id = (char*)palloc0(SESSION_ID_LEN);
    getSessionID(s_id,
        IS_THREAD_POOL_WORKER ? u_sess->proc_cxt.MyProcPort->SessionStartTime : t_thrd.proc_cxt.MyStartTime,
        IS_THREAD_POOL_WORKER ? u_sess->session_id : t_thrd.proc_cxt.MyProcPid);
    A_Const* n = makeNode(A_Const);
    n->val.type = T_String;
    n->val.val.str = s_id;
    n->location = *cur_location;

    *cur_location = *cur_location + strlen(s_id) + 3;

    *lexpr = (Node*)c;
    *rexpr = (Node*)n;
}

/*
 * Description: make UserIdExpr for WhereClause.
 * Parameters:
 * @in cur_location: current location.
 * @out lexpr: the left expr.
 * @out rexpr: the right expr.
 * @out op_loc: operation location.
 * Return: void
 */
static void makeUserIdExpr(Node** lexpr, Node** rexpr, int* cur_location, int* op_loc)
{
    /* for filter condition user_id */
    char* lcolname = "user_id";
    ColumnRef* c = makeNode(ColumnRef);
    c->fields = list_make1(makeString(lcolname));
    c->location = *cur_location;

    *cur_location = *cur_location + strlen("user_id=");
    *op_loc = *cur_location - 1;

    Oid user_id = GetCurrentUserId();
    A_Const* n = makeNode(A_Const);
    n->val.type = T_Integer;
    n->val.val.ival = user_id;
    n->location = *cur_location;

    *lexpr = (Node*)c;
    *rexpr = (Node*)n;
}

/*
 * Description: make WhereClause node including SessionIdExpr and UserIdExpr for plan_table detele stmt.
 * Parameters:
 * @in cur_location: current location.
 * @out whereClause: WhereClause that we made.
 * Return: void
 */
static void makeNewWhereClause(Node** whereClause, int* cur_location)
{
    Node* lexpr = NULL;
    Node* rexpr = NULL;
    A_Expr* clause_1 = NULL;
    A_Expr* clause_2 = NULL;

    int op_loc = -1;
    int and_loc = -1;

    makeSessionIdExpr(&lexpr, &rexpr, cur_location, &op_loc);
    clause_1 = makeSimpleA_Expr(AEXPR_OP, "=", lexpr, rexpr, op_loc);

    and_loc = *cur_location;
    *cur_location = *cur_location + strlen("AND ");

    makeUserIdExpr(&lexpr, &rexpr, cur_location, &op_loc);
    clause_2 = makeSimpleA_Expr(AEXPR_OP, "=", lexpr, rexpr, op_loc);

    *whereClause = (Node*)makeA_Expr(AEXPR_AND, NIL, (Node*)clause_1, (Node*)clause_2, and_loc);
}

/*
 * Description: add WhereClause for plan_table detele stmt.
 * Parameters:
 * @in pstate: ParseState.
 * @int stmt: delete stmt that user input.
 * Return: DeleteStmt that plus new WhereClause.
 */
static DeleteStmt* addWhereClauseForPlanTable(ParseState* pstate, DeleteStmt* stmt)
{
    int current_location = -1;

    DeleteStmt* new_stmt = (DeleteStmt*)copyObject(stmt);
    ((RangeVar*)linitial(new_stmt->relations))->relname = T_PLAN_TABLE_DATA;

    current_location = strlen("delete from plan_table_data where ");

    /* make a new WhereClause include session_id and user_id filter condition. */
    if (new_stmt->whereClause == NULL) {
        makeNewWhereClause(&new_stmt->whereClause, &current_location);
    } else {
        /* add a WhereClause include session_id and user_id filter condition, behind original WhereClause. */
        makeNewWhereClause(&new_stmt->whereClause, &current_location);

        new_stmt->whereClause = (Node*)makeA_Expr(AEXPR_AND, NIL, new_stmt->whereClause, stmt->whereClause, -1);
    }

    return new_stmt;
}

const void SendCommandIdForInsertCte(Query* qry, WithClause* withclause)
{
    ListCell* tl = NULL;

    /*
     * For a WITH query that deletes from a parent table in the
     * main query & inserts a row in the child table in the WITH query
     * we need to use command ID communication to remote nodes in order
     * to maintain global data visibility.
     * For example
     * CREATE TEMP TABLE parent ( id int, val text ) DISTRIBUTE BY REPLICATION;
     * CREATE TEMP TABLE child ( ) INHERITS ( parent ) DISTRIBUTE BY REPLICATION;
     * INSERT INTO parent VALUES ( 42, 'old' );
     * INSERT INTO child VALUES ( 42, 'older' );
     * WITH wcte AS ( INSERT INTO child VALUES ( 42, 'new' ) RETURNING id AS newid )
     * DELETE FROM parent USING wcte WHERE id = newid;
     * The last query gets translated into the following multi-statement
     * transaction on the primary datanode
     * (a) SELECT id, ctid FROM ONLY parent WHERE true
     * (b) START TRANSACTION ISOLATION LEVEL read committed READ WRITE
     * (c) INSERT INTO child (id, val) VALUES ($1, $2) RETURNING id -- (42, 'new')
     * (d) DELETE FROM ONLY parent parent WHERE (parent.ctid = $1)
     * (e) SELECT id, ctid FROM ONLY child parent WHERE true
     * (f) DELETE FROM ONLY child parent WHERE (parent.ctid = $1)
     * (g) COMMIT TRANSACTION
     * The command id of the select in step (e), should be such that
     * it does not see the insert of step (c)
     */
    if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
        foreach (tl, withclause->ctes) {
            CommonTableExpr* cte = (CommonTableExpr*)lfirst(tl);
            if (IsA(cte->ctequery, InsertStmt)) {
                qry->has_to_save_cmd_id = true;
                SetSendCommandId(true);
                break;
            }
        }
    }
}

/*
 * CheckTsDelete - check if delete by timerange
 */
#ifdef ENABLE_MULTIPLE_NODES
/*
 * check delete by time range or not
 */
static bool check_del_timerange(const Node* qual)
{
    if (qual == NULL) {
        return false;
    }
    OpExpr* opexpr = (OpExpr*)qual;
    const int filter_param_num = 2;

    if ((nodeTag(qual) == T_OpExpr) && (list_length(opexpr->args) == filter_param_num) &&
        ((opexpr->opfuncid <= INTERVALINFUNCOID && opexpr->opfuncid >= TIMESTAMPTZOUTFUNCOID) ||
        (opexpr->opfuncid <= TIMESTAMPTZCMPTIMESTAMPFUNCOID &&
        opexpr->opfuncid >= TIMESTAMPLTTIMESTAMPTZFUNCOID) ||
        (opexpr->opfuncid <= TimeOprId::TIMESTAMP_OP_MAX &&
        opexpr->opfuncid >= TimeOprId::TIMESTAMP_OP_MIN) ||
        (opexpr->opfuncid <= TimeOprId::TIMESTAMP_DATE_OP_MAX &&
        opexpr->opfuncid >= TimeOprId::TIMESTAMP_DATE_OP_MIN))) {
        return true;
    } else if ((nodeTag(qual) == T_BoolExpr) && (((BoolExpr*)qual)->boolop == OR_EXPR)) {
        bool check_time_qual = true;
        ListCell* cell = NULL;
        /* if is or expr, we must confirm each sub-clause has time range in least 1 hour */
        for (cell = list_head(((BoolExpr*)qual)->args); cell != NULL && check_time_qual; cell = lnext(cell)) {
            check_time_qual = check_time_qual && check_del_timerange((Node*)lfirst(cell));
        }
        return check_time_qual;
    } else if ((nodeTag(qual) == T_BoolExpr) && (((BoolExpr*)qual)->boolop == AND_EXPR)) {
        bool check_time_qual = false;
        ListCell* cell = NULL;
        /* if is and expr, we can just have one sub-clause has time range in least 1 hour */
        for (cell = list_head(((BoolExpr*)qual)->args); cell != NULL; cell = lnext(cell)) {
            check_time_qual = check_time_qual || check_del_timerange((Node*)lfirst(cell));
        }
        return check_time_qual;
    }

    return false;
}

static void CheckTsDelete(const ParseState* pstate, const Query* qry)
{
    ListCell* l;
    if (pstate == NULL || qry == NULL) {
        ereport(ERROR, (errmodule(MOD_TIMESERIES), errmsg("Parsing null pointer to CheckTsDelete!")));
    }
    foreach (l, pstate->p_target_relation) {
        Relation targetrel = (Relation)lfirst(l);
        if (g_instance.attr.attr_common.enable_tsdb && RelationIsTsStore(targetrel)) {
            if (qry->jointree == NULL) {
                ereport(ERROR, (errmodule(MOD_TIMESERIES), errmsg("Timeseries only supports deletion by time range!")));
            } else {
                Node* quals = qry->jointree->quals;
                if (!check_del_timerange(quals)) {
                    ereport(ERROR, (errmodule(MOD_TIMESERIES),
                                    errmsg("Timeseries only supports deletion by time range!")));
                }
            }
        }
    }
    return;    
}
#endif   /* ENABLE_MULTIPLE_NODES */

static bool IsSupportDeleteLimit(Relation rel, bool hasLimit)
{
    RelationLocInfo* relLocInfo = NULL;

    if (rel == NULL) {
        return true;
    }
    relLocInfo = rel->rd_locator_info;
    if (relLocInfo == NULL) {
        return true;
    }
    if (IsRelationReplicated(relLocInfo) && hasLimit) {
        return false;
    }
    return true;
}

/*
 * transformUpdateSortClause -
 *	  transforms an update statement sort clause
 */
static List* transformUpdateSortClause(ParseState* pstate, UpdateStmt* stmt, Query* qry)
{
    ListCell* lc = NULL;
    TargetEntry* tle = NULL;

    if (stmt->sortClause == NULL) {
        return NULL;
    }

    int len = list_length(qry->targetList);
    foreach(lc, qry->targetList) {
        tle = (TargetEntry*)lfirst(lc);
        tle->resjunk = true;
    }

    List* rlst = transformSortClause(pstate, stmt->sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, true, false);
    int i = 0;
    foreach(lc, qry->targetList) {
        tle = (TargetEntry*)lfirst(lc);
        tle->resjunk = false;
        if (++i >= len) {
            break;
        }
    }
    return rlst;
}

/*
 * transformLimitSortClause -
 *	  transforms an delete|update statement limit and sort clause
 */
static void transformLimitSortClause(ParseState* pstate, void* stmt, Query* qry, bool forDel)
{
    List *rlist = NULL;
    qry->limitCount = forDel ? transformLimitClause(pstate, ((DeleteStmt*)stmt)->limitClause, EXPR_KIND_LIMIT, "LIMIT") :
                            transformLimitClause(pstate, ((UpdateStmt*)stmt)->limitClause, EXPR_KIND_LIMIT, "LIMIT");

    if (!IsSupportDeleteLimit((Relation)linitial(pstate->p_target_relation), (qry->limitCount != NULL))) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                forDel ? errdetail("replication table doesn't allow DELETE LIMIT") :
                errdetail("replication table doesn't allow UPDATE LIMIT")));
    }

    rlist =  forDel ? transformSortClause(pstate, ((DeleteStmt*)stmt)->sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, true, false) :
                transformUpdateSortClause(pstate, (UpdateStmt*)stmt, qry);

    /*
     * For update statement, the effective range of sort clause is not optimized. For delete statement,
     * sorting is performed only when limit or returning clause takes effect.
     */
    if (qry->limitCount != NULL) {
        /* flag for discriminating rownum */
        Const *flag = makeNode(Const);
        flag->ismaxvalue = true;
        flag->location = -1;
        flag->consttype = INT8OID;
        flag->consttypmod = -1;
        qry->limitOffset = (Node*)flag;
        qry->sortClause = rlist;
    } else if (!forDel || qry->returningList != NULL) {
        qry->sortClause = rlist;
    }
}

static void CheckRelationSupportMultiModify(Relation targetrel, bool forDel)
{
    const char* type = NULL;
    if (forDel) {
        CheckDeleteRelation(targetrel);
        type = "DELETE";
    } else {
        CheckUpdateRelation(targetrel);
        type = "UPDATE";
    }

    if (RelationIsColStore(targetrel)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("column stored relation in mutilple-relations modifying doesn't support %s", type)));
    }
    if (RelationIsForeignTable(targetrel) || RelationIsStream(targetrel)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("FDW relation in mutilple-relations modifying doesn't support %s", type)));
    }
    if (targetrel->rd_rel->relhasrules) {
        if (RelationIsView(targetrel) && (targetrel->rd_rules->numLocks > 1 ||
            view_has_instead_trigger(targetrel, forDel ? CMD_DELETE : CMD_UPDATE))) {
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("view has rules or tiggers in mutilple-relations modifying doesn't support %s",
                                type)));
        } else if (!RelationIsView(targetrel)) {
                ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("relation has rules in mutilple-relations modifying doesn't support %s", type)));
        } else {
            /* recursive check for view */
            Query* viewquery = get_view_query(targetrel);
            RangeTblRef* rtr = NULL;
            RangeTblEntry* base_rte = NULL;
            Relation base_rel;

            /* Ignore here, and it would throw an error during query rewrite. */
            if (list_length(viewquery->jointree->fromlist) != 1) {
                return;
            }

            rtr = (RangeTblRef*)linitial(viewquery->jointree->fromlist);

            /* Ignore here, it would throw an error during query rewrite. */
            if (!IsA(rtr, RangeTblRef)) {
                return;
            }

            base_rte = rt_fetch(rtr->rtindex, viewquery->rtable);
            base_rel = try_relation_open(base_rte->relid, AccessShareLock);

            if (base_rel != NULL) {
                CheckRelationSupportMultiModify(base_rel, forDel);

                relation_close(base_rel, AccessShareLock);
            }
        }
    }
}

static void CheckUDRelations(ParseState* pstate, List* sortClause, Node* limitClause, List* returningList,
                             bool forDel)
{
    if (list_length(pstate->p_target_relation) == 1) {
        if (forDel) {
            CheckDeleteRelation((Relation)linitial(pstate->p_target_relation));
        } else {
            CheckUpdateRelation((Relation)linitial(pstate->p_target_relation));
        }
        return;
    }

    const char* type = forDel ? "DELETE" : "UPDATE";
    if (sortClause != NIL) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("mutilple-relations modifying doesn't support %s ORDER BY", type)));
    }
    if (limitClause != NULL) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("mutilple-relations modifying doesn't support %s LIMIT", type)));
    }
    if (returningList != NIL) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("mutilple-relations modifying doesn't support %s returning", type)));
    }

    foreach_cell (l, pstate->p_target_relation) {
        Relation targetrel = (Relation)lfirst(l);
        CheckRelationSupportMultiModify(targetrel, forDel);
    }
}

static void CheckDeleteRelation(Relation targetrel)
{
    // check hdfs relation distributed by replication
    if (targetrel->rd_locator_info &&
        IsLocatorReplicated(targetrel->rd_locator_info->locatorType) &&
        RelationIsPAXFormat(targetrel)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("hdfs replication table doesn't allow DELETE")));
    }

#ifdef ENABLE_MULTIPLE_NODES
    if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
#endif
        if (RelationIsMatview(targetrel)) {
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Unsupported feature"),
                errdetail("Materialized view doesn't allow DELETE")));
        }
#ifdef ENABLE_MULTIPLE_NODES
    }
#endif

    /* check delete permission */
    if (((unsigned int)RelationGetInternalMask(targetrel) & INTERNAL_MASK_DDELETE)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("internal relation doesn't allow DELETE")));
    }

    // @Online expansion: check if the target relation is being redistributed in read only mode
    if (!u_sess->attr.attr_sql.enable_cluster_resize && targetrel &&
        RelationInClusterResizingWriteErrorMode(targetrel)) {
        ereport(ERROR,
            (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
                errmsg("%s is redistributing, please retry later.", targetrel->rd_rel->relname.data)));
    }
}
/*
 * transformDeleteStmt -
 *	  transforms a Delete Statement
 */
static Query* transformDeleteStmt(ParseState* pstate, DeleteStmt* stmt)
{
    Query* qry = makeNode(Query);
    ParseNamespaceItem *nsitem = NULL;
    Node* qual = NULL;
    /*
     * Check the delete object name whether is 'plan_table'
     * If it is, we will make a new where clause that include session_id and user_id filter condition.
     */
    if (checkDeleteStmtForPlanTable(stmt->relations)) {
        stmt = addWhereClauseForPlanTable(pstate, stmt);
        OnlyDeleteFromPlanTable = true;
    }

    qry->commandType = CMD_DELETE;
    /* set io state for backend status for the thread, we will use it to check user space */
    pgstat_set_io_state(IOSTATE_READ);
    /* process the WITH clause independently of all else */
    if (stmt->withClause) {
#ifdef PGXC
        SendCommandIdForInsertCte(qry, stmt->withClause);
#endif
        qry->hasRecursive = stmt->withClause->recursive;
        qry->cteList = transformWithClause(pstate, stmt->withClause);
        qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
    }

    /*
     * for sql_compatibility B, using clause may have the same relation name with result relations, so
     * put transformFromClause here in advance to avoid checkNameSpaceConflicts with result relations.
     */
    if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) {
        transformFromClause(pstate, stmt->usingClause);
    }
    /* set up range table with just the result rel */
    qry->resultRelations =
        setTargetTables(pstate, stmt->relations, true, true, ACL_DELETE);

    CheckUDRelations(pstate, stmt->sortClause, stmt->limitClause, stmt->returningList, true);
    /* there's no DISTINCT in DELETE */
    qry->distinctClause = NIL;
    /*
     * The USING clause is non-standard SQL syntax, and is equivalent in
     * functionality to the FROM list that can be specified for UPDATE. The
     * USING keyword is used rather than FROM because FROM is already a
     * keyword in the DELETE syntax.
     */
    if (u_sess->attr.attr_sql.sql_compatibility != B_FORMAT) {
        /* grab the namespace item made by setTargetTable */
        nsitem = (ParseNamespaceItem *)llast(pstate->p_relnamespace);
        /* subqueries in USING cannot access the result relation */
        nsitem->p_lateral_only = true;
        nsitem->p_lateral_ok = false;

        transformFromClause(pstate, stmt->usingClause);
        /* remaining clauses can reference the result relation normally */
        nsitem->p_lateral_only = false;
        nsitem->p_lateral_ok = true;
    }



    qual = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE");

    Relation targetrel = (Relation)linitial(pstate->p_target_relation);
    qry->returningList = transformReturningList(pstate, stmt->returningList);
    if (pstate->p_target_relation && qry->returningList != NIL && RelationIsColStore(targetrel)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("column stored relation doesn't support DELETE returning")));
    } else if (pstate->p_target_relation && qry->returningList != NIL && RelationIsTsStore(targetrel)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("Timeseries stored relation doesn't support DELETE returning")));
    }

    if (!checkAllowedTableCombination(pstate)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-supported feature"),
                errdetail("UStore relations cannot be used with other storage types in DELETE statement")));
    }

    /* done building the range table and jointree */
    qry->rtable = pstate->p_rtable;
    qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
    transformLimitSortClause(pstate, stmt, qry, true);

    qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
    qry->hasSubLinks = pstate->p_hasSubLinks;
    qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
    if (pstate->p_hasWindowFuncs)
        parseCheckWindowFuncs(pstate, qry);
    qry->hasAggs = pstate->p_hasAggs;

    assign_query_collations(pstate, qry);

    /* this must be done after collations, for reliable comparison of exprs */
    if (pstate->p_hasAggs)
        parseCheckAggregates(pstate, qry);

    qry->hintState = stmt->hintState;

#ifdef ENABLE_MULTIPLE_NODES
    CheckTsDelete(pstate, qry);
#endif   /* ENABLE_MULTIPLE_NODES */

    return qry;
}

/* @hdfs
 * brief: Check whether there is a openGauss forein table in qry. Return true means
 *        openGauss foreign table existed, false not existed
 * input param @qry: the Query for foreign table
 */
static bool IsFindPgfdwForeignTbl(Query* qry)
{
    List* rtable = qry->rtable;
    ListCell* lc = NULL;

    foreach (lc, rtable) {
        RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);

        if (rte->relkind == RELKIND_FOREIGN_TABLE || rte->relkind == RELKIND_STREAM) {
            if (IS_POSTGRESFDW_FOREIGN_TABLE(rte->relid)) {
                return true;
            }
        }
    }

    return false;
}

/* @hdfs
 * brief: Check whether there is a HDFS forein table in qry. Return true means
 *        HDFS foreign table existed, false not existed
 * input param @qry: the Query for foreign table
 */
static bool IsFindHDFSForeignTbl(Query* qry)
{
    List* rtable = qry->rtable;
    ListCell* lc = NULL;

    foreach (lc, rtable) {
        RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);
        if (rte->relkind == RELKIND_FOREIGN_TABLE || rte->relkind == RELKIND_STREAM) {
            if (isObsOrHdfsTableFormTblOid(rte->relid) || IS_OBS_CSV_TXT_FOREIGN_TABLE(rte->relid)) {
                return true;
            }
        }
    }

    return false;
}

static Query* FindQueryContainForeignTbl(Query* qry)
{
    List* rtable = qry->rtable;
    ListCell* lc = NULL;

    foreach (lc, rtable) {
        RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);

        if (rte->relkind == RELKIND_FOREIGN_TABLE || rte->relkind == RELKIND_STREAM) {
            return qry;
        } else if (rte->rtekind == RTE_SUBQUERY) {
            return FindQueryContainForeignTbl(rte->subquery);
        }
    }

    ListCell* target = NULL;
    foreach (target, qry->targetList) {
        TargetEntry* entry = (TargetEntry*)lfirst(target);
        if (IsA(entry->expr, SubLink) && IsA(((SubLink*)entry->expr)->subselect, Query)) {
            Query* q = FindQueryContainForeignTbl((Query*)((SubLink*)entry->expr)->subselect);
            if (q != NULL)
                return q;
        }
    }
    return NULL;
}

#ifdef ENABLE_MOT
/*
 * @brief: Expression_tree_walker callback function.
 * Checks the entire RTE used by a query to identify their storage engine type (MOT or PAGE).
 */
static bool StorageEngineUsedWalker(Node* node, RTEDetectorContext* context)
{
    if (node == NULL) {
        return false;
    }

    if (IsA(node, RangeTblRef)) {
        RangeTblRef* rtr = (RangeTblRef*)node;
        Query* qry = (Query*)llast(context->queryNodes);
        RangeTblEntry* rte = rt_fetch(rtr->rtindex, qry->rtable);
        if (rte->rtekind == RTE_RELATION) {
            if (rte->relkind == RELKIND_FOREIGN_TABLE && isMOTFromTblOid(rte->relid)) {
                context->isMotTable = true;
            } else if (!is_sys_table(rte->relid)) {
                context->isPageTable = true;
            }
        }
        return false;
    }

    if (IsA(node, Query)) {
        /* Recurse into subselects */
        bool result = false;
        context->queryNodes = lappend(context->queryNodes, (Query*)node);
        context->sublevelsUp++;
        result = query_tree_walker((Query*)node, (bool (*)())StorageEngineUsedWalker, (void*)context, 0);
        context->sublevelsUp--;
        context->queryNodes = list_delete(context->queryNodes, llast(context->queryNodes));
        return result;
    }
    return expression_tree_walker(node, (bool (*)())StorageEngineUsedWalker, (void*)context);
}

/*
 * @brief: Analyze a query to check which storage engines are being used,
 * PG, MOT, mixed PG & MOT or Other type.
 * @input: Query to be analyzed.
 * @output: Type of storage engine.
 */
void CheckTablesStorageEngine(Query* qry, StorageEngineType* type)
{
    RTEDetectorContext context;
    context.queryNodes = NIL;
    context.sublevelsUp = 0;
    context.isMotTable = false;
    context.isPageTable = false;

    *type = SE_TYPE_UNSPECIFIED;

    /* check root node RTEs in case of non RangeTblRef nodes */
    List* rtable = qry->rtable;
    ListCell* lc = NULL;
    foreach (lc, rtable) {
        RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);
        if (rte->rtekind == RTE_RELATION) {
            if (rte->relkind == RELKIND_FOREIGN_TABLE && isMOTFromTblOid(rte->relid)) {
                context.isMotTable = true;
            } else if (!is_sys_table(rte->relid)) {
                context.isPageTable = true;
            }
        }
    }

    /* MOT JIT: check if this is an invoke MOT JITted SP */
    if (JitExec::IsInvokeReadyFunction(qry)) {
        context.isMotTable = true;
    }

    /* add root query to query stack list */
    context.queryNodes = lappend(context.queryNodes, qry);

    /* recursive walk on the query */
    (void)query_or_expression_tree_walker((Node*)qry, (bool (*)())StorageEngineUsedWalker, (void*)&context, 0);

    if (context.isMotTable && context.isPageTable) {
        *type = SE_TYPE_MIXED;
    } else if (context.isPageTable) {
        *type = SE_TYPE_PAGE_BASED;
    } else if (context.isMotTable) {
        *type = SE_TYPE_MOT;
    }
}
#endif

static void CheckUnsupportInsertSelectClause(Query* query)
{
    RangeTblEntry* result = NULL;
    Query* subquery = NULL;

    if (query == NULL)
        return;
    result = rt_fetch(linitial_int(query->resultRelations), query->rtable);

    AssertEreport(query->commandType == CMD_INSERT, MOD_OPT, "Only deal with CMD_INSERT commondType here");
    if (result->relkind == RELKIND_FOREIGN_TABLE || result->relkind == RELKIND_STREAM) {
        if (CheckSupportedFDWType(result->relid)) {
            return;
        }
#ifdef ENABLE_MULTIPLE_NODES
         if (relid_is_stream(result->relid)) {
            if (t_thrd.streaming_cxt.loaded) {
                return;
            } else {
                ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                        errdetail("Before insert stream object, please switch on the stream engine")));
            }
        }
#endif
        if (list_length(query->rtable) == 1)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("insert statement is an INSERT INTO VALUES(...)")));

        if (list_length(query->rtable) > 2)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("too complex subquery")));

        RangeTblEntry* subrte = rt_fetch(linitial_int(query->resultRelations) == 1 ? 2 : 1, query->rtable);
        /* non-subquery is unsupported */
        if (subrte->rtekind != RTE_SUBQUERY)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("only subquery is supported")));

        subquery = subrte->subquery;
        if (list_length(subquery->rtable) != 1)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("subquery can not contain more than one relation")));

        if (rt_fetch(1, subquery->rtable)->relkind != RELKIND_RELATION)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("range table of subquery should be a normal relation")));

        /* complex simple table query is not supported */
        if (subquery->distinctClause)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("subquery contains DISTINCT clause")));

        if (subquery->hasAggs || subquery->groupClause)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("subquery contains aggregation")));

        if (subquery->hasWindowFuncs)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("subquery contains window function")));

        if (subquery->hasSubLinks)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("subquery contains other subqueries")));

        if (subquery->limitCount || subquery->limitOffset)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("subquery contains LIMIT clause")));

        if (subquery->sortClause)
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("subquery contains SORT clause")));
    } else if (list_length(query->rtable) == 1) {
        return;
    } else if ((subquery = FindQueryContainForeignTbl(query)) != NULL) {
        if (!IsFindHDFSForeignTbl(subquery) && !IsFindPgfdwForeignTbl(subquery)) {
            if (list_length(query->rtable) > 2) {
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("Un-support feature"),
                        errdetail("too complex statement to foreign table")));
            }

            // check if SELECT clause contains JOIN foreign table
            if (list_length(subquery->rtable) > 1) {
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("Un-support feature"),
                        errdetail("JOIN contains foreign table")));
            }

            RangeTblEntry* rte = rt_fetch(linitial_int(query->resultRelations) == 1 ? 2 : 1, query->rtable);
            AssertEreport(rte->rtekind == RTE_SUBQUERY, MOD_OPT, "Only deal with subquery in FROM clause here");

            if (rte->subquery != subquery) {
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("Un-support feature"),
                        errdetail("too complex statement to foreign table")));
            }

            // Unsupport LIMIT clause
            if (subquery->limitCount != NULL || subquery->limitOffset != NULL) {
                ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("Un-support feature"),
                        errdetail("subquery contains LIMIT clause")));
            }
        }
    }
}


static void SetInsertAttrnoState(ParseState* pstate, List* attrnos, int exprLen) 
{
    RightRefState* rstate = pstate->rightRefState;
    Relation relation = (Relation)linitial(pstate->p_target_relation);
    rstate->colCnt = RelationGetNumberOfAttributes(relation);
    rstate->explicitAttrLen = exprLen;
    rstate->explicitAttrNos = (int*)palloc0(sizeof(int) * exprLen);
    
    ListCell* attr = list_head(attrnos);
    for (int i = 0; i < exprLen; ++i) {
        rstate->explicitAttrNos[i] = lfirst_int(attr);
        attr = lnext(attr);
    }
}

static void SetUpsertAttrnoState(ParseState* pstate, List *targetList) 
{
    if (!targetList) {
        return;
    }
    RightRefState* rstate = pstate->rightRefState;
    const int len = list_length(targetList);
    rstate->usExplicitAttrLen = len;
    rstate->usExplicitAttrNos = (int*)palloc0(sizeof(int) * len);

    Relation relation = (Relation)linitial(pstate->p_target_relation);
    FormData_pg_attribute* attr = relation->rd_att->attrs;
    const int colNum = RelationGetNumberOfAttributes(relation);
    ListCell* target = list_head(targetList);
    for (int ni = 0; ni < len; ++ni) {
        ResTarget* res = (ResTarget*)lfirst(target);
        char* name = nullptr;
        if (list_length(res->indirection) > 0 && IsA(linitial(res->indirection), String)) {
            name = strVal(linitial(res->indirection));
        } else {
            name = res->name;
        }

        if (name != NULL) {
            for (int ci = 0; ci < colNum; ++ci) {
                if (attr[ci].attisdropped) {
                    continue;
                }
                if (strcmp(name, attr[ci].attname.data) == 0) {
                    rstate->usExplicitAttrNos[ni] = ci + 1;
                    break;
                }
            }
        }

        if (rstate->usExplicitAttrNos[ni] == InvalidOid) {
            ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_COLUMN),
                 errmsg("Column \"%s\" not found in upsert attrno state initialization", name)));
        }
        target = lnext(target);
    }
}

static bool isSubExprSupportRightRef(Node* node)
{
    if (!node) {
        return true;
    }
    if (IsA(node, A_Expr)) {
        A_Expr* expr = (A_Expr*)node;
        return isSubExprSupportRightRef(expr->lexpr) &&
               isSubExprSupportRightRef(expr->rexpr);
    } else if (IsA(node, SubLink)) {
        SubLink* sl = (SubLink*)node;
        if (sl->subselect) {
            SelectStmt* subsel = (SelectStmt*)sl->subselect;
            return (!subsel->whereClause || subsel->fromClause);
        }
    }
    return true;
}

static RightRefState* MakeRightRefStateIfSupported(SelectStmt* selectStmt)
{
    bool isSupported = DB_IS_CMPT(B_FORMAT) && selectStmt && selectStmt->valuesLists && !IsInitdb;
    if (!isSupported) {
        return nullptr;
    }
    ListCell* lc = NULL;
    ListCell* lc2 = NULL;
    foreach (lc, selectStmt->valuesLists) {
        List* sublist = (List*)lfirst(lc);
        foreach(lc2, sublist) {
            Node* col  = (Node*)lfirst(lc2);
            if (!isSubExprSupportRightRef(col)) {
                return nullptr;
            }
        }
    }
    RightRefState* refState = (RightRefState*)palloc0(sizeof(RightRefState));
    refState->isSupported = true;
    return refState;
}

/*
 * transformInsertStmt -
 *	  transform an Insert Statement
 */
static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt)
{
    Query* qry = makeNode(Query);
    SelectStmt* selectStmt = (SelectStmt*)stmt->selectStmt;
    List* exprList = NIL;
    bool isGeneralSelect = false;
    List* sub_rtable = NIL;
    List* sub_relnamespace = NIL;
    List* sub_varnamespace = NIL;
    List* icolumns = NIL;
    List* attrnos = NIL;
    RangeTblEntry* rte = NULL;
    RangeTblRef* rtr = NULL;
    ListCell* icols = NULL;
    ListCell* attnos = NULL;
    ListCell* lc = NULL;
    AclMode    targetPerms = ACL_INSERT;
    Relation targetrel = NULL;

    /* There can't be any outer WITH to worry about */
    AssertEreport(pstate->p_ctenamespace == NIL, MOD_OPT, "para should be NIL");

    RightRefState* rightRefState = MakeRightRefStateIfSupported((SelectStmt*)stmt->selectStmt);
    
    qry->commandType = CMD_INSERT;
    pstate->p_is_insert = true;
    pstate->p_has_ignore = stmt->hasIgnore;
    pstate->rightRefState = rightRefState;
    
    /* set io state for backend status for the thread, we will use it to check user space */
    pgstat_set_io_state(IOSTATE_WRITE);

    qry->isReplace = stmt->isReplace;
    if (stmt->isReplace) {
        targetPerms |= ACL_DELETE;
        /* transform set clause in REPLACE INTO stmt */
        if (selectStmt == NULL && stmt->cols == NULL && stmt->targetList != NULL) {
            stmt->selectStmt = (Node *)makeNode(SelectStmt);
            selectStmt = (SelectStmt *)stmt->selectStmt;
            ListCell* o_target = NULL;
            List *ctext_expr_list = NULL;
            
            foreach (o_target, stmt->targetList) {
                ResTarget* res = (ResTarget*)lfirst(o_target);

                ctext_expr_list = lappend(ctext_expr_list, res->val);
            }
            selectStmt->valuesLists = list_make1(ctext_expr_list);

            stmt->cols = (List *)copyObject(stmt->targetList);
            foreach (o_target, stmt->cols) {
                ResTarget* res = (ResTarget*)lfirst(o_target);
                res->val = NULL;
            }

        }
    }

    /* process the WITH clause independently of all else */
    if (stmt->withClause) {
        qry->hasRecursive = stmt->withClause->recursive;
        qry->cteList = transformWithClause(pstate, stmt->withClause);
        qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
    }

    /*
     * We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL),
     * VALUES list, or general SELECT input.  We special-case VALUES, both for
     * efficiency and so we can handle DEFAULT specifications.
     *
     * The grammar allows attaching ORDER BY, LIMIT, FOR UPDATE, or WITH to a
     * VALUES clause.  If we have any of those, treat it as a general SELECT;
     * so it will work, but you can't use DEFAULT items together with those.
     */
    isGeneralSelect = (selectStmt && (selectStmt->valuesLists == NIL || selectStmt->sortClause != NIL ||
                                         selectStmt->limitOffset != NULL || selectStmt->limitCount != NULL ||
                                         selectStmt->lockingClause != NIL || selectStmt->withClause != NULL));

    /*
     * If a non-nil rangetable/namespace was passed in, and we are doing
     * INSERT/SELECT, arrange to pass the rangetable/namespace down to the
     * SELECT.	This can only happen if we are inside a CREATE RULE, and in
     * that case we want the rule's OLD and NEW rtable entries to appear as
     * part of the SELECT's rtable, not as outer references for it.  (Kluge!)
     * The SELECT's joinlist is not affected however.  We must do this before
     * adding the target table to the INSERT's rtable.
     */
    if (isGeneralSelect) {
        sub_rtable = pstate->p_rtable;
        pstate->p_rtable = NIL;
        sub_relnamespace = pstate->p_relnamespace;
        pstate->p_relnamespace = NIL;
        sub_varnamespace = pstate->p_varnamespace;
        pstate->p_varnamespace = NIL;
        if (stmt->returningList || stmt->upsertClause)
            qry->is_dist_insertselect = false;
        else
            qry->is_dist_insertselect = stmt->is_dist_insertselect;
    } else {
        sub_rtable = NIL; /* not used, but keep compiler quiet */
        sub_relnamespace = NIL;
        sub_varnamespace = NIL;
        qry->is_dist_insertselect = false;
    }

    /*
     * Must get write lock on INSERT target table before scanning SELECT, else
     * we will grab the wrong kind of initial lock if the target table is also
     * mentioned in the SELECT part.  Note that the target table is not added
     * to the joinlist or namespace.
     */
    if (stmt->upsertClause != NULL && stmt->upsertClause->targetList != NIL) {
        targetPerms |= ACL_UPDATE;
    }

    qry->resultRelations = setTargetTables(pstate, list_make1(stmt->relation), false, false, targetPerms);
    targetrel = (Relation)linitial(pstate->p_target_relation);

    CheckInsertTargetRelation(pstate, stmt, targetrel, true);

    if (qry->is_dist_insertselect) {
        Oid relid = RelationGetRelid(targetrel);
        HeapTuple classtup = SearchSysCache1(RELOID, relid);
        Form_pg_class class_struct = (Form_pg_class)GETSTRUCT(classtup);
        if (class_struct->parttype == PARTTYPE_PARTITIONED_RELATION ||
            class_struct->parttype == PARTTYPE_SUBPARTITIONED_RELATION ||
            class_struct->parttype == PARTTYPE_VALUE_PARTITIONED_RELATION ) {
            qry->is_dist_insertselect = false;
        }
        ReleaseSysCache(classtup);
    }

    /* Validate stmt->cols list, or build default list if no list given */
    icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
    
    AssertEreport(list_length(icolumns) == list_length(attrnos), MOD_OPT, "list length inconsistent");

    /*
     * Determine which variant of INSERT we have.
     */
    if (selectStmt == NULL) {
        /*
         * We have INSERT ... DEFAULT VALUES.  We can handle this case by
         * emitting an empty targetlist --- all columns will be defaulted when
         * the planner expands the targetlist.
         */
        exprList = NIL;
    } else if (isGeneralSelect) {
        /*
         * We make the sub-pstate a child of the outer pstate so that it can
         * see any Param definitions supplied from above.  Since the outer
         * pstate's rtable and namespace are presently empty, there are no
         * side-effects of exposing names the sub-SELECT shouldn't be able to
         * see.
         */
        ParseState* sub_pstate = make_parsestate(pstate);
        Query* selectQuery = NULL;
#ifdef PGXC
        RangeTblEntry* target_rte = NULL;
#endif

        /*
         * Process the source SELECT.
         *
         * It is important that this be handled just like a standalone SELECT;
         * otherwise the behavior of SELECT within INSERT might be different
         * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had
         * bugs of just that nature...)
         */
        sub_pstate->p_rtable = sub_rtable;
        sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */
        sub_pstate->p_relnamespace = sub_relnamespace;
        sub_pstate->p_varnamespace = sub_varnamespace;
        /* Set sub_query is in insert */
        sub_pstate->p_is_in_insert = true;
        sub_pstate->p_resolve_unknowns = false;

        selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
        Assert(selectQuery != NULL);

        free_parsestate(sub_pstate);

        /* The grammar should have produced a SELECT */
        if (!IsA(selectQuery, Query) || selectQuery->commandType != CMD_SELECT || selectQuery->utilityStmt != NULL) {
            ereport(
                ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("unexpected non-SELECT command in INSERT ... SELECT")));
        }

        /*
         * Make the source be a subquery in the INSERT's rangetable, and add
         * it to the INSERT's joinlist.
         */
        rte = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias("*SELECT*", NIL), false, false);
#ifdef PGXC
        /*
         * For an INSERT SELECT involving INSERT on a child after scanning
         * the parent, set flag to send command ID communication to remote
         * nodes in order to maintain global data visibility.
         */
        if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
            target_rte = rt_fetch(linitial_int(qry->resultRelations), pstate->p_rtable);
            if (is_relation_child(target_rte, selectQuery->rtable)) {
                qry->has_to_save_cmd_id = true;
                SetSendCommandId(true);
            }
        }
#endif
        rtr = makeNode(RangeTblRef);
        /* assume new rte is at end */
        rtr->rtindex = list_length(pstate->p_rtable);
        AssertEreport(
            rte == rt_fetch(rtr->rtindex, pstate->p_rtable), MOD_OPT, "check inconsistant with rt_fetch functon");
        pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);

        /* ----------
         * Generate an expression list for the INSERT that selects all the
         * non-resjunk columns from the subquery.  (INSERT's tlist must be
         * separate from the subquery's tlist because we may add columns,
         * insert datatype coercions, etc.)
         *
         * HACK: unknown-type constants and params in the SELECT's targetlist
         * are copied up as-is rather than being referenced as subquery
         * outputs.  This is to ensure that when we try to coerce them to
         * the target column's datatype, the right things happen (see
         * special cases in coerce_type).  Otherwise, this fails:
         *		INSERT INTO foo SELECT 'bar', ... FROM baz
         * ----------
         */
        int subpos = 0;
        int respos = 0;
        Bitmapset* subset = NULL;
        Bitmapset* resset = NULL;

        exprList = NIL;
        foreach (lc, selectQuery->targetList) {
            TargetEntry* tle = (TargetEntry*)lfirst(lc);
            Expr* expr = NULL;

            subpos++;
            if (tle->resjunk) {
                continue;
            }

            respos++;
            if (tle->expr && (IsA(tle->expr, Const) || IsA(tle->expr, Param)) &&
                exprType((Node*)tle->expr) == UNKNOWNOID) {
                subset = bms_add_member(subset, subpos);
                resset = bms_add_member(resset, respos);
                expr = tle->expr;
            } else {
                Var* var = makeVarFromTargetEntry(rtr->rtindex, tle);

                var->location = exprLocation((Node*)tle->expr);
                expr = (Expr*)var;
            }
            exprList = lappend(exprList, expr);
        }

        /*
         * If td_compatible_truncation equal true and no foreign table found,
         * the auto truncation funciton should be enabled.
         */
        if (u_sess->attr.attr_sql.sql_compatibility == C_FORMAT && targetrel != NULL &&
            !RelationIsForeignTable(targetrel) &&
            !RelationIsStream(targetrel)) {
            if (u_sess->attr.attr_sql.td_compatible_truncation) {
                pstate->p_is_td_compatible_truncation = true;
            } else {
                pstate->tdTruncCastStatus = NOT_CAST_BECAUSEOF_GUC;
            }
        }

        /* Prepare row for assignment to target table */
        exprList = transformInsertRow(pstate, exprList, stmt->cols, icolumns, attrnos);

        if (!bms_is_empty(subset)) {
            /*
             * Try to coerce unknown-type constants and params to the target
             * column's datatype.
             */
            Assert(!bms_is_empty(resset));

            while ((subpos = bms_first_member(subset)) > 0) {
                TargetEntry* tle = (TargetEntry*)list_nth(selectQuery->targetList, subpos - 1);

                respos = bms_first_member(resset);
                Assert(respos > 0);

                ListCell* rescell = list_nth_cell(exprList, respos - 1);
                tle->expr = (Expr*)copyObject(lfirst(rescell));

                /*
                 * After coercion, we need to transform those constants from
                 * the HACKY representation to a normal Var node like others.
                 *
                 * Old exprs in exprList are replaced by the newly
                 * generated Vars and then freed on each iteration.
                 */
                if ((IsA(tle->expr, Const) || IsA(tle->expr, Param)) &&
                    exprType((Node*)tle->expr) != UNKNOWNOID) {
                    lfirst(rescell) = (void*)makeVarFromTargetEntry(rtr->rtindex, tle);
                }
            }
        }
    } else if (list_length(selectStmt->valuesLists) > 1) {
        /*
         * Process INSERT ... VALUES with multiple VALUES sublists. We
         * generate a VALUES RTE holding the transformed expression lists, and
         * build up a targetlist containing Vars that reference the VALUES
         * RTE.
         */
        List* exprsLists = NIL;
        List* collations = NIL;
        int sublist_length = -1;
        int i;
        List* temp_list = NIL;

        AssertEreport(selectStmt->intoClause == NULL, MOD_OPT, "into Clause should not happen here");
        /*
         * Here, because the auto truncation feature need to be blocked,
         * once we found there is a foreign table involve. so here we change
         * orignal one foreach loop to two foreach loops.
         * first loop only transformExpressionList for all value lists and
         * save temp result to temp_list. Here we also generate the result whether
         * auto truncation is enable or not.
         * Seconde loop will continues process the temp_list that first loop generated.
         */
        foreach (lc, selectStmt->valuesLists) {
            List* sublist = (List*)lfirst(lc);

            /* Do basic expression transformation (same as a ROW() expr) */
            sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES);

            /*
             * All the sublists must be the same length, *after*
             * transformation (which might expand '*' into multiple items).
             * The VALUES RTE can't handle anything different.
             */
            if (sublist_length < 0) {
                /* Remember post-transformation length of first sublist */
                sublist_length = list_length(sublist);
            } else if (sublist_length != list_length(sublist)) {
                ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                        errmsg("VALUES lists must all be the same length"),
                        parser_errposition(pstate, exprLocation((Node*)sublist))));
            }

            /*
             * If td_compatible_truncation equal true and no foreign table found,
             * the auto truncation funciton should be enabled.
             */
            if (u_sess->attr.attr_sql.sql_compatibility == C_FORMAT && targetrel != NULL &&
                !RelationIsForeignTable(targetrel) &&
                !RelationIsStream(targetrel)) {
                if (u_sess->attr.attr_sql.td_compatible_truncation) {
                    pstate->p_is_td_compatible_truncation = true;
                } else {
                    pstate->tdTruncCastStatus = NOT_CAST_BECAUSEOF_GUC;
                }
            }

            temp_list = lappend(temp_list, sublist);
        }

        foreach (lc, temp_list) {
            List* sublist = (List*)lfirst(lc);

            /* Prepare row for assignment to target table */
            sublist = transformInsertRow(pstate, sublist, stmt->cols, icolumns, attrnos);

            /*
             * We must assign collations now because assign_query_collations
             * doesn't process rangetable entries.  We just assign all the
             * collations independently in each row, and don't worry about
             * whether they are consistent vertically.	The outer INSERT query
             * isn't going to care about the collations of the VALUES columns,
             * so it's not worth the effort to identify a common collation for
             * each one here.  (But note this does have one user-visible
             * consequence: INSERT ... VALUES won't complain about conflicting
             * explicit COLLATEs in a column, whereas the same VALUES
             * construct in another context would complain.)
             */
            assign_list_collations(pstate, sublist);

            exprsLists = lappend(exprsLists, sublist);
        }

        /*
         * Although we don't really need collation info, let's just make sure
         * we provide a correctly-sized list in the VALUES RTE.
         */
        for (i = 0; i < sublist_length; i++) {
            collations = lappend_oid(collations, InvalidOid);
        }

        if (list_length(pstate->p_rtable) != 1 && contain_vars_of_level((Node*)exprsLists, 0)) {
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("VALUES must not contain OLD or NEW references"),
                    errhint("Use SELECT ... UNION ALL ... instead."),
                    parser_errposition(pstate, locate_var_of_level((Node*)exprsLists, 0))));
        }

        /*
         * Generate the VALUES RTE
         */
        rte = addRangeTableEntryForValues(pstate, exprsLists, collations, NULL, true);
        rtr = makeNode(RangeTblRef);
        /* assume new rte is at end */
        rtr->rtindex = list_length(pstate->p_rtable);
        AssertEreport(
            rte == rt_fetch(rtr->rtindex, pstate->p_rtable), MOD_OPT, "check inconsistant with rt_fetch functon");
        pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);

        /*
         * Generate list of Vars referencing the RTE
         */
        expandRTE(rte, rtr->rtindex, 0, -1, false, NULL, &exprList);
    } else {
        /* ----------
         * Process INSERT ... VALUES with a single VALUES sublist.
         * We treat this separately for efficiency and for historical
         * compatibility --- specifically, allowing table references,
         * such as
         *			INSERT INTO foo VALUES(bar.*)
         *
         * The sublist is just computed directly as the Query's targetlist,
         * with no VALUES RTE.	So it works just like SELECT without FROM.
         * ----------
         */
        List* valuesLists = selectStmt->valuesLists;

        AssertEreport(list_length(valuesLists) == 1, MOD_OPT, "list length is wrong");
        AssertEreport(selectStmt->intoClause == NULL, MOD_OPT, "intoClause should not happen here");

        /* Do basic expression transformation (same as a ROW() expr) */
        exprList = transformExpressionList(pstate, (List*)linitial(valuesLists), EXPR_KIND_VALUES_SINGLE);

        /*
         * If td_compatible_truncation equal true and no foreign table found,
         * the auto truncation funciton should be enabled.
         */
        if (u_sess->attr.attr_sql.sql_compatibility == C_FORMAT && targetrel != NULL &&
            !RelationIsForeignTable(targetrel) &&
            !RelationIsStream(targetrel)) {
            if (u_sess->attr.attr_sql.td_compatible_truncation) {
                pstate->p_is_td_compatible_truncation = true;
            } else {
                pstate->tdTruncCastStatus = NOT_CAST_BECAUSEOF_GUC;
            }
        }

        /* Prepare row for assignment to target table */
        exprList = transformInsertRow(pstate, exprList, stmt->cols, icolumns, attrnos);
    }

    if (IS_SUPPORT_RIGHT_REF(rightRefState)) {
        SetInsertAttrnoState(pstate, attrnos, list_length(exprList));
    }

    /*
     * Generate query's target list using the computed list of expressions.
     * Also, mark all the target columns as needing insert permissions.
     */
    rte = (RangeTblEntry*)linitial(pstate->p_target_rangetblentry);
    qry->targetList = NIL;
    icols = list_head(icolumns);
    attnos = list_head(attrnos);
    foreach (lc, exprList) {
        Expr* expr = (Expr*)lfirst(lc);
        ResTarget* col = NULL;
        AttrNumber attr_num;
        TargetEntry* tle = NULL;

        col = (ResTarget*)lfirst(icols);
        AssertEreport(IsA(col, ResTarget), MOD_OPT, "nodeType inconsistant");
        attr_num = (AttrNumber)lfirst_int(attnos);

        tle = makeTargetEntry(expr, attr_num, col->name, false);
        qry->targetList = lappend(qry->targetList, tle);

        rte->insertedCols = bms_add_member(rte->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber);

        icols = lnext(icols);
        attnos = lnext(attnos);
    }

    /* Process DUPLICATE KEY UPDATE, if any. */
    if (stmt->upsertClause) {
        if (IS_SUPPORT_RIGHT_REF(rightRefState)) {
            pstate->p_varnamespace = NIL;
            rightRefState->isUpsert = true;
            SetUpsertAttrnoState(pstate, stmt->upsertClause->targetList);
            qry->upsertClause = transformUpsertClause(pstate, stmt->upsertClause, qry->resultRelations);
            rightRefState->isUpsert = false;
        } else {
            qry->upsertClause = transformUpsertClause(pstate, stmt->upsertClause, qry->resultRelations);
        }
    }
    /*
     * If we have a RETURNING clause, we need to add the target relation to
     * the query namespace before processing it, so that Var references in
     * RETURNING will work.  Also, remove any namespace entries added in a
     * sub-SELECT or VALUES list.
     */
    if (stmt->returningList) {
        pstate->p_relnamespace = NIL;
        pstate->p_varnamespace = NIL;
        pstate->rightRefState = nullptr;
        addRTEtoQuery(pstate, rte, false, true, true);
        qry->returningList = transformReturningList(pstate, stmt->returningList);
        if (qry->returningList != NIL && RelationIsColStore(targetrel)) {
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Un-support feature"),
                    errdetail("column stored relation doesn't support INSERT returning")));
        }
    }

    /* done building the range table and jointree */
    qry->rtable = pstate->p_rtable;
    qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);

    qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
    qry->hasSubLinks = pstate->p_hasSubLinks;
    /* aggregates not allowed (but subselects are okay) */
    if (pstate->p_hasAggs) {
        ereport(ERROR,
            (errcode(ERRCODE_GROUPING_ERROR),
                errmsg("cannot use aggregate function in VALUES"),
                parser_errposition(pstate, locate_agg_of_level((Node*)qry, 0))));
    }
    if (pstate->p_hasWindowFuncs) {
        ereport(ERROR,
            (errcode(ERRCODE_WINDOWING_ERROR),
                errmsg("cannot use window function in VALUES"),
                parser_errposition(pstate, locate_windowfunc((Node*)qry))));
    }

    assign_query_collations(pstate, qry);
    assign_query_ignore_flag(pstate, qry);

    CheckUnsupportInsertSelectClause(qry);

    /* Set query tdTruncCastStatus to recored if auto truncation enabled or not. */
    qry->tdTruncCastStatus = pstate->tdTruncCastStatus;
    qry->hintState = stmt->hintState;
    qry->hasIgnore = stmt->hasIgnore;

    if (IS_ENABLE_RIGHT_REF(rightRefState)) {
        qry->rightRefState = rightRefState;
    } else {
        qry->rightRefState = nullptr;
        pstate->rightRefState = nullptr;
        pfree_ext(rightRefState);
    }

    return qry;
}

static void checkUpsertTargetlist(Relation targetTable, List* updateTlist)
{
    List* index_list = RelationGetIndexInfoList(targetTable);
    if (check_unique_constraint(index_list)) {
        ListCell* target = NULL;
        ListCell* index = NULL;
        IndexInfo* index_info = NULL;
        Bitmapset* target_attrs = NULL; /* attr bitmap according to targetlist */
        Bitmapset* index_attrs = NULL; /* attr bitmap according to index */
        TargetEntry* tle = NULL;

        foreach (target, updateTlist) {
            tle = (TargetEntry*)lfirst(target);
            target_attrs = bms_add_member(target_attrs, tle->resno);
        }

        foreach (index, index_list) {
            index_info = (IndexInfo*)lfirst(index);
            for (int i = 0; i < index_info->ii_NumIndexAttrs; i++) {
                int attrno = index_info->ii_KeyAttrNumbers[i];
                if (attrno > 0) {
                    index_attrs = bms_add_member(index_attrs, attrno);
                }
            }
        }

        /* Allow in B_FORMAT */
        if (bms_overlap(index_attrs, target_attrs) && u_sess->attr.attr_sql.sql_compatibility != B_FORMAT) {
            ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                         errmsg("INSERT ON DUPLICATE KEY UPDATE don't allow update on primary key or unique key."))));
        }
    }
}

List* BuildExcludedTargetlist(Relation targetrel, Index exclRelIndex)
{
    List* result = NIL;
    int attno;
    Var* var = NULL;
    TargetEntry* te = NULL;

    /*
     * Note that resnos of the tlist must correspond to attnos of the
     * underlying relation, hence we need entries for dropped columns too.
     */
    for (attno = 0; attno < RelationGetNumberOfAttributes(targetrel); attno++) {
        Form_pg_attribute attr = &targetrel->rd_att->attrs[attno];
        char* name = NULL;

        if (attr->attisdropped) {
            /*
             * can't use atttypid here, but it doesn't really matter what type
             * the Const claims to be.
             */
            var = (Var*)makeNullConst(INT4OID, -1, InvalidOid);
        } else {
            var = makeVar(exclRelIndex, attno + 1, attr->atttypid, attr->atttypmod,
                          attr->attcollation, 0);
            name = pstrdup(NameStr(attr->attname));
        }

        te = makeTargetEntry((Expr*)var, attno + 1, name, false);

        result = lappend(result, te);
    }

    /*
     * Add a whole-row-Var entry to support references to "EXCLUDED.*".  Like
     * the other entries in the EXCLUDED tlist, its resno must match the Var's
     * varattno, else the wrong things happen while resolving references in
     * setrefs.c.  This is against normal conventions for targetlists, but
     * it's okay since we don't use this as a real tlist.
     */
    var = makeVar(exclRelIndex, InvalidAttrNumber, targetrel->rd_rel->reltype,
                  -1, InvalidOid, 0);
    te = makeTargetEntry((Expr*)var, InvalidAttrNumber, NULL, true);
    result = lappend(result, te);

    return result;
}

static bool CheckRlsPolicyForUpsert(Relation targetrel)
{
    ListCell *item = NULL;
    RlsPolicy *policy = NULL;
    List *relRlsPolicies = targetrel->rd_rlsdesc->rlsPolicies;
    foreach (item, relRlsPolicies) {
        policy = (RlsPolicy *)lfirst(item);
        if (policy->cmdName == ACL_UPDATE_CHR || policy->cmdName == RLS_CMD_ALL_CHR) {
            return true;
        }
    }
    return false;
}

/*
 * The following check only affect distributed deployment.
 * Sublink in upsert's where clause is supported for centralized mode.
 */
#ifdef ENABLE_MULTIPLE_NODES
static bool ContainSubLinkWalker(Node* node, void* context)
{
    if (node == NULL) {
        return false;
    }

    if (IsA(node, SubLink)) {
        return true;
    }

    return expression_tree_walker(node, (bool (*)())ContainSubLinkWalker, (void*)context);
}

static bool ContainSubLink(Node* clause)
{
    return ContainSubLinkWalker(clause, NULL);
}
#endif /* ENABLE_MULTIPLE_NODES */

static UpsertExpr* transformUpsertClause(ParseState* pstate, UpsertClause* upsertClause, List* resultRelations)
{
    UpsertExpr* result = NULL;
    List* updateTlist = NIL;
    RangeTblEntry* exclRte = NULL;
    int exclRelIndex = 0;
    List* exclRelTlist = NIL;
    Node* updateWhere = NULL;
    UpsertAction action = UPSERT_NOTHING;
    Relation targetrel = (Relation)linitial(pstate->p_target_relation);
#ifdef ENABLE_MULTIPLE_NODES
    if (targetrel->rd_rel->relhastriggers) {
        ereport(WARNING,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("triggers are not supported and will be ignored by INSERT ON DUPLICATE KEY UPDATE.")));
    }
#endif

    if (RelationHasRlspolicy(targetrel->rd_id) && CheckRlsPolicyForUpsert(targetrel)) {
        ereport(ERROR,
            (errmodule(MOD_SEC_POLICY),
                errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Row level security policy is not supported by INSERT ON DUPLICATE KEY UPDATE."),
                errdetail("Table %s with row level security policy.", RelationGetRelationName(targetrel)),
                errcause("Target table with row level security policy."),
                erraction("Shutdown row level security policy, Use INSERT and UPDATE instead INSERT ON DUPLICATE KEY "
                          "UPDATE.")));
    }

    if (upsertClause->targetList != NIL) {
        pstate->p_is_insert = false;
        action = UPSERT_UPDATE;
        exclRte = addRangeTableEntryForRelation(pstate, targetrel, makeAlias("excluded", NIL), false, false);
        exclRte->isexcluded = true;
        exclRelIndex = list_length(pstate->p_rtable);

        /*
         * Build a targetlist for the EXCLUDED pseudo relation. Out of
         * simplicity we do that here, because expandRelAttrs() happens to
         * nearly do the right thing; specifically it also works with views.
         * It'd be more proper to instead scan some pseudo scan node, but it
         * doesn't seem worth the amount of code required.
         *
         * The only caveat of this hack is that the permissions expandRelAttrs
         * adds have to be reset. markVarForSelectPriv() will add the exact
         * required permissions back.
         */

        exclRelTlist = BuildExcludedTargetlist(targetrel, exclRelIndex);
        exclRte->requiredPerms = 0;
        exclRte->selectedCols = NULL;

        /*
         * Add EXCLUDED and the target RTE to the namespace, so that they can
         * be used in the UPDATE statement.
         */
        addRTEtoQuery(pstate, exclRte, false, true, true);
        addRTEtoQuery(pstate, (RangeTblEntry*)linitial(pstate->p_target_rangetblentry), false, true, true);

        updateTlist = transformTargetList(pstate, upsertClause->targetList, EXPR_KIND_UPDATE_TARGET);
        /* Done with select-like processing, move on transforming to match update set target column */
        updateTlist = transformUpdateTargetList(pstate, updateTlist, upsertClause->targetList, resultRelations);
        updateWhere = transformWhereClause(pstate, upsertClause->whereClause, EXPR_KIND_WHERE, "WHERE");
#ifdef ENABLE_MULTIPLE_NODES
        /* Do not support sublinks in update where clause for now */
        if (ContainSubLink(updateWhere)) {
            ereport(ERROR,
                (errmodule(MOD_OPT_PLANNER), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                    errmsg("Feature is not supported for INSERT ON DUPLICATE KEY UPDATE."),
                    errdetail("Do not support sublink in where clause."),
                    errcause("Unsupported syntax."),
                    erraction("Check if the query can be rewritten into MERGE INTO statement "
                              "or reduce the sublink in where clause.")));
        }
#endif /* ENABLE_MULTIPLE_NODES */
        /* We can't update primary or unique key in upsert, check it here */
#ifdef ENABLE_MULTIPLE_NODES
        if (IS_PGXC_COORDINATOR && !u_sess->attr.attr_sql.enable_upsert_to_merge) {
            checkUpsertTargetlist(targetrel, updateTlist);
        }
#else
        checkUpsertTargetlist(targetrel, updateTlist);
#endif
    }

    /* Finally, build DUPLICATE KEY UPDATE [NOTHING | ... ] expression */
    result = makeNode(UpsertExpr);
    result->updateTlist = updateTlist;
    result->exclRelIndex = exclRelIndex;
    result->exclRelTlist = exclRelTlist;
    result->upsertAction = action;
    result->upsertWhere = updateWhere;
    return result;
}

/*
 * Prepare an INSERT row for assignment to the target table.
 *
 * The row might be either a VALUES row, or variables referencing a
 * sub-SELECT output.
 */
List* transformInsertRow(ParseState* pstate, List* exprlist, List* stmtcols, List* icolumns, List* attrnos)
{
    List* result = NIL;
    ListCell* lc = NULL;
    ListCell* icols = NULL;
    ListCell* attnos = NULL;

    /*
     * Check length of expr list.  It must not have more expressions than
     * there are target columns.  We allow fewer, but only if no explicit
     * columns list was given (the remaining columns are implicitly
     * defaulted).	Note we must check this *after* transformation because
     * that could expand '*' into multiple items.
     */
    if (list_length(exprlist) > list_length(icolumns)) {
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("INSERT has more expressions than target columns"),
                parser_errposition(pstate, exprLocation((Node*)list_nth(exprlist, list_length(icolumns))))));
    }
    if (stmtcols != NIL && list_length(exprlist) < list_length(icolumns)) {
        /*
         * We can get here for cases like INSERT ... SELECT (a,b,c) FROM ...
         * where the user accidentally created a RowExpr instead of separate
         * columns.  Add a suitable hint if that seems to be the problem,
         * because the main error message is quite misleading for this case.
         * (If there's no stmtcols, you'll get something about data type
         * mismatch, which is less misleading so we don't worry about giving a
         * hint in that case.)
         */
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("INSERT has more target columns than expressions"),
                ((list_length(exprlist) == 1 &&
                     count_rowexpr_columns(pstate, (Node*)linitial(exprlist)) == list_length(icolumns))
                        ? errhint("The insertion source is a row expression containing the same number of columns "
                                  "expected by the INSERT. Did you accidentally use extra parentheses?")
                        : 0),
                parser_errposition(pstate, exprLocation((Node*)list_nth(icolumns, list_length(exprlist))))));
    }

    /*
     * Prepare columns for assignment to target table.
     */
    result = NIL;
    icols = list_head(icolumns);
    attnos = list_head(attrnos);
    foreach (lc, exprlist) {
        Expr* expr = (Expr*)lfirst(lc);
        ResTarget* col = NULL;
#ifndef ENABLE_MULTIPLE_NODES		
        /*
         * Rownum is not allowed in exprlist in INSERT statement.
         */
        ExcludeRownumExpr(pstate, (Node*)expr);
#endif
        col = (ResTarget*)lfirst(icols);
        AssertEreport(IsA(col, ResTarget), MOD_OPT, "nodeType inconsistant");

        expr = transformAssignedExpr(pstate, expr, EXPR_KIND_INSERT_TARGET, col->name, lfirst_int(attnos), col->indirection, col->location,
            (Relation)linitial(pstate->p_target_relation), (RangeTblEntry*)linitial(pstate->p_target_rangetblentry));

        result = lappend(result, expr);

        icols = lnext(icols);
        attnos = lnext(attnos);
    }

    return result;
}

/*
 * count_rowexpr_columns -
 *	  get number of columns contained in a ROW() expression;
 *	  return -1 if expression isn't a RowExpr or a Var referencing one.
 *
 * This is currently used only for hint purposes, so we aren't terribly
 * tense about recognizing all possible cases.	The Var case is interesting
 * because that's what we'll get in the INSERT ... SELECT (...) case.
 */
static int count_rowexpr_columns(ParseState* pstate, Node* expr)
{
    if (expr == NULL) {
        return -1;
    }
    if (IsA(expr, RowExpr)) {
        return list_length(((RowExpr*)expr)->args);
    }
    if (IsA(expr, Var)) {
        Var* var = (Var*)expr;
        AttrNumber attnum = var->varattno;

        if (attnum > 0 && var->vartype == RECORDOID) {
            RangeTblEntry* rte = NULL;

            rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
            if (rte->rtekind == RTE_SUBQUERY) {
                /* Subselect-in-FROM: examine sub-select's output expr */
                TargetEntry* ste = get_tle_by_resno(rte->subquery->targetList, attnum);

                if (ste == NULL || ste->resjunk) {
                    return -1;
                }
                expr = (Node*)ste->expr;
                if (IsA(expr, RowExpr)) {
                    return list_length(((RowExpr*)expr)->args);
                }
            }
        }
    }
    return -1;
}

static char* fetchSelectStmtFromCTAS(char* source_text)
{
    char* select_pos = strcasestr(source_text, "SELECT");
    return select_pos;
}

static bool shouldTransformStartWithStmt(ParseState* pstate, SelectStmt* stmt, Query* selectQuery)
{
    /*
     * Only applicable to START WITH CONNECT BY clauses.
     */
    if (stmt->startWithClause == NULL || pstate->p_sourcetext == NULL) {
        return false;
    }

    /*
     * Back up the current select statement to be restored after query re-writing
     * for cases of START WITH CONNNECT BY under CREATE TABLE AS.
     */
    selectQuery->sql_statement = fetchSelectStmtFromCTAS((char*)pstrdup(pstate->p_sourcetext));

    return true;
}

/*
 * transformVariableSetValueStmt - 
 *    transforms a VariableSetStmt
 */
static Query* transformVariableMutiSetStmt(ParseState* pstate, VariableMultiSetStmt* muti_stmt)
{
    Query *query = makeNode(Query);
    List *resultList = NIL;

    List* stmts = muti_stmt->args;
    ListCell* cell = NULL;
    VariableSetStmt* set_stmt;
    List *usersetlist = NIL;

    foreach(cell, stmts) {
        Node* stmt = (Node*)lfirst(cell);

        if (nodeTag(stmt) == T_AlterSystemStmt) {
            AlterSystemStmt *alter_sys_stmt = (AlterSystemStmt *)stmt;
            set_stmt = alter_sys_stmt->setstmt;
        } else {
            set_stmt = (VariableSetStmt*)stmt;
        }

        if (set_stmt->kind == VAR_SET_DEFINED) {
            transformVariableSetStmt(pstate, set_stmt);
            if (strcmp(set_stmt->name, "USER DEFINED VARIABLE") == 0) {
                usersetlist = list_concat(usersetlist, set_stmt->defined_args);
            }
        }

        if (set_stmt->kind == VAR_SET_VALUE)
            transformVariableSetValueStmt(pstate, set_stmt);

        if (nodeTag(stmt) == T_AlterSystemStmt) {
            AlterSystemStmt *newnode = makeNode(AlterSystemStmt);
            newnode->setstmt = set_stmt;
            resultList = lappend(resultList, newnode);
        } else if (set_stmt->kind == VAR_SET_DEFINED) {
            if (strcmp(set_stmt->name, "USER DEFINED VARIABLE") != 0) {
                resultList = lappend(resultList, set_stmt);
            }
        } else {
            resultList = lappend(resultList, set_stmt);
        }
    }

    if (list_length(usersetlist) != 0) {
        VariableSetStmt* new_set_stmt = makeNode(VariableSetStmt);
        new_set_stmt->kind = VAR_SET_DEFINED;
        new_set_stmt->name = "USER DEFINED VARIABLE";
        new_set_stmt->is_local = false;
        new_set_stmt->defined_args = usersetlist;
        resultList = lappend(resultList, new_set_stmt);
    }

    list_free(muti_stmt->args);
    muti_stmt->args = resultList;

    query->commandType = CMD_UTILITY;
    query->utilityStmt = (Node*)muti_stmt;
    return query;
}

/*
 * transformVariableSetStmt - transforms a user-defined variable
 */
static void transformVariableSetStmt(ParseState* pstate, VariableSetStmt* stmt)
{
    List *resultList = NIL;
    ListCell *temp = NULL;

    foreach (temp, stmt->defined_args) {
        UserSetElem *userElem = (UserSetElem *)lfirst(temp);
        UserSetElem *newUserElem = makeNode(UserSetElem);
        newUserElem->name = userElem->name;
        
        Node *node = transformExprRecurse(pstate, (Node *)userElem->val);
        assign_expr_collations(pstate, node);

        if (IsA(node, UserSetElem)) {
            newUserElem->name = list_concat(newUserElem->name, ((UserSetElem *)node)->name);
            newUserElem->val = ((UserSetElem *)node)->val;
        } else {
            newUserElem->val = (Expr *)node;
        }
        
        resultList = lappend(resultList, newUserElem);
    }
    stmt->defined_args = resultList;
}

/*
 * transformVariableSetValueStmt - 
 *    transforms a VariableSetStmt
 */
static void transformVariableSetValueStmt(ParseState* pstate, VariableSetStmt* stmt)
{
    List *resultlist = NIL;
    ListCell* l = NULL;

    foreach(l, stmt->args) {
        Node* expr = (Node*)lfirst(l);

        /* 
         * The expression will eventually be converted to A_Const, 
         * so the current A_Const expr does not need to be converted.
         */
        Node *node = NULL;
        if (IsA(expr, A_Const)) {
            node = expr;
        } else {
            node = transformExpr(pstate, expr, EXPR_KIND_OTHER);
        }

        resultlist = lappend(resultlist, (Expr*)node);
    }

    list_free(stmt->args);
    stmt->args = resultlist;
}

/*
 * transformSelectStmt -
 *	  transforms a Select Statement
 *
 * Note: this covers only cases with no set operations and no VALUES lists;
 * see below for the other cases.
 */
static Query* transformSelectStmt(ParseState* pstate, SelectStmt* stmt, bool isFirstNode, bool isCreateView)
{
    Query* qry = makeNode(Query);
    ParseState *origin_pstate = NULL;
    SelectStmt *origin_stmt = NULL;
    Node* qual = NULL;
    ListCell* l = NULL;

    /* support unrotate grammar */
    if (stmt->unrotateInfo != NULL) {
        return transformUnrotateStmt(pstate, stmt);
    }

    qry->commandType = CMD_SELECT;

    if (stmt->startWithClause != NULL) {
        errno_t rc;
        origin_stmt = (SelectStmt *)copyObject(stmt);
        origin_pstate = make_parsestate(NULL);
        rc = memcpy_s(origin_pstate, sizeof(ParseState), pstate, sizeof(ParseState));
        origin_pstate->p_rtable = list_copy(pstate->p_rtable);
        origin_pstate->p_ctenamespace = list_copy(pstate->p_ctenamespace);
        origin_pstate->p_relnamespace = list_copy(pstate->p_relnamespace);
        origin_pstate->p_varnamespace = list_copy(pstate->p_varnamespace);
        origin_pstate->p_joinlist = list_copy(pstate->p_joinlist);
        origin_pstate->p_joinexprs = list_copy(pstate->p_joinexprs);
        securec_check(rc, "\0", "\0");
        pstate->p_addStartInfo = true;
        pstate->p_sw_selectstmt = stmt;
        pstate->origin_with = (WithClause *)copyObject(stmt->withClause);
    }

    /* process the WITH clause independently of all else */
    if (stmt->withClause) {
        qry->hasRecursive = stmt->withClause->recursive;
        qry->cteList = transformWithClause(pstate, stmt->withClause);
        qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
    }

    /* Complain if we get called from someplace where INTO is not allowed */
    if (stmt->intoClause) {
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("SELECT ... INTO is not allowed here"),
                parser_errposition(pstate, exprLocation((Node*)stmt->intoClause))));
    }

    /* make FOR UPDATE/FOR SHARE info available to addRangeTableEntry */
    pstate->p_locking_clause = stmt->lockingClause;

    /* make WINDOW info available for window functions, too */
    pstate->p_windowdefs = stmt->windowClause;

    /* process the FROM clause */
    transformFromClause(pstate, stmt->fromClause, isFirstNode, isCreateView);

    /* process index_hint in tables*/
    qry->indexhintList = lappend3(qry->indexhintList, pstate->p_indexhintLists);

    /* transform START WITH...CONNECT BY clause */
    if (shouldTransformStartWithStmt(pstate, stmt, qry)) {
        transformStartWith(pstate, origin_pstate, stmt, origin_stmt, qry, isFirstNode, isCreateView);
    }

    /* transform targetlist */
    qry->targetList = transformTargetList(pstate, stmt->targetList, EXPR_KIND_SELECT_TARGET);

    /* Transform operator "(+)" to outer join */
    if (stmt->hasPlus && stmt->whereClause != NULL) {
        transformOperatorPlus(pstate, &stmt->whereClause);
    }

    qry->starStart = list_copy(pstate->p_star_start);
    qry->starEnd = list_copy(pstate->p_star_end);
    qry->starOnly = list_copy(pstate->p_star_only);

    /* mark column origins */
    markTargetListOrigins(pstate, qry->targetList);

    /* transform WHERE
     * Only "(+)" is valid when  it's in WhereClause of Select, set the flag to be trure
     * during transform Whereclause.
     */
    setIgnorePlusFlag(pstate, true);
    qual = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE");
    setIgnorePlusFlag(pstate, false);

    /*
     * Initial processing of HAVING clause is just like WHERE clause.
     */
    qry->havingQual = transformWhereClause(pstate, stmt->havingClause, EXPR_KIND_HAVING, "HAVING");

    pstate->shouldCheckOrderbyCol = (!ALLOW_ORDERBY_UNDISTINCT_COLUMN &&
                                    stmt->distinctClause && linitial(stmt->distinctClause) == NULL &&
                                    !IsInitdb && DB_IS_CMPT(B_FORMAT));

    /*
     * Transform sorting/grouping stuff.  Do ORDER BY first because both
     * transformGroupClause and transformDistinctClause need the results. Note
     * that these functions can also change the targetList, so it's passed to
     * them by reference.
     */
    qry->sortClause = transformSortClause(
        pstate, stmt->sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, true /* fix unknowns */, false /* allow SQL92 rules */);

    pstate->shouldCheckOrderbyCol = false;

    /*
     * Transform A_const to columnref type in group by clause, So that repeated group column
     * will deleted in function transformGroupClause. If not to delete repeated column, for
     * group by rollup can have error result, because we need set null to non- group column.
     *
     * select a, b, b
     *	from t1
     *	group by rollup(1, 2), 3;
     *
     * To this example, column b should not be set to null, but if not to delete repeated column
     * b will be set to null and two b value is not equal.
     */
    if (include_groupingset((Node*)stmt->groupClause)) {
        transformGroupConstToColumn(pstate, (Node*)stmt->groupClause, qry->targetList);
    }

    qry->groupClause = transformGroupClause(pstate,
        stmt->groupClause,
        &qry->groupingSets,
        &qry->targetList,
        qry->sortClause,
        EXPR_KIND_GROUP_BY,
        false /* allow SQL92 rules */);

    if (stmt->distinctClause == NIL) {
        qry->distinctClause = NIL;
        qry->hasDistinctOn = false;
    } else if (linitial(stmt->distinctClause) == NULL) {
        /* We had SELECT DISTINCT */
        qry->distinctClause = transformDistinctClause(pstate, &qry->targetList, qry->sortClause, false);
        qry->hasDistinctOn = false;
    } else {
        /* We had SELECT DISTINCT ON */
        qry->distinctClause =
            transformDistinctOnClause(pstate, stmt->distinctClause, &qry->targetList, qry->sortClause);
        qry->hasDistinctOn = true;
    }

    /* transform LIMIT */
    pstate->p_is_percent = stmt->limitIsPercent;
    qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset, EXPR_KIND_OFFSET, "OFFSET");
    qry->limitCount = transformLimitClause(pstate, stmt->limitCount, EXPR_KIND_LIMIT, "LIMIT");
    qry->limitIsPercent = stmt->limitIsPercent;
    qry->limitWithTies = stmt->limitWithTies;
    if (u_sess->hook_cxt.invokeTransformSelectForLimitHook) {
        ((InvokeTransformSelectForLimitHookType)(u_sess->hook_cxt.invokeTransformSelectForLimitHook))(stmt);
    }
    qry->isFetch = stmt->isFetch;

    /* transform window clauses after we have seen all window functions */
    qry->windowClause = transformWindowDefinitions(pstate, pstate->p_windowdefs, &qry->targetList);

    /* resolve any still-unresolved output columns as being type text */
    if (pstate->p_resolve_unknowns) {
        resolveTargetListUnknowns(pstate, qry->targetList);
    }

    qry->rtable = pstate->p_rtable;
    qry->jointree = makeFromExpr(pstate->p_joinlist, qual);

    qry->hasSubLinks = pstate->p_hasSubLinks;
    qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
    if (pstate->p_hasWindowFuncs) {
        parseCheckWindowFuncs(pstate, qry);
    }
    qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
    qry->hasAggs = pstate->p_hasAggs;

    foreach (l, stmt->lockingClause) {
        transformLockingClause(pstate, qry, (LockingClause*)lfirst(l), false);
    }

    qry->hintState = stmt->hintState;

    /*
     * If query is under one insert statement and include a foreign table,
     * then set top level parsestate p_is_foreignTbl_exist to true.
     */
    if (u_sess->attr.attr_sql.td_compatible_truncation && u_sess->attr.attr_sql.sql_compatibility == C_FORMAT &&
        pstate->p_is_in_insert && checkForeignTableExist(pstate->p_rtable))
        set_ancestor_ps_contain_foreigntbl(pstate);

    assign_query_collations(pstate, qry);

    /* this must be done after collations, for reliable comparison of exprs */
    if (pstate->p_hasAggs || qry->groupClause || qry->groupingSets || qry->havingQual) {
        parseCheckAggregates(pstate, qry);
    }

    /*
     * If SelectStmt has been rewrite by startwith/connectby, it should
     * return as With-Recursive for upper level. So we need to fix fromClause.
     */
    if (pstate->p_addStartInfo) {
        AdaptSWSelectStmt(pstate, stmt);
    }

    return qry;
}

static List* removeTargetListByNameList(List* targetList, List* nameList)
{
    ListCell * cell, *targetCell, *next, *prev;
    bool isfind = false;
    prev = NULL;
    for (targetCell = list_head(targetList); targetCell; targetCell = next) {
        ResTarget *resTarget = (ResTarget *)lfirst(targetCell);
        next = lnext(targetCell);
        isfind = false;
        if (IsA(resTarget->val, ColumnRef)) {
            Node *field = (Node *)linitial(((ColumnRef *)resTarget->val)->fields);
            if (IsA(field, A_Star))
                continue;
            const char *colName = strVal(field);
            foreach (cell, nameList) {
                if (strcmp(strVal((Value *)lfirst(cell)), colName) == 0) {
                    targetList = list_delete_cell(targetList, targetCell, prev);
                    isfind = true;
                    break;
                }
            }
        }
        if (!isfind)
            prev = targetCell;
    }
    return targetList;
}


char* AConstToString(A_Const *con)
{
    StringInfoData buf;
    initStringInfo(&buf);
    Value *v = &con->val;
    switch (v->type) {
        case T_Integer:
            appendStringInfo(&buf, "%ld", intVal(v));
            break;
        case T_Float:
        case T_String:
        case T_BitString:
            appendStringInfo(&buf, "%s", strVal(v));
            break;
        case T_Null:
        default:
            /* nothing to do */
            break;
    }
    return buf.data;
}

bool get_column_name(Node* node, char** column_name)
{
    if (node == NULL) {
        return false;
    }
    if (IsA(node, A_Const)) {
        A_Const* a_const = (A_Const*)node;
        *column_name = pstrdup(AConstToString(a_const));
        return true;
    } else if (IsA(node, ColumnRef)) {
        Node *field = (Node *)linitial(((ColumnRef *)node)->fields);
        if (IsA(field, A_Star))
            return false;
        *column_name = pstrdup(strVal((Value *)field));
        return true;
    } else {
        return raw_expression_tree_walker(node, (bool (*)())get_column_name, (void*)column_name);
    }
}

static Query* transformUnrotateStmt(ParseState* pstate, SelectStmt* stmt)
{
    List *parsetree_list;
    ListCell *cell, *cell1, *targetCell, *next, *prev;
    ListCell *inexprCell, *colCell, *forCell, *aliasCell;
    StringInfoData union_all_sql;
    StringInfoData from_clause_sql;
    int counter = 0;
    int in_counter = 0;
    List *targetList = NIL;
    List *aStarList = NIL;

    if (stmt->withClause) {
        WithClause *withclause = (WithClause *)copyObject(stmt->withClause);
        (void)transformWithClause(pstate, withclause);
    }
    ParseState *pstate1 = make_parsestate(pstate);
    pstate1->p_sourcetext = pstrdup(pstate->p_sourcetext);

    transformFromClause(pstate1, stmt->fromClause);

    if (!pstate1->p_rtable) {
        ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("NOT ROTATE in from clause error")));
    }
    
    initStringInfo(&from_clause_sql);
    RangeTblEntry *rte = (RangeTblEntry *)linitial(pstate1->p_rtable);
    if (RTE_RELATION == rte->rtekind)
        appendStringInfo(&from_clause_sql, " FROM %s ", quote_identifier(rte->relname));
    else if (RTE_SUBQUERY == rte->rtekind) {
        StringInfo select_sql = makeStringInfo();
        deparse_query(rte->subquery, select_sql, NIL, false, false, (void*)pstate1->p_ref_hook_state);
        appendStringInfo(&from_clause_sql, " FROM (%s) ", select_sql->data);
        DestroyStringInfo(select_sql);
    } else if (RTE_CTE == rte->rtekind) {
        appendStringInfo(&from_clause_sql, " FROM %s ", quote_identifier(rte->ctename));
    } else {
        ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("NOT ROTATE in from clause error")));
    }
    if (rte->alias)
        appendStringInfo(&from_clause_sql, "AS %s ", quote_identifier(rte->alias->aliasname));
    List *stmt_targetList = list_copy(stmt->targetList);
    /* remove target in colNameList */
    stmt_targetList = removeTargetListByNameList(stmt_targetList, stmt->unrotateInfo->colNameList);
    /* remove target in forColName */
    stmt_targetList = removeTargetListByNameList(stmt_targetList, stmt->unrotateInfo->forColName);

    foreach(targetCell, stmt_targetList) {
        ResTarget *resTarget1 = (ResTarget *)lfirst(targetCell);
        if (IsA(resTarget1->val, ColumnRef)) {
            ColumnRef *cref = (ColumnRef *)resTarget1->val;
            Node *field = (Node *)linitial(cref->fields);
            if (IsA(field, A_Star)) {
                if (!targetList) {
                    aStarList = list_make1(lfirst(targetCell));
                    targetList = transformTargetList(pstate1, aStarList, EXPR_KIND_SELECT_TARGET);
                    free_parsestate(pstate1);
                    break;
                }
            }
        }
    }

    /* remove target in unrotateInExpr */
    foreach (inexprCell, stmt->unrotateInfo->inExprList) {
        UnrotateInCell *unrotateinCell = (UnrotateInCell *)lfirst(inexprCell);
        foreach (cell, unrotateinCell->unrotateInExpr) {
            ResTarget *resTarget = (ResTarget *)lfirst(cell);
            if (IsA(resTarget->val, ColumnRef)) {
                const char *colName = strVal((Value *)linitial(((ColumnRef *)resTarget->val)->fields));
                prev = NULL;
                for (targetCell = list_head(targetList); targetCell; targetCell = next) {
                    TargetEntry *te = (TargetEntry *)lfirst(targetCell);
                    next = lnext(targetCell);
                    if (strcmp(te->resname, colName) == 0)
                        targetList = list_delete_cell(targetList, targetCell, prev);
                    else
                        prev = targetCell;
                }
                for (targetCell = list_head(stmt_targetList); targetCell; targetCell = next) {
                    ResTarget *rt = (ResTarget *)lfirst(targetCell);
                    char* colName1 = NULL;
                    if (!get_column_name(rt->val, &colName1))
                        continue;
                    next = lnext(targetCell);
                    if (strcmp(colName1, colName) == 0)
                        stmt_targetList = list_delete_cell(stmt_targetList, targetCell, prev);
                    else
                        prev = targetCell;

                    pfree_ext(colName1);
                }
            } else {
                ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("NOT ROTATE in clause error"),
                                parser_errposition(pstate, exprLocation((Node *)resTarget->val))));
            }
        }
    }

    initStringInfo(&union_all_sql);
    foreach (inexprCell, stmt->unrotateInfo->inExprList) {
        UnrotateInCell *unrotateinCell = (UnrotateInCell *)lfirst(inexprCell);

        if (counter > 0)
            appendStringInfo(&union_all_sql, "UNION ALL ");

        appendStringInfo(&union_all_sql, "SELECT ");
        if (list_length(stmt->unrotateInfo->colNameList) != list_length(unrotateinCell->unrotateInExpr)) {
            ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR), 
                    errmsg("the number of elements in NOT ROTATE IN CLAUSE doesn't agree with the number of columns NOT ROTATE specified")));
        }

        foreach (targetCell, stmt_targetList) {
            ResTarget *resTarget1 = (ResTarget *)lfirst(targetCell);
            if (IsA(resTarget1->val, A_Const)) {
                A_Const* a_const = (A_Const*)resTarget1->val;
                char* res_name = AConstToString(a_const);
                appendStringInfo(&union_all_sql, "'%s', ", res_name);
                pfree(res_name);
            } else {
                ColumnRef *cref = (ColumnRef *)resTarget1->val;
                Node *field = (Node *)linitial(cref->fields);
                if (IsA(field, A_Star)) {
                    foreach (cell1, targetList) {
                        TargetEntry *te = (TargetEntry *)lfirst(cell1);
                        appendStringInfo(&union_all_sql, "%s, ", te->resname);
                    }
                } else
                    appendStringInfo(&union_all_sql, "%s, ", strVal((Value *)field));
            }
        }

        if (!unrotateinCell->aliaList) {
            foreach(forCell,stmt->unrotateInfo->forColName) {
                in_counter = 0;
                appendStringInfo(&union_all_sql, "\'");
                foreach(cell, unrotateinCell->unrotateInExpr) {
                    if (in_counter > 0) {
                        appendStringInfo(&union_all_sql, "_");
                    }
                    ResTarget *resTarget = (ResTarget *)lfirst(cell);
                    if (IsA(resTarget->val, ColumnRef)) {
                        if (NULL != resTarget->name) {
                            appendStringInfo(&union_all_sql, "%s", resTarget->name);
                        }
                        else {
                            appendStringInfo(&union_all_sql, "%s", strVal((Value *) linitial(((ColumnRef*) resTarget->val)->fields)));
                        }
                    }
                    else {
                         ereport(ERROR,
                             (errcode(ERRCODE_SYNTAX_ERROR),
                                 errmsg("NOT ROTATE in clause error"),
                                    parser_errposition(pstate, exprLocation((Node*)resTarget->val))));
                    }
                    in_counter++;
                }
                appendStringInfo(&union_all_sql, "\' AS %s,", strVal((Value *)lfirst(forCell)));
            }
        } 
        else {
            if (list_length(stmt->unrotateInfo->forColName) != list_length(unrotateinCell->aliaList)) {
                ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
                    errmsg("the number of elements in alias clause doesn't agree with the number of columns NOT ROTATE_FOR specified")));
            }
            forboth(forCell, stmt->unrotateInfo->forColName, aliasCell, unrotateinCell->aliaList) {
                if (!IsA(lfirst(aliasCell), A_Const)) {
                    ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("NOT ROTATE alias clause error")));
                }
                appendStringInfo(&union_all_sql, "\'%s\' AS %s,", strVal(&((A_Const *)lfirst(aliasCell))->val), strVal((Value *)lfirst(forCell)));
            }
        }
        int counter1 = 0;
        forboth(colCell, stmt->unrotateInfo->colNameList, cell, unrotateinCell->unrotateInExpr) {
            ResTarget *resTarget = (ResTarget *)lfirst(cell);
            if (IsA(resTarget->val, ColumnRef))
            {
                if (counter1 > 0)
                    appendStringInfo(&union_all_sql, ",");
                appendStringInfo(&union_all_sql, " \"%s\" AS %s",
                    strVal((Value *)linitial(((ColumnRef *)resTarget->val)->fields)),
                    strVal((Value *)lfirst(colCell)));
                counter1++;
            }
            else {
                ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("NOT ROTATE in clause error"),
                            parser_errposition(pstate, exprLocation((Node *)resTarget->val))));
            }
        }

        appendStringInfo(&union_all_sql, "%s", from_clause_sql.data);
        in_counter = 0;
        if (stmt->unrotateInfo->includeNull == false) {
            appendStringInfo(&union_all_sql, "where ");
            foreach(cell, unrotateinCell->unrotateInExpr) {
                if (in_counter > 0)
                    appendStringInfo(&union_all_sql, "or ");
                ResTarget *resTarget = (ResTarget *)lfirst(cell);
                if (IsA(resTarget->val, ColumnRef)) {
                    appendStringInfo(&union_all_sql, "%s is not null ",
                        strVal((Value *) linitial(((ColumnRef*) resTarget->val)->fields)));
                }
                else {
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("NOT ROTATE in clause error"),
                            parser_errposition(pstate, exprLocation((Node*)resTarget->val))));
                }
                in_counter++;
            }
        }
        counter++;
    }
    appendStringInfo(&union_all_sql, ";");
    pfree_ext(from_clause_sql.data);

    list_free_ext(stmt->targetList);
    stmt->targetList = list_copy(stmt_targetList);

    list_free_ext(stmt_targetList);
    list_free_ext(aStarList);
    list_free_ext(targetList);

    parsetree_list = pg_parse_query(union_all_sql.data);

    /* rewrite unrotate clause as a subquery */
    RangeSubselect *subselect = makeNode(RangeSubselect);
    Alias *sub_alias;
    if (stmt->unrotateInfo->alias == NULL) {
        sub_alias = makeAlias("unrotate_rewrite", NIL);
    } else {
        sub_alias = (Alias *)copyObject(stmt->unrotateInfo->alias);
    }
    subselect->subquery = (Node *)linitial(parsetree_list);
    subselect->alias = sub_alias;

    list_free_ext(stmt->fromClause);
    stmt->fromClause = list_make1((Node *)subselect);

    list_free_ext(stmt->unrotateInfo->colNameList);
    list_free_ext(stmt->unrotateInfo->forColName);
    list_free_ext(stmt->unrotateInfo->inExprList);
    pfree_ext(stmt->unrotateInfo);

    list_free(pstate->p_ctenamespace);
    pstate->p_ctenamespace = NIL;
    list_free(pstate->p_future_ctes);
    pstate->p_future_ctes = NIL;

    return transformStmt(pstate, (Node *)stmt);
}

/*
 * transformValuesClause -
 *	  transforms a VALUES clause that's being used as a standalone SELECT
 *
 * We build a Query containing a VALUES RTE, rather as if one had written
 *			SELECT * FROM (VALUES ...) AS "*VALUES*"
 */
static Query* transformValuesClause(ParseState* pstate, SelectStmt* stmt)
{
    Query* qry = makeNode(Query);
    List* exprsLists = NIL;
    List* collations = NIL;
    List** colexprs = NULL;
    int sublist_length = -1;
    RangeTblEntry* rte = NULL;
    int rtindex;
    ListCell* lc = NULL;
    ListCell* lc2 = NULL;
    int i;

    qry->commandType = CMD_SELECT;

    /* Most SELECT stuff doesn't apply in a VALUES clause */
    AssertEreport(stmt->distinctClause == NIL, MOD_OPT, "distinctClause should not happen here");

    AssertEreport(stmt->intoClause == NULL, MOD_OPT, "intoClause should not happen here");

    AssertEreport(stmt->targetList == NIL, MOD_OPT, "targetList only accept NIL here");

    AssertEreport(stmt->fromClause == NIL, MOD_OPT, "fromClause should not happen here");

    AssertEreport(stmt->whereClause == NULL, MOD_OPT, "whereClause should not happen here");

    AssertEreport(stmt->groupClause == NIL, MOD_OPT, "groupClause should not happen here");

    AssertEreport(stmt->havingClause == NULL, MOD_OPT, "havingClause should not happen here");

    AssertEreport(stmt->windowClause == NIL, MOD_OPT, "windowClause should not happen here");

    AssertEreport(stmt->op == SETOP_NONE, MOD_OPT, "SetOperation type is inconsistant");

    /* process the WITH clause independently of all else */
    if (stmt->withClause) {
        qry->hasRecursive = stmt->withClause->recursive;
        qry->cteList = transformWithClause(pstate, stmt->withClause);
        qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
    }

    /*
     * For each row of VALUES, transform the raw expressions.  This is also a
     * handy place to reject DEFAULT nodes and Rownum, which the grammar allows for
     * simplicity.
     *
     * Note that the intermediate representation we build is column-organized
     * not row-organized.  That simplifies the type and collation processing
     * below.
     */
    foreach (lc, stmt->valuesLists) {
        List* sublist = (List*)lfirst(lc);

        /* Do basic expression transformation (same as a ROW() expr) */
        sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES);

        /*
         * All the sublists must be the same length, *after* transformation
         * (which might expand '*' into multiple items).  The VALUES RTE can't
         * handle anything different.
         */
        if (sublist_length < 0) {
            /* Remember post-transformation length of first sublist */
            sublist_length = list_length(sublist);
            /* and allocate array for per-column lists */
            colexprs = (List**)palloc0(sublist_length * sizeof(List*));
        } else if (sublist_length != list_length(sublist)) {
            ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                    errmsg("VALUES lists must all be the same length"),
                    parser_errposition(pstate, exprLocation((Node*)sublist))));
        }

        /* Check for DEFAULT and Rownum, then build per-column expression lists */
        i = 0;
        foreach (lc2, sublist) {
            Node* col = (Node*)lfirst(lc2);

            if (IsA(col, SetToDefault)) {
                ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                        errmsg("DEFAULT can only appear in a VALUES list within INSERT"),
                        parser_errposition(pstate, exprLocation(col))));
            }
#ifndef ENABLE_MULTIPLE_NODES
            ExcludeRownumExpr(pstate, col);
#endif
            colexprs[i] = lappend(colexprs[i], col);
            i++;
        }

        /* Release sub-list's cells to save memory */
        list_free_ext(sublist);
    }

    /*
     * Now resolve the common types of the columns, and coerce everything to
     * those types.  Then identify the common collation, if any, of each
     * column.
     *
     * We must do collation processing now because (1) assign_query_collations
     * doesn't process rangetable entries, and (2) we need to label the VALUES
     * RTE with column collations for use in the outer query.  We don't
     * consider conflict of implicit collations to be an error here; instead
     * the column will just show InvalidOid as its collation, and you'll get a
     * failure later if that results in failure to resolve a collation.
     *
     * Note we modify the per-column expression lists in-place.
     */
    collations = NIL;
    for (i = 0; i < sublist_length; i++) {
        Oid coltype;
        Oid colcoll;

        coltype = select_common_type(pstate, colexprs[i], "VALUES", NULL);

        foreach (lc, colexprs[i]) {
            Node* col = (Node*)lfirst(lc);

            col = coerce_to_common_type(pstate, col, coltype, "VALUES");
            lfirst(lc) = (void*)col;
        }

        colcoll = select_common_collation(pstate, colexprs[i], true);

        collations = lappend_oid(collations, colcoll);
    }

    /*
     * Finally, rearrange the coerced expressions into row-organized lists.
     */
    exprsLists = NIL;
    foreach (lc, colexprs[0]) {
        Node* col = (Node*)lfirst(lc);
        List* sublist = NIL;

        sublist = list_make1(col);
        exprsLists = lappend(exprsLists, sublist);
    }
    list_free_ext(colexprs[0]);
    for (i = 1; i < sublist_length; i++) {
        forboth(lc, colexprs[i], lc2, exprsLists)
        {
            Node* col = (Node*)lfirst(lc);
            List* sublist = (List*)lfirst(lc2);

            /* sublist pointer in exprsLists won't need adjustment */
            (void)lappend(sublist, col);
        }
        list_free_ext(colexprs[i]);
    }

    /*
     * Generate the VALUES RTE
     */
    rte = addRangeTableEntryForValues(pstate, exprsLists, collations, NULL, true);
    addRTEtoQuery(pstate, rte, true, true, true);
    /* assume new rte is at end */
    rtindex = list_length(pstate->p_rtable);
    if (rte != rt_fetch(rtindex, pstate->p_rtable)) {
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("rte is not in p_rtable list")));
    }


    /*
     * Generate a targetlist as though expanding "*"
     */
    AssertEreport(pstate->p_next_resno == 1, MOD_OPT, "");
    qry->targetList = expandRelAttrs(pstate, rte, rtindex, 0, -1);

    /*
     * The grammar allows attaching ORDER BY, LIMIT, and FOR UPDATE to a
     * VALUES, so cope.
     */
    qry->sortClause = transformSortClause(
        pstate, stmt->sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, true /* fix unknowns */, false /* allow SQL92 rules */);

    qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset, EXPR_KIND_OFFSET, "OFFSET");
    qry->limitCount = transformLimitClause(pstate, stmt->limitCount, EXPR_KIND_LIMIT, "LIMIT");

    if (stmt->lockingClause) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
    }

    /*
     * There mustn't have been any table references in the expressions, else
     * strange things would happen, like Cartesian products of those tables
     * with the VALUES list.  We have to check this after parsing ORDER BY et
     * al since those could insert more junk.
     */
    if (list_length(pstate->p_joinlist) != 1) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("VALUES must not contain table references"),
                parser_errposition(pstate, locate_var_of_level((Node*)exprsLists, 0))));
    }

    if (list_length(pstate->p_rtable) != 1 && contain_vars_of_level((Node*)exprsLists, 0)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("VALUES must not contain OLD or NEW references"),
                errhint("Use SELECT ... UNION ALL ... instead."),
                parser_errposition(pstate, locate_var_of_level((Node*)exprsLists, 0))));
    }

    qry->rtable = pstate->p_rtable;
    qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);

    qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
    qry->hasSubLinks = pstate->p_hasSubLinks;
    /* aggregates not allowed (but subselects are okay) */
    if (pstate->p_hasAggs) {
        ereport(ERROR,
            (errcode(ERRCODE_GROUPING_ERROR),
                errmsg("cannot use aggregate function in VALUES"),
                parser_errposition(pstate, locate_agg_of_level((Node*)exprsLists, 0))));
    }
    if (pstate->p_hasWindowFuncs) {
        ereport(ERROR,
            (errcode(ERRCODE_WINDOWING_ERROR),
                errmsg("cannot use window function in VALUES"),
                parser_errposition(pstate, locate_windowfunc((Node*)exprsLists))));
    }

    assign_query_collations(pstate, qry);

    return qry;
}

/*
 * transformSetOperationStmt -
 *	  transforms a set-operations tree
 *
 * A set-operation tree is just a SELECT, but with UNION/INTERSECT/EXCEPT
 * structure to it.  We must transform each leaf SELECT and build up a top-
 * level Query that contains the leaf SELECTs as subqueries in its rangetable.
 * The tree of set operations is converted into the setOperations field of
 * the top-level Query.
 */
static Query* transformSetOperationStmt(ParseState* pstate, SelectStmt* stmt)
{
    Query* qry = makeNode(Query);
    SelectStmt* leftmostSelect = NULL;
    int leftmostRTI;
    Query* leftmostQuery = NULL;
    SetOperationStmt* sostmt = NULL;
    List* sortClause = NIL;
    Node* limitOffset = NULL;
    Node* limitCount = NULL;
    List* lockingClause = NIL;
    WithClause* withClause = NULL;
    Node* node = NULL;
    ListCell* left_tlist = NULL;
    ListCell* lct = NULL;
    ListCell* lcm = NULL;
    ListCell* lcc = NULL;
    ListCell* l = NULL;
    List* targetvars = NIL;
    List* targetnames = NIL;
    List* sv_relnamespace = NIL;
    List* sv_varnamespace = NIL;
    int sv_rtable_length;
    RangeTblEntry* jrte = NULL;
    int tllen;

    qry->commandType = CMD_SELECT;

    /*
     * Find leftmost leaf SelectStmt.  We currently only need to do this in
     * order to deliver a suitable error message if there's an INTO clause
     * there, implying the set-op tree is in a context that doesn't allow
     * INTO.  (transformSetOperationTree would throw error anyway, but it
     * seems worth the trouble to throw a different error for non-leftmost
     * INTO, so we produce that error in transformSetOperationTree.)
     */
    leftmostSelect = stmt->larg;
    while (leftmostSelect != NULL && leftmostSelect->op != SETOP_NONE) {
        leftmostSelect = leftmostSelect->larg;
    }
    AssertEreport(leftmostSelect && IsA(leftmostSelect, SelectStmt) && leftmostSelect->larg == NULL, MOD_OPT, "");
    if (leftmostSelect->intoClause) {
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("SELECT ... INTO is not allowed here"),
                parser_errposition(pstate, exprLocation((Node*)leftmostSelect->intoClause))));
    }

    /*
     * We need to extract ORDER BY and other top-level clauses here and not
     * let transformSetOperationTree() see them --- else it'll just recurse
     * right back here!
     */
    sortClause = stmt->sortClause;
    limitOffset = stmt->limitOffset;
    limitCount = stmt->limitCount;
    lockingClause = stmt->lockingClause;
    withClause = stmt->withClause;

    stmt->sortClause = NIL;
    stmt->limitOffset = NULL;
    stmt->limitCount = NULL;
    stmt->lockingClause = NIL;
    stmt->withClause = NULL;

    /* We don't support FOR UPDATE/SHARE with set ops at the moment. */
    if (lockingClause != NULL) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
    }

    /* Process the WITH clause independently of all else */
    if (withClause != NULL) {
        qry->hasRecursive = withClause->recursive;
        qry->cteList = transformWithClause(pstate, withClause);
        qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
    }

    /*
     * Recursively transform the components of the tree.
     */
    sostmt = (SetOperationStmt*)transformSetOperationTree(pstate, stmt, true, NULL);
    AssertEreport(
        sostmt && IsA(sostmt, SetOperationStmt), MOD_OPT, "Recursively transform the components of the tree failure");
    qry->setOperations = (Node*)sostmt;

    /*
     * Re-find leftmost SELECT (now it's a sub-query in rangetable)
     */
    node = sostmt->larg;
    while (node && IsA(node, SetOperationStmt))
        node = ((SetOperationStmt*)node)->larg;
    AssertEreport(node && IsA(node, RangeTblRef), MOD_OPT, "Node type inconsistant");
    leftmostRTI = ((RangeTblRef*)node)->rtindex;
    leftmostQuery = rt_fetch(leftmostRTI, pstate->p_rtable)->subquery;
    AssertEreport(leftmostQuery != NULL, MOD_OPT, "para should not be NULL");
    /*
     * Generate dummy targetlist for outer query using column names of
     * leftmost select and common datatypes/collations of topmost set
     * operation.  Also make lists of the dummy vars and their names for use
     * in parsing ORDER BY.
     *
     * Note: we use leftmostRTI as the varno of the dummy variables. It
     * shouldn't matter too much which RT index they have, as long as they
     * have one that corresponds to a real RT entry; else funny things may
     * happen when the tree is mashed by rule rewriting.
     */
    qry->targetList = NIL;
    targetvars = NIL;
    targetnames = NIL;
    left_tlist = list_head(leftmostQuery->targetList);

    forthree(lct, sostmt->colTypes, lcm, sostmt->colTypmods, lcc, sostmt->colCollations)
    {
        Oid colType = lfirst_oid(lct);
        int32 colTypmod = lfirst_int(lcm);
        Oid colCollation = lfirst_oid(lcc);
        TargetEntry* lefttle = (TargetEntry*)lfirst(left_tlist);
        char* colName = NULL;
        TargetEntry* tle = NULL;
        Var* var = NULL;

        AssertEreport(!lefttle->resjunk, MOD_OPT, "");
        colName = pstrdup(lefttle->resname);
        var = makeVar(leftmostRTI, lefttle->resno, colType, colTypmod, colCollation, 0);
        var->location = exprLocation((Node*)lefttle->expr);
        tle = makeTargetEntry((Expr*)var, (AttrNumber)pstate->p_next_resno++, colName, false);
        qry->targetList = lappend(qry->targetList, tle);
        targetvars = lappend(targetvars, var);
        targetnames = lappend(targetnames, makeString(colName));
        left_tlist = lnext(left_tlist);
    }

    /*
     * As a first step towards supporting sort clauses that are expressions
     * using the output columns, generate a varnamespace entry that makes the
     * output columns visible.	A Join RTE node is handy for this, since we
     * can easily control the Vars generated upon matches.
     *
     * Note: we don't yet do anything useful with such cases, but at least
     * "ORDER BY upper(foo)" will draw the right error message rather than
     * "foo not found".
     */
    sv_rtable_length = list_length(pstate->p_rtable);

    jrte = addRangeTableEntryForJoin(pstate, targetnames, JOIN_INNER, targetvars, NULL, false);

    sv_relnamespace = pstate->p_relnamespace;

    sv_varnamespace = pstate->p_varnamespace;
    pstate->p_relnamespace = NIL;
    pstate->p_varnamespace = NIL;

    /* add jrte to varnamespace only */
    addRTEtoQuery(pstate, jrte, false, false, true);

    /*
     * For now, we don't support resjunk sort clauses on the output of a
     * setOperation tree --- you can only use the SQL92-spec options of
     * selecting an output column by name or number.  Enforce by checking that
     * transformSortClause doesn't add any items to tlist.
     */
    tllen = list_length(qry->targetList);

    qry->sortClause = transformSortClause(
        pstate, sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, false /* no unknowns expected */, false /* allow SQL92 rules */);

    pstate->p_rtable = list_truncate(pstate->p_rtable, sv_rtable_length);
    pstate->p_relnamespace = sv_relnamespace;
    pstate->p_varnamespace = sv_varnamespace;

    if (tllen != list_length(qry->targetList)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("invalid UNION/INTERSECT/EXCEPT ORDER BY clause"),
                errdetail("Only result column names can be used, not expressions or functions."),
                errhint("Add the expression/function to every SELECT, or move the UNION into a FROM clause."),
                parser_errposition(pstate, exprLocation((const Node*)list_nth(qry->targetList, tllen)))));
    }

    qry->limitOffset = transformLimitClause(pstate, limitOffset, EXPR_KIND_OFFSET, "OFFSET");
    qry->limitCount = transformLimitClause(pstate, limitCount, EXPR_KIND_LIMIT, "LIMIT");

    qry->rtable = pstate->p_rtable;
    qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);

    qry->hasSubLinks = pstate->p_hasSubLinks;
    qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
    if (pstate->p_hasWindowFuncs) {
        parseCheckWindowFuncs(pstate, qry);
    }
    qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
    qry->hasAggs = pstate->p_hasAggs;

    foreach (l, lockingClause) {
        transformLockingClause(pstate, qry, (LockingClause*)lfirst(l), false);
    }

    assign_query_collations(pstate, qry);

    /* this must be done after collations, for reliable comparison of exprs */
    if (pstate->p_hasAggs || qry->groupClause || qry->groupingSets || qry->havingQual) {
        parseCheckAggregates(pstate, qry);
    }

    return qry;
}

/* Find real target entry and convert its character set in UNION tree. */
static void convert_set_operation_tree_charset(ParseState* pstate, Node* op_tree,
    int arg_id, Oid target_collation, int target_charset)
{
    if (IsA(op_tree, SetOperationStmt)) {
        SetOperationStmt* opstmt = (SetOperationStmt*)op_tree;
        convert_set_operation_tree_charset(pstate, opstmt->larg, arg_id, target_collation, target_charset);
        convert_set_operation_tree_charset(pstate, opstmt->rarg, arg_id, target_collation, target_charset);
        return;
    }

    Assert(IsA(op_tree, RangeTblRef));
    RangeTblRef* rtr = (RangeTblRef*)op_tree;
    RangeTblEntry* rte = rt_fetch(rtr->rtindex, pstate->p_rtable);
    TargetEntry* te = NULL;
    int n = 0;

    foreach_cell(tmp_cell, rte->subquery->targetList) {
        te = (TargetEntry*)lfirst(tmp_cell);
        if (te->resjunk) {
            continue;
        }
        if (++n > arg_id) {
            break;
        }
    }

    te->expr = (Expr*)coerce_to_target_charset(
        (Node*)te->expr, target_charset, exprType((Node*)te->expr), exprTypmod((Node*)te->expr), target_collation);
}

/* Converts the character set of the query column on a side of the UNION. */
static void convert_set_operation_charset(ParseState* pstate, Node* op_tree, TargetEntry* arg,
    int arg_id, Oid target_collation, int target_charset)
{
    Node* arg_expr = (Node*)arg->expr;
    Oid expr_type = exprType(arg_expr);
    Oid expr_collation = exprCollation(arg_expr);
    if (expr_collation == BINARY_COLLATION_OID || expr_collation == target_collation) {
        return;
    }

    if (!IsA(arg->expr, SetToDefault)) {
        arg->expr = (Expr*)coerce_to_target_charset(
            arg_expr, target_charset, expr_type, exprTypmod(arg_expr), target_collation);
        return;
    }

    Assert(IsA(op_tree, SetOperationStmt));
    convert_set_operation_tree_charset(pstate, op_tree, arg_id, target_collation, target_charset);
}

/*
 * transformSetOperationTree
 *		Recursively transform leaves and internal nodes of a set-op tree
 *
 * In addition to returning the transformed node, if targetlist isn't NULL
 * then we return a list of its non-resjunk TargetEntry nodes.	For a leaf
 * set-op node these are the actual targetlist entries; otherwise they are
 * dummy entries created to carry the type, typmod, collation, and location
 * (for error messages) of each output column of the set-op node.  This info
 * is needed only during the internal recursion of this function, so outside
 * callers pass NULL for targetlist.  Note: the reason for passing the
 * actual targetlist entries of a leaf node is so that upper levels can
 * replace UNKNOWN Consts with properly-coerced constants.
 */
static Node* transformSetOperationTree(ParseState* pstate, SelectStmt* stmt, bool isTopLevel, List** targetlist)
{
    bool isLeaf = false;
    int rc = 0;

    AssertEreport(stmt && IsA(stmt, SelectStmt), MOD_OPT, "check stmt failure");

    /* Guard against stack overflow due to overly complex set-expressions */
    check_stack_depth();

    /*
     * Validity-check both leaf and internal SELECTs for disallowed ops.
     */
    if (stmt->intoClause) {
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"),
                parser_errposition(pstate, exprLocation((Node*)stmt->intoClause))));
    }

    /* We don't support FOR UPDATE/SHARE with set ops at the moment. */
    if (stmt->lockingClause) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
    }

    /*
     * If an internal node of a set-op tree has ORDER BY, LIMIT, FOR UPDATE,
     * or WITH clauses attached, we need to treat it like a leaf node to
     * generate an independent sub-Query tree.  Otherwise, it can be
     * represented by a SetOperationStmt node underneath the parent Query.
     */
    if (stmt->op == SETOP_NONE) {
        AssertEreport(stmt->larg == NULL && stmt->rarg == NULL, MOD_OPT, "stmt is inconsistant");
        isLeaf = true;
    } else {
        AssertEreport(stmt->larg != NULL && stmt->rarg != NULL, MOD_OPT, "");
        if (stmt->sortClause || stmt->limitOffset || stmt->limitCount || stmt->lockingClause || stmt->withClause) {
            isLeaf = true;
        } else {
            isLeaf = false;
        }
    }

    if (isLeaf) {
        /* Process leaf SELECT */
        Query* selectQuery = NULL;
        char selectName[32];
        RangeTblEntry PG_USED_FOR_ASSERTS_ONLY* rte = NULL;
        RangeTblRef* rtr = NULL;
        ListCell* tl = NULL;

        /*
         * Transform SelectStmt into a Query.
         *
         * Note: previously transformed sub-queries don't affect the parsing
         * of this sub-query, because they are not in the toplevel pstate's
         * namespace list.
         */
        selectQuery = parse_sub_analyze((Node*)stmt, pstate, NULL, false, false);

        /*
         * Check for bogus references to Vars on the current query level (but
         * upper-level references are okay). Normally this can't happen
         * because the namespace will be empty, but it could happen if we are
         * inside a rule.
         */
        if (pstate->p_relnamespace || pstate->p_varnamespace) {
            if (contain_vars_of_level((Node*)selectQuery, 1)) {
                ereport(ERROR,
                    (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                        errmsg("UNION/INTERSECT/EXCEPT member statement cannot refer to other relations of same query "
                               "level"),
                        parser_errposition(pstate, locate_var_of_level((Node*)selectQuery, 1))));
            }
        }

        /*
         * Extract a list of the non-junk TLEs for upper-level processing.
         */
        if (targetlist != NULL) {
            *targetlist = NIL;
            foreach (tl, selectQuery->targetList) {
                TargetEntry* tle = (TargetEntry*)lfirst(tl);

                if (!tle->resjunk) {
                    *targetlist = lappend(*targetlist, tle);
                }
            }
        }

        /*
         * Make the leaf query be a subquery in the top-level rangetable.
         */
        rc = snprintf_s(
            selectName, sizeof(selectName), sizeof(selectName) - 1, "*SELECT* %d", list_length(pstate->p_rtable) + 1);
        securec_check_ss(rc, "", "");
        rte = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias(selectName, NIL), false, false);

        /*
         * Return a RangeTblRef to replace the SelectStmt in the set-op tree.
         */
        rtr = makeNode(RangeTblRef);
        /* assume new rte is at end */
        rtr->rtindex = list_length(pstate->p_rtable);
        AssertEreport(rte == rt_fetch(rtr->rtindex, pstate->p_rtable), MOD_OPT, "check with rt_fetch failure");
        return (Node*)rtr;
    } else {
        /* Process an internal node (set operation node) */
        SetOperationStmt* op = makeNode(SetOperationStmt);
        List* ltargetlist = NIL;
        List* rtargetlist = NIL;
        ListCell* ltl = NULL;
        ListCell* rtl = NULL;
        const char* context = NULL;
        int col_id = 0;

        context = (stmt->op == SETOP_UNION ? "UNION" : (stmt->op == SETOP_INTERSECT ? "INTERSECT" : "EXCEPT"));

        op->op = stmt->op;
        op->all = stmt->all;

        /*
         * Recursively transform the left child node.
         */
        op->larg = transformSetOperationTree(pstate, stmt->larg, false, &ltargetlist);

        /*
         * If we are processing a recursive union query, now is the time to
         * examine the non-recursive term's output columns and mark the
         * containing CTE as having those result columns.  We should do this
         * only at the topmost setop of the CTE, of course.
         */
        if (isTopLevel && pstate->p_parent_cte && pstate->p_parent_cte->cterecursive) {
            determineRecursiveColTypes(pstate, op->larg, ltargetlist);
        }

        /*
         * Recursively transform the right child node.
         */
        op->rarg = transformSetOperationTree(pstate, stmt->rarg, false, &rtargetlist);

        /*
         * Verify that the two children have the same number of non-junk
         * columns, and determine the types of the merged output columns.
         */
        if (list_length(ltargetlist) != list_length(rtargetlist)) {
            ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                    errmsg("each %s query must have the same number of columns", context),
                    parser_errposition(pstate, exprLocation((Node*)rtargetlist))));
        }

        if (targetlist != NULL) {
            *targetlist = NIL;
        }
        op->colTypes = NIL;
        op->colTypmods = NIL;
        op->colCollations = NIL;
        op->groupClauses = NIL;
        forboth(ltl, ltargetlist, rtl, rtargetlist)
        {
            TargetEntry* ltle = (TargetEntry*)lfirst(ltl);
            TargetEntry* rtle = (TargetEntry*)lfirst(rtl);
            Node* lcolnode = (Node*)ltle->expr;
            Node* rcolnode = (Node*)rtle->expr;
            Oid lcoltype = exprType(lcolnode);
            Oid rcoltype = exprType(rcolnode);
            int32 lcoltypmod = exprTypmod(lcolnode);
            int32 rcoltypmod = exprTypmod(rcolnode);
            Node* bestexpr = NULL;
            int bestlocation;
            Oid rescoltype;
            int32 rescoltypmod;
            Oid rescolcoll;

            if (IsA(lcolnode, SetToDefault) && ((SetToDefault*)lcolnode)->lrchild_unknown && rcoltype != UNKNOWNOID) {
                /* the type of lcolnode's l&r child must be unknown or NULL, and lcoltype must be text. so allow text cover to other type */
                rescoltype = rcoltype;
                bestexpr = rcolnode;
            } else if (IsA(rcolnode, SetToDefault) && ((SetToDefault*)rcolnode)->lrchild_unknown && lcoltype != UNKNOWNOID) {
                rescoltype = lcoltype;
                bestexpr = lcolnode;
            } else {
                /* select common type, same as CASE et al */
                rescoltype = select_common_type(pstate, list_make2(lcolnode, rcolnode), context, &bestexpr);
            }
            bestlocation = exprLocation(bestexpr);
            /* if same type and same typmod, use typmod; else default */
            if (lcoltype == rcoltype && lcoltypmod == rcoltypmod) {
                rescoltypmod = lcoltypmod;
            } else if (u_sess->attr.attr_sql.sql_compatibility == A_FORMAT && lcoltype == rcoltype && \
                    !(lcoltypmod == -1 || rcoltypmod == -1)) {
                /* same type, neither typmod is -1, select max */
                rescoltypmod = Max(lcoltypmod, rcoltypmod);
            } else {
                rescoltypmod = -1;
            }

            /*
             * Verify the coercions are actually possible.	If not, we'd fail
             * later anyway, but we want to fail now while we have sufficient
             * context to produce an error cursor position.
             *
             * For all non-UNKNOWN-type cases, we verify coercibility but we
             * don't modify the child's expression, for fear of changing the
             * child query's semantics.
             *
             * If a child expression is an UNKNOWN-type Const or Param, we
             * want to replace it with the coerced expression.	This can only
             * happen when the child is a leaf set-op node.  It's safe to
             * replace the expression because if the child query's semantics
             * depended on the type of this output column, it'd have already
             * coerced the UNKNOWN to something else.  We want to do this
             * because (a) we want to verify that a Const is valid for the
             * target type, or resolve the actual type of an UNKNOWN Param,
             * and (b) we want to avoid unnecessary discrepancies between the
             * output type of the child query and the resolved target type.
             * Such a discrepancy would disable optimization in the planner.
             *
             * If it's some other UNKNOWN-type node, eg a Var, we do nothing
             * (knowing that coerce_to_common_type would fail).  The planner
             * is sometimes able to fold an UNKNOWN Var to a constant before
             * it has to coerce the type, so failing now would just break
             * cases that might work.
             */
            if (lcoltype != UNKNOWNOID) {
                lcolnode = coerce_to_common_type(pstate, lcolnode, rescoltype, context);
            } else if (IsA(lcolnode, Const) || IsA(lcolnode, Param)) {
                lcolnode = coerce_to_common_type(pstate, lcolnode, rescoltype, context);
                ltle->expr = (Expr*)lcolnode;
            }

            if (rcoltype != UNKNOWNOID) {
                rcolnode = coerce_to_common_type(pstate, rcolnode, rescoltype, context);
            } else if (IsA(rcolnode, Const) || IsA(rcolnode, Param)) {
                rcolnode = coerce_to_common_type(pstate, rcolnode, rescoltype, context);
                rtle->expr = (Expr*)rcolnode;
            }

            /*
             * Select common collation.  A common collation is required for
             * all set operators except UNION ALL; see SQL:2008 7.13 <query
             * expression> Syntax Rule 15c.  (If we fail to identify a common
             * collation for a UNION ALL column, the curCollations element
             * will be set to InvalidOid, which may result in a runtime error
             * if something at a higher query level wants to use the column's
             * collation.)
             */
            rescolcoll =
                select_common_collation(pstate, list_make2(lcolnode, rcolnode), (op->op == SETOP_UNION && op->all));
            if (ENABLE_MULTI_CHARSET && rescolcoll != BINARY_COLLATION_OID) {
                int res_charset = get_valid_charset_by_collation(rescolcoll);
                convert_set_operation_charset(pstate, op->larg, ltle, col_id, rescolcoll, res_charset);
                convert_set_operation_charset(pstate, op->rarg, rtle, col_id, rescolcoll, res_charset);
            }
            col_id++;
            
            /* emit results */
            op->colTypes = lappend_oid(op->colTypes, rescoltype);
            op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
            op->colCollations = lappend_oid(op->colCollations, rescolcoll);

            /*
             * For all cases except UNION ALL, identify the grouping operators
             * (and, if available, sorting operators) that will be used to
             * eliminate duplicates.
             */
            if (op->op != SETOP_UNION || !op->all) {
                SortGroupClause* grpcl = makeNode(SortGroupClause);
                Oid sortop;
                Oid eqop;
                bool hashable = false;
                ParseCallbackState pcbstate;

                setup_parser_errposition_callback(&pcbstate, pstate, bestlocation);

                /* determine the eqop and optional sortop */
                get_sort_group_operators(rescoltype, false, true, false, &sortop, &eqop, NULL, &hashable);

                cancel_parser_errposition_callback(&pcbstate);

                /* we don't have a tlist yet, so can't assign sortgrouprefs */
                grpcl->tleSortGroupRef = 0;
                grpcl->eqop = eqop;
                grpcl->sortop = sortop;
                grpcl->nulls_first = false; /* OK with or without sortop */
                grpcl->hashable = hashable;

                op->groupClauses = lappend(op->groupClauses, grpcl);
            }

            /*
             * Construct a dummy tlist entry to return.  We use a SetToDefault
             * node for the expression, since it carries exactly the fields
             * needed, but any other expression node type would do as well.
             */
            if (targetlist != NULL) {
                SetToDefault* rescolnode = makeNode(SetToDefault);
                TargetEntry* restle = NULL;

                rescolnode->typeId = rescoltype;
                rescolnode->typeMod = rescoltypmod;
                rescolnode->collation = rescolcoll;
                rescolnode->location = bestlocation;
                rescolnode->lrchild_unknown = false;
                if ((lcoltype == UNKNOWNOID || (IsA(ltle->expr, SetToDefault) && ((SetToDefault*)ltle->expr)->lrchild_unknown))
                    && (rcoltype == UNKNOWNOID || (IsA(rtle->expr, SetToDefault) && ((SetToDefault*)rtle->expr)->lrchild_unknown))) {
                    rescolnode->lrchild_unknown = true;
                }
                restle = makeTargetEntry((Expr*)rescolnode,
                    0, /* no need to set resno */
                    NULL,
                    false);
                *targetlist = lappend(*targetlist, restle);
            }
        }

        return (Node*)op;
    }
}

/*
 * Process the outputs of the non-recursive term of a recursive union
 * to set up the parent CTE's columns
 */
static void determineRecursiveColTypes(ParseState* pstate, Node* larg, List* nrtargetlist)
{
    Node* node = NULL;
    int leftmostRTI;
    Query* leftmostQuery = NULL;
    List* targetList = NIL;
    ListCell* left_tlist = NULL;
    ListCell* nrtl = NULL;
    int next_resno;

    /*
     * Find leftmost leaf SELECT
     */
    node = larg;
    while (node && IsA(node, SetOperationStmt)) {
        node = ((SetOperationStmt*)node)->larg;
    }
    AssertEreport(node && IsA(node, RangeTblRef), MOD_OPT, "check node type inconsistant");
    leftmostRTI = ((RangeTblRef*)node)->rtindex;
    leftmostQuery = rt_fetch(leftmostRTI, pstate->p_rtable)->subquery;
    if (unlikely(leftmostQuery == NULL)) {
        ereport(ERROR,
            (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), 
                errmsg("leftmostQuery should not be null")));
    }

    /*
     * Generate dummy targetlist using column names of leftmost select and
     * dummy result expressions of the non-recursive term.
     */
    targetList = NIL;
    left_tlist = list_head(leftmostQuery->targetList);
    next_resno = 1;

    foreach (nrtl, nrtargetlist) {
        TargetEntry* nrtle = (TargetEntry*)lfirst(nrtl);
        TargetEntry* lefttle = (TargetEntry*)lfirst(left_tlist);
        char* colName = NULL;
        TargetEntry* tle = NULL;

        AssertEreport(!lefttle->resjunk, MOD_OPT, "");
        colName = pstrdup(lefttle->resname);
        tle = makeTargetEntry(nrtle->expr, next_resno++, colName, false);
        targetList = lappend(targetList, tle);
        left_tlist = lnext(left_tlist);
    }

    /* Now build CTE's output column info using dummy targetlist */
    analyzeCTETargetList(pstate, pstate->p_parent_cte, targetList);
}

/*
 *  fixResTargetNameWithTableNameRef
 *  The goal of this function try to handle set_clause in updatestmt, and update
 *  with table name or alias prefix. Do this for compatibility A.
 */
void fixResTargetNameWithTableNameRef(Relation rd, RangeVar* rel, ResTarget* res)
{
    char* resname = NULL;
    AttrNumber attrnoIndName = InvalidAttrNumber;
    AttrNumber attrnoResName = InvalidAttrNumber;
    Oid attrtypeid = InvalidOid;
    Oid typrelid = InvalidOid;

    /* the length of indireciton can't less than 1 and the first item type must not be A_Indices */
    if ((list_length(res->indirection) < 1) || IsA(linitial(res->indirection), A_Indices)) {
        return;
    }

    /* name must relname or aliasname */
    if ((strcmp(rel->relname, res->name) != 0) &&
        ((rel->alias == NULL) || (strcmp((rel->alias)->aliasname, res->name) != 0))) {
        return;
    }

    /*
     * if meet set_clause like a.b.c = 1 then the indirection will exceed one,
     * so treat first indirection as tablename/alisename like a in this ex.
     */
    resname = pstrdup(strVal(linitial(res->indirection)));
    if (list_length(res->indirection) > 1) {
        res->name = resname;
        res->indirection = RemoveListCell(res->indirection, 1);
        return;
    }

    /*
     * Here only meet set_clause which has two elements like a.b or test.b.
     * So first keep forward-compatibility behavior of old version, that results
     * if a is a composite type and also relname/alisename then we first handle it as
     * composite type.
     * Otherwise we handle it like a.b like relname/alisename, then a is a relname just
     * update set_clause over.
     */
    attrnoResName = attnameAttNum(rd, res->name, true);
    attrnoIndName = attnameAttNum(rd, resname, true);

    if (attrnoResName > 0) {
        attrtypeid = attnumTypeId(rd, attrnoResName);
        if (attrtypeid) {
            typrelid = typeidTypeRelid(attrtypeid);
            if (typrelid && ((get_attnum(typrelid, resname) != InvalidAttrNumber) || (attrnoIndName <= 0))) {
                ereport(
                    NOTICE, (errmsg("update field '%s' of column '%s', though it's ambiguous.", resname, res->name)));
                return;
            }
        }
    }

    res->name = resname;
    res->indirection = RemoveListCell(res->indirection, 1);
}

/*
 * fixResTargetListWithTableNameRef
 * 	  transforms an update set_clause_list with tablename or alias prefix
 */
void fixResTargetListWithTableNameRef(Relation rd, RangeVar* rel, List* clause_list)
{
    ListCell* cell = NULL;

    foreach (cell, clause_list) {
        if (IsA(lfirst(cell), ResTarget)) {
            fixResTargetNameWithTableNameRef(rd, rel, (ResTarget*)lfirst(cell));
        }
    }
}

/* Merge the targetlists of multiple identical result relations into the one and change their rtindex. */
static void MergeTargetList(List** targetLists, RangeTblEntry* rte1, int rtindex1,
                            RangeTblEntry* rte2, int rtindex2)
{
    ListCell* l = NULL;

    foreach (l, targetLists[rtindex2 - 1]) {
        TargetEntry* tle = (TargetEntry*)lfirst(l);
        tle->rtindex = (Index)rtindex1;
        rte1->updatedCols = bms_add_member(rte1->updatedCols, tle->resno - FirstLowInvalidHeapAttributeNumber);
        rte2->updatedCols = bms_del_member(rte2->updatedCols, tle->resno - FirstLowInvalidHeapAttributeNumber);
    }
    targetLists[rtindex1 - 1] = list_concat(targetLists[rtindex1 - 1], targetLists[rtindex2 - 1]);
    targetLists[rtindex2 - 1] = NULL;
}

static void transformMultiTargetList(List* target_rangetblentry, List** targetLists, List* result_relations)
{
    if (list_length(target_rangetblentry) <= 1) {
        return;
    }

    ListCell *l1 = NULL;
    ListCell *l2 = NULL;
    forboth (l1, target_rangetblentry, l2, result_relations) {
        RangeTblEntry* rte1 = (RangeTblEntry*)lfirst(l1);
        int rtindex1 = lfirst_int(l2);
        ListCell *l3 = lnext(l1);
        ListCell *l4 = lnext(l2);

        while (l3 && l4) {
            RangeTblEntry* rte2 = (RangeTblEntry*)lfirst(l3);
            int rtindex2 = lfirst_int(l4);
            if (rte2->relid == rte1->relid) {
                MergeTargetList(targetLists, rte1, rtindex1, rte2, rtindex2);
            }
            l3 = lnext(l3);
            l4 = lnext(l4);
        }
    }
}

/*
 * If a relation has no column updated in the resultRelations, it is redundant
 * and remove it from the resultRelations.
 */
static List* remove_update_redundant_relation(List* resultRelations, List* target_rangetblentry)
{
    if (list_length(resultRelations) == 1) {
        return resultRelations;
    }
    RangeTblEntry* target_rte;
    ListCell* rte = list_head(target_rangetblentry);
    ListCell* res = list_head(resultRelations);
    ListCell* res_pre = NULL;
    ListCell* res_next = NULL;

    while (res != NULL) {
        res_next = lnext(res);
        target_rte = (RangeTblEntry*)lfirst(rte);
        if (bms_is_empty(target_rte->updatedCols)) {
            resultRelations = list_delete_cell(resultRelations, res, res_pre);
        } else {
            res_pre = res;
        }

        res = res_next;
        rte = lnext(rte);
    }
    return resultRelations;
}

static void CheckUpdateRelation(Relation targetrel)
{
	// check column store relation distributed by replication
    if (RelationIsColStore(targetrel) &&
        targetrel->rd_locator_info &&
        IsLocatorReplicated(targetrel->rd_locator_info->locatorType)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("replicated columnar table doesn't allow UPDATE")));
    }

#ifdef ENABLE_MULTIPLE_NODES
    if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
#endif
        if (RelationIsMatview(targetrel)) {
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                            errmsg("Unsupported feature"),
                            errdetail("Materialized view doesn't allow UPDATE")));
        }
#ifdef ENABLE_MULTIPLE_NODES
    }
#endif

    // check update permission
    if (((unsigned int)RelationGetInternalMask(targetrel) & INTERNAL_MASK_DUPDATE)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("internal relation doesn't allow UPDATE")));
    }
#ifdef ENABLE_MULTIPLE_NODES
    // check if the target relation is being redistributed in read only mode
    if (!u_sess->attr.attr_sql.enable_cluster_resize && targetrel != NULL &&
        RelationInClusterResizingWriteErrorMode(targetrel)) {
        ereport(ERROR,
            (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
                errmsg("%s is redistributing, please retry later.", targetrel->rd_rel->relname.data)));
    }
#endif
}

void UpdateParseCheck(ParseState *pstate, Node *qry)
{
    /*
     * Top-level aggregates are simply disallowed in UPDATE, per spec. (From
     * an implementation point of view, this is forced because the implicit
     * ctid reference would otherwise be an ungrouped variable.)
     */
    if (pstate->p_hasAggs) {
        ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("cannot use aggregate function in UPDATE"),
                        parser_errposition(pstate, locate_agg_of_level(qry, 0))));
    }
    if (pstate->p_hasWindowFuncs) {
        ereport(ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("cannot use window function in UPDATE"),
                        parser_errposition(pstate, locate_windowfunc(qry))));
    }
}

/*
 * transformUpdateStmt -
 *	  transforms an update statement
 */
static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt)
{
    Query* qry = makeNode(Query);
    Node* qual = NULL;
    ParseNamespaceItem *nsitem = NULL;
    qry->commandType = CMD_UPDATE;
    pstate->p_is_insert = false;
    ListCell* l;
    pstate->p_has_ignore = stmt->hasIgnore;

    /* set io state for backend status for the thread, we will use it to check user space */
    pgstat_set_io_state(IOSTATE_READ);

    /* process the WITH clause independently of all else */
    if (stmt->withClause) {
#ifdef PGXC
        SendCommandIdForInsertCte(qry, stmt->withClause);
#endif
        qry->hasRecursive = stmt->withClause->recursive;
        qry->cteList = transformWithClause(pstate, stmt->withClause);
        qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
    }

    if (list_length(stmt->relationClause) > 1 || !IsA(linitial(stmt->relationClause), RangeVar)) {
        /* add all relations from relationClause to resultRelations. */
        transformFromClause(pstate, stmt->relationClause, true, false, true);
        qry->resultRelations = pstate->p_updateRelations;
    } else {
        qry->resultRelations = setTargetTables(pstate, stmt->relationClause, true, true, ACL_UPDATE);
    }

    /* non-supported IGNORE cases */
    Relation rel = ((Relation)linitial2(pstate->p_target_relation));
    if (pstate->p_has_ignore && rel != NULL) {
        if (RelationIsColumnFormat(rel)) {
            ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             errmsg("IGNORE is not supported on UPDATE column orientated table."))));
        }
    }

    /* subqueries in FROM cannot access the result relation */
    int nsitem_count = list_length(pstate->p_varnamespace);
    foreach(l, pstate->p_varnamespace) {
        nsitem = (ParseNamespaceItem *)lfirst(l);
        nsitem->p_lateral_only = true;
        nsitem->p_lateral_ok = false;
    }

    CheckUDRelations(pstate, stmt->sortClause, stmt->limitClause, stmt->returningList, false);
    /*
     * the FROM clause is non-standard SQL syntax. We used to be able to do
     * this with REPLACE in POSTQUEL so we keep the feature.
     */
    transformFromClause(pstate, stmt->fromClause);

    qry->targetList = transformTargetList(pstate, stmt->targetList, EXPR_KIND_UPDATE_TARGET);
    qual = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE");

    /* remaining clauses can reference the result relation normally */
    foreach (l, pstate->p_varnamespace) {
        if (nsitem_count == 0) {
            break;
        }
        nsitem = (ParseNamespaceItem *)lfirst(l);
        nsitem->p_lateral_only = false;
        nsitem->p_lateral_ok = true;
        nsitem_count--;
    }

    /*
     * Now we are done with SELECT-like processing, and can get on with
     * transforming the target list to match the UPDATE target columns.
     */
    qry->targetList = transformUpdateTargetList(pstate, qry->targetList, stmt->targetList, qry->resultRelations);
    transformLimitSortClause(pstate, stmt, qry, false);

    qry->resultRelations = remove_update_redundant_relation(qry->resultRelations, pstate->p_target_rangetblentry);

    qry->returningList = transformReturningList(pstate, stmt->returningList);
    if (qry->returningList != NIL && RelationIsColStore(((Relation)linitial(pstate->p_target_relation)))) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("column stored relation doesn't support UPDATE returning")));
    }

    qry->rtable = pstate->p_rtable;
    qry->jointree = makeFromExpr(pstate->p_joinlist, qual);

    qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
    qry->hasSubLinks = pstate->p_hasSubLinks;

    UpdateParseCheck(pstate, (Node *)qry);

    assign_query_collations(pstate, qry);
    assign_query_ignore_flag(pstate, qry);
    qry->hintState = stmt->hintState;
    qry->hasIgnore = stmt->hasIgnore;

    return qry;
}

char* checkUpdateResTargetName(Relation rd, RangeVar* rel, ResTarget* res, bool* matchRelname)
{
    char* resname = NULL;
    AttrNumber attrnoIndName = InvalidAttrNumber;
    AttrNumber attrnoResName = InvalidAttrNumber;
    Oid attrtypeid = InvalidOid;
    Oid typrelid = InvalidOid;

    /* the length of indireciton can't less than 1 and the first item type must not be A_Indices */
    if ((list_length(res->indirection) < 1) || IsA(linitial(res->indirection), A_Indices)) {
        return res->name;
    }

    /* name must relname or aliasname */
    if ((strcmp(rel->relname, res->name) != 0) &&
        ((rel->alias == NULL) || (strcmp((rel->alias)->aliasname, res->name) != 0))) {
        return res->name;
    }

    /*
    * if meet set_clause like a.b.c = 1 then the indirection will exceed one,
    * so treat first indirection as tablename/alisename like a in this ex.
    */
    resname = pstrdup(strVal(linitial(res->indirection)));
    if (list_length(res->indirection) > 1) {
        *matchRelname = true;
        return resname;
    }

    /*
    * Here only meet set_clause which has two elements like a.b or test.b.
    * So first keep forward-compatibility behavior of old version, that results
    * if a is a composite type and also relname/alisename then we first handle it as
    * composite type.
    * Otherwise we handle it like a.b like relname/alisename, then a is a relname just
    * update set_clause over.
    */
    attrnoResName = attnameAttNum(rd, res->name, true);
    attrnoIndName = attnameAttNum(rd, resname, true);

    if (attrnoResName > 0) {
        attrtypeid = attnumTypeId(rd, attrnoResName);
        if (attrtypeid) {
            typrelid = typeidTypeRelid(attrtypeid);
            if (typrelid && ((get_attnum(typrelid, resname) != InvalidAttrNumber) || (attrnoIndName <= 0))) {
                ereport(
                    NOTICE, (errmsg("update field '%s' of column '%s', though it's ambiguous.", resname, res->name)));
                return res->name;
            }
        }
    }

    *matchRelname = true;
    return resname;
}

/* Find the attrno corresponding to ResTarget from target tables. */
static int fixUpdateResTargetName(ParseState* pstate, List* resultRelations, ResTarget* res, int* rti,
    Relation* rd, RangeTblEntry** rte)
{
    ListCell* l1;
    ListCell* l2;
    ListCell* l3;
    ListCell* l4;
    bool removeRelname = false, matchRelname = false;
    char* resname = NULL;
    char* resultResName = NULL;
    int attrno, resultAttrno = InvalidAttrNumber;
    bool isMatched = false;

    forfour (l1, pstate->p_target_rangetblentry, l2, pstate->p_target_relation, l3, pstate->p_updateRangeVars,
        l4, resultRelations) {
        RangeTblEntry* target_rte = (RangeTblEntry*)lfirst(l1);
        Relation targetrel = (Relation)lfirst(l2);
        RangeVar* rangeVar = (RangeVar*)lfirst(l3);
        int rtindex = lfirst_int(l4);
        
        resname = checkUpdateResTargetName(targetrel, rangeVar, res, &matchRelname);
        attrno = attnameAttNum(targetrel, resname, true);
        if (attrno != InvalidAttrNumber) {
            /*
             * If the targetlist matches more than once and when target contains a relname(matchRelname)
             * is the same as before(removeRelname), report error.
             */
            if (isMatched == true && (matchRelname == removeRelname)) {
                ereport(ERROR,
                    (errcode(ERRCODE_AMBIGUOUS_COLUMN),
                        errmsg("column reference \"%s\" is ambiguous", res->name),
                        parser_errposition(pstate, res->location)));
            }
            resultAttrno = attrno;
            resultResName = resname;
            *rti = rtindex;
            *rd = targetrel;
            *rte = target_rte;

            isMatched = true;
            /* Mark the target column as requiring update permissions */
            target_rte->updatedCols = bms_add_member(target_rte->updatedCols,
                                                     attrno - FirstLowInvalidHeapAttributeNumber);
            if (DB_IS_CMPT(B_FORMAT) && ((strcmp(rangeVar->relname, res->name) == 0) ||
                ((rangeVar->alias != NULL) && (strcmp((rangeVar->alias)->aliasname, res->name) == 0)))) {
                if (matchRelname == true) {
                    removeRelname = true;
                }
                break;
            }
        }
        if (matchRelname == true) {
            removeRelname = true;
        }
    }

    if (!isMatched) {
        res->name = resname;
        if (matchRelname) {
            res->indirection = RemoveListCell(res->indirection, 1);
        }
        return InvalidAttrNumber;
    }

    if (removeRelname) {
        res->name = resultResName;
        res->indirection = RemoveListCell(res->indirection, 1);
    }
    return resultAttrno;
}

static void UndefinedColumnError(ParseState* pstate, ResTarget* origTarget, int targetRelationNum)
{
    if (!origTarget->indirection) {
        if (targetRelationNum == 1) {
            ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_COLUMN),
                    errmsg("column \"%s\" of relation \"%s\" does not exist",
                        origTarget->name,
                        RelationGetRelationName((Relation)linitial(pstate->p_target_relation))),
                    parser_errposition(pstate, origTarget->location)));
        }
        ereport(ERROR,
            (errcode(ERRCODE_UNDEFINED_COLUMN),
                errmsg("column \"%s\" does not exist",
                    origTarget->name),
                parser_errposition(pstate, origTarget->location)));
    } else {
        char* resname = pstrdup((char*)(((Value*)lfirst(list_head(origTarget->indirection)))->val.str));
        if (targetRelationNum == 1) {
            ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_COLUMN),
                    errmsg("column \"%s.%s\" of relation \"%s\" does not exist",
                        origTarget->name,
                        resname,
                        RelationGetRelationName((Relation)linitial(pstate->p_target_relation))),
                    parser_errposition(pstate, origTarget->location)));
        }
        ereport(ERROR,
            (errcode(ERRCODE_UNDEFINED_COLUMN),
                errmsg("column \"%s.%s\" does not exist",
                    origTarget->name,
                    resname),
                parser_errposition(pstate, origTarget->location)));
    }
}

/*
 * checkSRFInMultiUpdate
 * Verify that a set-returning function is called in multiple-update and throw a error.
 */
static inline void checkSRFInMultiUpdate(Expr* expr, int targetRelationNum)
{
    if (targetRelationNum == 1)
        return;
  
    if ((expr->type == T_FuncExpr) && ((FuncExpr*)expr)->funcretset == true) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("set-returning functions are not allowed in multiple UPDATE")));
    }
}

/*
 * transformUpdateTargetList -
 * handle SET clause in UPDATE/INSERT ... DUPLICATE KEY UPDATE
 */
static List* transformUpdateTargetList(ParseState* pstate, List* qryTlist, List* origTlist, List* resultRelations)
{
    List* tlist = NIL;
    RangeTblEntry* target_rte = NULL;
    ListCell* tl;
    ListCell* orig_tl;
    Relation targetrel = NULL;
    int rtindex = 0;
    int targetRelationNum = list_length(pstate->p_target_relation);
    int rangeTableNum = list_length(pstate->p_rtable);

    List** new_tle = (List**)palloc0(rangeTableNum * sizeof(List*));

    /* Prepare to assign non-conflicting resnos to resjunk attributes */
    pstate->p_next_resno = 1;
    foreach_cell (cell, pstate->p_target_relation) {
        targetrel = (Relation)lfirst(cell);
        pstate->p_next_resno += targetrel->rd_rel->relnatts;
    }

    /* Prepare non-junk columns for assignment to target table */
    forboth (tl, qryTlist, orig_tl, origTlist) {
        TargetEntry* tle = (TargetEntry*)lfirst(tl);
        ResTarget* origTarget = (ResTarget*)lfirst(orig_tl);
        int attrno;

        if (tle->resjunk) {
            /*
             * Resjunk nodes need no additional processing, but be sure they
             * have resnos that do not match any target columns; else rewriter
             * or planner might get confused.  They don't need a resname
             * either.
             */
            tle->resno = (AttrNumber)pstate->p_next_resno++;
            tle->resname = NULL;
            continue;
        }

        attrno = fixUpdateResTargetName(pstate, resultRelations, origTarget, &rtindex, &targetrel, &target_rte);
        if (attrno == InvalidAttrNumber) {
            UndefinedColumnError(pstate, origTarget, targetRelationNum);
        }
        updateTargetListEntry(pstate, tle, origTarget->name, attrno, origTarget->indirection, origTarget->location,
            targetrel, target_rte);
        checkSRFInMultiUpdate(tle->expr, targetRelationNum);
        tle->rtindex = rtindex;
        new_tle[rtindex - 1] = lappend(new_tle[rtindex - 1], tle);
    }
    /*
     * If there are actually the same result relations by different alias
     * or synonym in multiple update, merge their targetLists.
     */
    transformMultiTargetList(pstate->p_target_rangetblentry, new_tle, resultRelations);

    if (targetRelationNum == 1) {
        int i = linitial_int(resultRelations);
        tlist = new_tle[i - 1];
    } else {
        for (int i = 0; i < rangeTableNum; i++) {
            if (new_tle[i]) {
                tlist = list_concat(tlist, new_tle[i]);
            }
        }
    }
    pfree(new_tle);
    return tlist;
}

/*
 * transformReturningList -
 *	handle a RETURNING clause in INSERT/UPDATE/DELETE
 */
static List* transformReturningList(ParseState* pstate, List* returningList)
{
    List* rlist = NIL;
    int save_next_resno;
    bool save_hasAggs = false;
    bool save_hasWindowFuncs = false;

    if (returningList == NIL) {
        return NIL; /* nothing to do */
    }

    /*
     * We need to assign resnos starting at one in the RETURNING list. Save
     * and restore the main tlist's value of p_next_resno, just in case
     * someone looks at it later (probably won't happen).
     */
    save_next_resno = pstate->p_next_resno;
    pstate->p_next_resno = 1;

    /* save other state so that we can detect disallowed stuff */
    save_hasAggs = pstate->p_hasAggs;
    pstate->p_hasAggs = false;
    save_hasWindowFuncs = pstate->p_hasWindowFuncs;
    pstate->p_hasWindowFuncs = false;

    /* transform RETURNING identically to a SELECT targetlist */
    rlist = transformTargetList(pstate, returningList, EXPR_KIND_RETURNING);

    /* check for disallowed stuff */

    /* aggregates not allowed (but subselects are okay) */
    if (pstate->p_hasAggs) {
        ereport(ERROR,
            (errcode(ERRCODE_GROUPING_ERROR),
                errmsg("cannot use aggregate function in RETURNING"),
                parser_errposition(pstate, locate_agg_of_level((Node*)rlist, 0))));
    }
    if (pstate->p_hasWindowFuncs) {
        ereport(ERROR,
            (errcode(ERRCODE_WINDOWING_ERROR),
                errmsg("cannot use window function in RETURNING"),
                parser_errposition(pstate, locate_windowfunc((Node*)rlist))));
    }

    /* mark column origins */
    markTargetListOrigins(pstate, rlist);

    /* resolve any still-unresolved output columns as being type text */
    if (pstate->p_resolve_unknowns) {
        resolveTargetListUnknowns(pstate, rlist);
    }

    /* restore state */
    pstate->p_next_resno = save_next_resno;
    pstate->p_hasAggs = save_hasAggs;
    pstate->p_hasWindowFuncs = save_hasWindowFuncs;

    return rlist;
}

/*
 * transformDeclareCursorStmt -
 *	transform a DECLARE CURSOR Statement
 *
 * DECLARE CURSOR is a hybrid case: it's an optimizable statement (in fact not
 * significantly different from a SELECT) as far as parsing/rewriting/planning
 * are concerned, but it's not passed to the executor and so in that sense is
 * a utility statement.  We transform it into a Query exactly as if it were
 * a SELECT, then stick the original DeclareCursorStmt into the utilityStmt
 * field to carry the cursor name and options.
 */
static Query* transformDeclareCursorStmt(ParseState* pstate, DeclareCursorStmt* stmt)
{
    Query* result = NULL;

    /*
     * Don't allow both SCROLL and NO SCROLL to be specified
     */
    if ((stmt->options & CURSOR_OPT_SCROLL) && (stmt->options & CURSOR_OPT_NO_SCROLL)) {
        ereport(
            ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot specify both SCROLL and NO SCROLL")));
    }

    PG_TRY();
    {
        /* according to DeclareCursorName to form a dependency on the used ROW type */
        if (!(stmt->options & CURSOR_OPT_HOLD)) {
            u_sess->analyze_cxt.DeclareCursorName = stmt->portalname;
        }
        result = transformStmt(pstate, stmt->query);
    }
    PG_CATCH();
    {
        u_sess->analyze_cxt.DeclareCursorName = NULL;
        PG_RE_THROW();
    }
    PG_END_TRY();

    u_sess->analyze_cxt.DeclareCursorName = NULL;

    /* Grammar should not have allowed anything but SELECT */
    if (!IsA(result, Query) || result->commandType != CMD_SELECT || result->utilityStmt != NULL) {
        ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("unexpected non-SELECT command in DECLARE CURSOR")));
    }

    /*
     * We also disallow data-modifying WITH in a cursor.  (This could be
     * allowed, but the semantics of when the updates occur might be
     * surprising.)
     */
    if (result->hasModifyingCTE) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("DECLARE CURSOR must not contain data-modifying statements in WITH")));
    }

    /* FOR UPDATE and WITH HOLD are not compatible */
    if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("DECLARE CURSOR WITH HOLD ... FOR UPDATE/SHARE is not supported"),
                errdetail("Holdable cursors must be READ ONLY.")));
    }

    /* FOR UPDATE and SCROLL are not compatible */
    if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("DECLARE SCROLL CURSOR ... FOR UPDATE/SHARE is not supported"),
                errdetail("Scrollable cursors must be READ ONLY.")));
    }

    /* FOR UPDATE and INSENSITIVE are not compatible */
    if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("DECLARE INSENSITIVE CURSOR ... FOR UPDATE/SHARE is not supported"),
                errdetail("Insensitive cursors must be READ ONLY.")));
    }

    /* We won't need the raw querytree any more */
    stmt->query = NULL;

    result->utilityStmt = (Node*)stmt;

    return result;
}

/*
 * transformExplainStmt -
 *	transform an EXPLAIN Statement
 *
 * EXPLAIN is like other utility statements in that we emit it as a
 * CMD_UTILITY Query node; however, we must first transform the contained
 * query.  We used to postpone that until execution, but it's really necessary
 * to do it during the normal parse analysis phase to ensure that side effects
 * of parser hooks happen at the expected time.
 */
static Query* transformExplainStmt(ParseState* pstate, ExplainStmt* stmt)
{
    Query* result = NULL;

    t_thrd.postgres_cxt.cur_command_tag = transform_node_tag(stmt->query);
    /* transform contained query, allowing SELECT INTO */
    stmt->query = (Node*)transformTopLevelStmt(pstate, stmt->query);

    /* represent the command as a utility Query */
    result = makeNode(Query);
    result->commandType = CMD_UTILITY;
    result->utilityStmt = (Node*)stmt;

    return result;
}

/*
 * transformCreateTableAsStmt -
 * transform a CREATE TABLE AS, SELECT ... INTO, or CREATE MATERIALIZED VIEW Statement
 *
 * As with EXPLAIN, transform the contained statement now.
 */
static Query* transformCreateTableAsStmt(ParseState* pstate, CreateTableAsStmt* stmt)
{
    Query* result = NULL;
    ListCell* lc = NULL;

    /*
     * Set relkind in IntoClause based on statement relkind.  These are
     * different types, because the parser users the ObjectType enumeration
     * and the executor uses RELKIND_* defines.
     */
    switch (stmt->relkind)
    {
        case (OBJECT_TABLE):
            stmt->into->relkind = RELKIND_RELATION;
            break;
        case (OBJECT_MATVIEW):
            stmt->into->relkind = RELKIND_MATVIEW;
            break;
        default:
            elog(ERROR, "unrecognized object relkind: %d",
                 (int) stmt->relkind);
    }

    /* transform contained query */
    stmt->query = (Node*)transformStmt(pstate, stmt->query);

    if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) {
        /* CREATE TABLE AS SELECT is not allowed with Foreign Key and Tablelike Clause*/
        foreach (lc, stmt->into->tableElts) {
            Node* node = (Node*)lfirst(lc);
            if (IsA(node, ColumnDef)) {
                ColumnDef* col = (ColumnDef*) node;
                ListCell* cell = NULL;
                foreach(cell, col->constraints){
                    if (IsA(lfirst(cell), Constraint)) {
                        Constraint* constraint = (Constraint*) lfirst(cell);
                        if (constraint->contype == CONSTR_FOREIGN) {
                            ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                errmsg("CREATE TABLE AS SELECT is not allowed with Foreign Key")));
                        }
                    }
                }
            } else if (IsA(node, Constraint)) {
                Constraint* constraint = (Constraint*) node;
                if (constraint->contype == CONSTR_FOREIGN) {
                    ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("CREATE TABLE AS SELECT is not allowed with Foreign Key")));
                }
            } else if (IsA(node, TableLikeClause)) {
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                        errmsg("CREATE TABLE AS SELECT is not allowed with Tablelike Clause")));
            }
        }
    }

    /* if result type of new relation is unknown-type, then resolve as type TEXT */
    foreach (lc, ((Query*)stmt->query)->targetList) {
        TargetEntry* tle = (TargetEntry*)lfirst(lc);
        if (exprType((Node*)tle->expr) == UNKNOWNOID) {
            tle->expr = (Expr*)coerce_type(
                pstate, (Node*)tle->expr, UNKNOWNOID, TEXTOID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST,
                NULL, NULL, -1);
        }
    }

    /* set io state for backend status for the thread, we will use it to check user space */
    pgstat_set_io_state(IOSTATE_WRITE);

    /* represent the command as a utility Query */
    result = makeNode(Query);
    result->commandType = CMD_UTILITY;
    result->hintState = (HintState*)copyObject(((Query*)stmt->query)->hintState);
    result->utilityStmt = (Node*)stmt;

    return result;
}

#ifdef PGXC
#ifdef ENABLE_DISTRIBUTE_TEST
/*
 * Check if given GUC variable can go through EXECUTE DIRECT
 */
static bool checkExecDirectVariableSetStmt(const Node* node)
{
    if (nodeTag(node) == T_VariableSetStmt) {
        if (pg_strcasecmp(((VariableSetStmt*)node)->name, "distribute_test_param") == 0) {
            return true;
        }
    }

    return false;
}
#endif

static void get_index_and_type_from_nodename(const char* node_name, int* node_index, char* node_type)
{
    Oid node_oid;

    if (node_name == NULL || node_index == NULL || node_type == NULL) {
        return;
    }

    node_oid = get_pgxc_nodeoid(node_name);
    if (!OidIsValid(node_oid)) {
        ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("PGXC Node %s: object not defined", node_name)));
    }

    *node_type = get_pgxc_nodetype(node_oid);
    *node_index = PGXCNodeGetNodeId(node_oid, get_pgxc_nodetype(node_oid));

    return;
}

RemoteQueryExecType fill_exec_type(char node_type, ExecDirectOption option)
{
    RemoteQueryExecType exec_type;

    switch (option) {
        case EXEC_DIRECT_ON_LIST:
            if (node_type == PGXC_NODE_COORDINATOR) {
                exec_type = EXEC_ON_COORDS;
            } else {
                exec_type = EXEC_ON_DATANODES;
            }
            break;
        case EXEC_DIRECT_ON_ALL_CN:
            exec_type = EXEC_ON_COORDS;
            break;
        case EXEC_DIRECT_ON_ALL_DN:
            exec_type = EXEC_ON_DATANODES;
            break;
        case EXEC_DIRECT_ON_ALL_NODES:
            exec_type = EXEC_ON_ALL_NODES;
            break;
        default:
            exec_type = EXEC_ON_NONE;
            break;
    }

    return exec_type;
}

void check_node_index(int node_index, char* node_flag, int length_node_flag)
{
    if ((node_flag != NULL) &&
        (node_index < length_node_flag) &&
        (node_index >= 0) &&
        node_flag[node_index] == 0) {
        node_flag[node_index] = 1;
    } else {
        ereport(ERROR,
                (errcode(ERRCODE_OPERATE_NOT_SUPPORTED),
                 errmsg("node name in node list is not correct")));
    }
}

static void fill_node_list_and_exec_type(const List* nodenames_list, ExecDirectOption option, RemoteQuery* query, bool* include_local)
{
    char* node_name = NULL;
    int node_index;
    char node_type = PGXC_NODE_NONE;
    ListCell* nodename_item = NULL;
    List* index_list = NULL;
    int number_of_cn = 0;
    int number_of_dn = 0;
    char* cn_flag = NULL;
    char* dn_flag = NULL;

    *include_local = false;
    if (option == EXEC_DIRECT_ON_LIST) {
        if (u_sess->pgxc_cxt.NumCoords > 0) {
            cn_flag = (char*)palloc0(u_sess->pgxc_cxt.NumCoords);
        }
        if (u_sess->pgxc_cxt.NumDataNodes > 0) {
            dn_flag = (char*)palloc0(u_sess->pgxc_cxt.NumDataNodes);
        }

        foreach(nodename_item, nodenames_list) {
            node_name = strVal(lfirst(nodename_item));
            get_index_and_type_from_nodename(node_name, &node_index, &node_type);
            if (node_type == PGXC_NODE_COORDINATOR) {
                check_node_index(node_index, cn_flag, u_sess->pgxc_cxt.NumCoords);
                number_of_cn++;
                if (node_index == u_sess->pgxc_cxt.PGXCNodeId - 1) {
                    *include_local = true;
                    continue;
                }
            } else if (node_type == PGXC_NODE_DATANODE) {
                check_node_index(node_index, dn_flag, u_sess->pgxc_cxt.NumDataNodes);
                number_of_dn++;
            } else {
                ereport(ERROR,
                        (errcode(ERRCODE_UNEXPECTED_NODE_STATE),
                         errmsg("unsupport node type: %d", node_type)));
            }
            index_list = lappend_int(index_list, node_index);
        }

        if ((number_of_cn != 0) && (number_of_dn != 0)) {
            ereport(ERROR,
                    (errcode(ERRCODE_OPERATE_NOT_SUPPORTED),
                     errmsg("not support both coordinator and datanode in the execute list")));
        }
    }

    query->exec_type = fill_exec_type(node_type, option);
    query->exec_nodes->nodeList = index_list;
    if ((option == EXEC_DIRECT_ON_ALL_CN) || (option == EXEC_DIRECT_ON_ALL_NODES)) {
        *include_local = true;
    }

    pfree_ext(cn_flag);
    pfree_ext(dn_flag);

    return;
}

ExecDirectType set_exec_direct_type(bool is_local, CmdType command_type)
{
    ExecDirectType type = EXEC_DIRECT_NONE;

    if (is_local) {
        if (command_type == CMD_UTILITY)
            type = EXEC_DIRECT_LOCAL_UTILITY;
        else
            type = EXEC_DIRECT_LOCAL;
    } else {
        switch (command_type) {
            case CMD_UTILITY:
                type = EXEC_DIRECT_UTILITY;
                break;
            case CMD_SELECT:
                type = EXEC_DIRECT_SELECT;
                break;
            case CMD_INSERT:
                type = EXEC_DIRECT_INSERT;
                break;
            case CMD_UPDATE:
                type = EXEC_DIRECT_UPDATE;
                break;
            case CMD_DELETE:
                type = EXEC_DIRECT_DELETE;
                break;
            case CMD_UNKNOWN:
                break;
            default:
                /* CMD_MERGE and CMD_NOTHING are handled before set up EXECUTE DIRECT flag */
                ereport(ERROR,
                    (errcode(ERRCODE_UNEXPECTED_NODE_STATE),
                        errmsg("unrecognized commandType: %d", command_type)));
                break;
        }
    }

    return type;
}

void check_command_type(CmdType command_type)
{
    if (command_type == CMD_MERGE) {
        ereport(
            ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXECUTE DIRECT cannot execute MERGE INTO query")));
    }

    if (command_type == CMD_NOTHING) {
        ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXECUTE DIRECT cannot execute CREATE RULE")));
    }
}

/*
 * Features not yet supported
 * DML can be launched without errors but this could compromise data
 * consistency, so block it.
 */
static void check_execute_direct_type_and_statement(ExecDirectType exec_direct_type, const Node* utility_statement, bool include_local)
{
    if (!u_sess->attr.attr_common.xc_maintenance_mode && !u_sess->attr.attr_common.IsInplaceUpgrade &&
        (exec_direct_type == EXEC_DIRECT_DELETE || exec_direct_type == EXEC_DIRECT_UPDATE ||
         exec_direct_type == EXEC_DIRECT_INSERT)) {
        ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXECUTE DIRECT cannot execute DML queries")));
    } else if (exec_direct_type == EXEC_DIRECT_UTILITY) {
        if (include_local || (!IsExecDirectUtilityStmt(utility_statement) &&
#ifdef ENABLE_DISTRIBUTE_TEST
               // just enable the VariableSetStmt named distribute_test_param for distribute test.
               !checkExecDirectVariableSetStmt(utility_statement) &&
#endif
               !u_sess->attr.attr_common.xc_maintenance_mode)) {
                /* In case this statement is an utility, check if it is authorized */
            ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXECUTE DIRECT cannot execute this utility query")));
        }
    } else if (exec_direct_type == EXEC_DIRECT_LOCAL_UTILITY &&
#ifdef ENABLE_DISTRIBUTE_TEST
               // just enable the VariableSetStmt named distribute_test_param for distribute test.
               !checkExecDirectVariableSetStmt(utility_statement) &&
#endif
               !u_sess->attr.attr_common.xc_maintenance_mode) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("EXECUTE DIRECT cannot execute locally this utility query")));
    }

    return;
}

static Query* generate_query_execdirect_statement(const ExecDirectStmt* stmt)
{
    Query* result = makeNode(Query);
    char* query = NULL;
    StringInfoData strinfo;
    List* raw_parsetree_list = NIL;
    ListCell* raw_parsetree_item = NULL;

    //  When the EXECUTE DIRECT ON 'xxxxx' statement is executed,
    //  once the 'xxxxx' statement is in error, the error will dislocation,
    // So the space of several characters should be added to ensure the correctness of the error location.
    // eg. EXECUTE DIRECT ON 'select a;', then 'select a;' will be replaced by '                   select a;'
    initStringInfo(&strinfo);
    appendStringInfoSpaces(&strinfo, stmt->location + 1);
    appendStringInfoString(&strinfo, stmt->query);
    query = strinfo.data;

    /* Transform the query into a raw parse list */
    raw_parsetree_list = pg_parse_query(query);
    /* EXECUTE DIRECT can just be executed with a single query */
    if (list_length(raw_parsetree_list) > 1) {
        ereport(
            ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXECUTE DIRECT cannot execute multiple queries")));
    }

    /*
     * Analyze the Raw parse tree
     * EXECUTE DIRECT is restricted to one-step usage
     */
    foreach (raw_parsetree_item, raw_parsetree_list) {
        Node* parsetree = (Node*)lfirst(raw_parsetree_item);
        /* Not allow EXECUTE DIRECT ON recursively */
        if (IsA(parsetree, ExecDirectStmt)) {
            ereport(
                ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXECUTE DIRECT cannot execute recursively")));
        }

        result = parse_analyze(parsetree, query, NULL, 0);
    }

    pfree_ext(strinfo.data);
    return result;
}

static void init_execdirect_utility_stmt(RemoteQuery* step, const char* statement)
{
    step->cursor = NULL;
    step->base_tlist = NIL;
    step->force_autocommit = false;
    step->read_only = true;
    step->sql_statement = pstrdup(statement);
    step->combine_type = COMBINE_TYPE_SAME;
}

/*
 * find agg functions which need add finalize function on sql statement in deparse_query
 */
static bool check_agg_in_execute_direct_query(Node* node, void* context)
{
    if (node == NULL || IS_PGXC_DATANODE) {
        return false;
    }

    if (IsA(node, Aggref)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
             errmsg("EXECUTE DIRECT on multinode not support agg functions.")));
    }

    if (IsA(node, Query)) {
        if (query_tree_walker((Query*)node,
            (bool (*)())check_agg_in_execute_direct_query, (void*)node, 0)) {
            return true;
        }
    }

    return expression_tree_walker(node, (bool (*)())check_agg_in_execute_direct_query, context);
}

/*
 * transformExecDirectStmt -
 *	transform an EXECUTE DIRECT Statement
 *
 * Handling is depends if we should execute on nodes or on Coordinator.
 * To execute on nodes we return CMD_UTILITY query having one T_RemoteQuery node
 * with the inner statement as a sql_command.
 * If statement is to run on Coordinator we should parse inner statement and
 * analyze resulting query tree.
 */
static Query* transformExecDirectStmt(ParseState* pstate, ExecDirectStmt* stmt)
{
    Query* result = NULL;
    List* nodelist = stmt->node_names;
    ExecDirectOption option = stmt->exec_option;
    RemoteQuery* step = makeNode(RemoteQuery);
    bool is_local = false;
    bool include_local = false;
    char nodetype = PGXC_NODE_COORDINATOR;
    int node_index;

    /* Support not available on Datanodes  except single node */
    if (IS_PGXC_DATANODE && !IS_SINGLE_NODE)
        ereport(
            ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXECUTE DIRECT cannot be executed on a Datanode")));

    Assert(IS_PGXC_COORDINATOR || IS_SINGLE_NODE);

    step->exec_nodes = makeNode(ExecNodes);
    result = generate_query_execdirect_statement(stmt);
    if (list_length(nodelist) == 1) {
        get_index_and_type_from_nodename(strVal(linitial(nodelist)), &node_index, &nodetype);
        /* Check if node is requested is the self-node or not */
        if ((nodetype == PGXC_NODE_COORDINATOR && node_index == u_sess->pgxc_cxt.PGXCNodeId - 1) || IS_SINGLE_NODE)
            is_local = true;
        step->exec_type = fill_exec_type(nodetype, option);
        step->exec_nodes->nodeList = lappend_int(step->exec_nodes->nodeList, node_index);
    } else {
        check_agg_in_execute_direct_query((Node*)result, (void*)result);
        fill_node_list_and_exec_type(nodelist, option, step, &include_local);
        step->is_remote_function_query = true;
    }

    /* Needed by planner */
    result->sql_statement = pstrdup(stmt->query);
    init_execdirect_utility_stmt(step, result->sql_statement);

    check_command_type(result->commandType);
    step->exec_direct_type = set_exec_direct_type(is_local, result->commandType);
    check_execute_direct_type_and_statement(step->exec_direct_type, result->utilityStmt, include_local);

    /* Associate newly-created RemoteQuery node to the returned Query result */
    is_local = (is_local || include_local);
    result->is_local = is_local;
    if (!is_local || include_local) {
        result->utilityStmt = (Node*)step;
    }

    /* Not allow SELECT with normal table on CN (no data) */
    if ((step->exec_type == EXEC_ON_COORDS || (step->exec_type == EXEC_ON_ALL_NODES)) &&
        result->commandType == CMD_SELECT &&
        containing_ordinary_table((Node*)result)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("EXECUTE DIRECT cannot execute SELECT query with normal table on coordinator")));
    }

    return result;
}

/*
 * Check if given node is authorized to go through EXECUTE DIRECT
 */
static bool IsExecDirectUtilityStmt(const Node* node)
{
    bool res = true;

    if (node == NULL) {
        return res;
    }

    switch (nodeTag(node)) {
        /*
         * CREATE/DROP TABLESPACE are authorized to control
         * tablespace at single node level.
         */
        case T_CreateTableSpaceStmt:
        case T_DropTableSpaceStmt:
            res = true;
            break;
        default:
            res = false;
            break;
    }

    return res;
}

/*
 * Returns whether or not the rtable (and its subqueries)
 * contain any relation who is the parent of
 * the passed relation
 */
static bool is_relation_child(RangeTblEntry* child_rte, List* rtable)
{
    ListCell* item = NULL;

    if (child_rte == NULL || rtable == NULL) {
        return false;
    }

    if (child_rte->rtekind != RTE_RELATION) {
        return false;
    }

    foreach (item, rtable) {
        RangeTblEntry* rte = (RangeTblEntry*)lfirst(item);

        if (rte->rtekind == RTE_RELATION) {
            if (is_rel_child_of_rel(child_rte, rte)) {
                return true;
            }
        } else if (rte->rtekind == RTE_SUBQUERY) {
            return is_relation_child(child_rte, rte->subquery->rtable);
        }
    }
    return false;
}

/*
 * Returns whether the passed RTEs have a parent child relationship
 */
static bool is_rel_child_of_rel(RangeTblEntry* child_rte, RangeTblEntry* parent_rte)
{
    Oid parentOID;
    bool res = false;
    Relation relation;
    SysScanDesc scan;
    ScanKeyData key[1];
    HeapTuple inheritsTuple;
    Oid inhrelid;

    /* Does parent RT entry allow inheritance? */
    if (!parent_rte->inh) {
        return false;
    }

    /* Ignore any already-expanded UNION ALL nodes */
    if (parent_rte->rtekind != RTE_RELATION) {
        return false;
    }

    /* Fast path for common case of childless table */
    parentOID = parent_rte->relid;
    if (!has_subclass(parentOID)) {
        return false;
    }

    /* Assume we did not find any match */
    res = false;

    /* Scan pg_inherits and get all the subclass OIDs one by one. */
    relation = heap_open(InheritsRelationId, AccessShareLock);
    ScanKeyInit(&key[0], Anum_pg_inherits_inhparent, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(parentOID));
    scan = systable_beginscan(relation, InheritsParentIndexId, true, NULL, 1, key);

    while ((inheritsTuple = systable_getnext(scan)) != NULL) {
        inhrelid = ((Form_pg_inherits)GETSTRUCT(inheritsTuple))->inhrelid;
        /* Did we find the Oid of the passed RTE in one of the children? */
        if (child_rte->relid == inhrelid) {
            res = true;
            break;
        }
    }

    systable_endscan(scan);
    heap_close(relation, AccessShareLock);
    return res;
}

#endif

/*
 * Check for features that are not supported together with FOR [KEY] UPDATE/SHARE.
 *
 * exported so planner can check again after rewriting, query pullup, etc
 */
void CheckSelectLocking(Query* qry)
{
    if (qry->setOperations) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("SELECT FOR UPDATE/SHARE%s is not allowed with UNION/INTERSECT/EXCEPT",
                       NOKEYUPDATE_KEYSHARE_ERRMSG)));
    }
    if (qry->distinctClause != NIL) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("SELECT FOR UPDATE/SHARE%s is not allowed with DISTINCT clause", NOKEYUPDATE_KEYSHARE_ERRMSG)));
    }
    if (qry->groupClause != NIL) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("SELECT FOR UPDATE/SHARE%s is not allowed with GROUP BY clause", NOKEYUPDATE_KEYSHARE_ERRMSG)));
    }
    if (qry->havingQual != NULL) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("SELECT FOR UPDATE/SHARE%s is not allowed with HAVING clause", NOKEYUPDATE_KEYSHARE_ERRMSG)));
    }
    if (qry->hasAggs) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("SELECT FOR UPDATE/SHARE%s is not allowed with aggregate functions",
                       NOKEYUPDATE_KEYSHARE_ERRMSG)));
    }
    if (qry->hasWindowFuncs) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("SELECT FOR UPDATE/SHARE%s is not allowed with window functions", NOKEYUPDATE_KEYSHARE_ERRMSG)));
    }
    if (expression_returns_set((Node*)qry->targetList)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("SELECT FOR UPDATE/SHARE%s is not allowed with set-returning functions in the target list",
                       NOKEYUPDATE_KEYSHARE_ERRMSG)));
    }
}

static bool CheckViewBasedOnCstore(Relation targetrel)
{
    Assert(RelationIsView(targetrel));

    Query* viewquery = get_view_query(targetrel);
    ListCell* l = NULL;
    foreach (l, viewquery->rtable) {
        Node* rte = (Node*)lfirst(l);
        if (ContainColStoreWalker(rte, targetrel->rd_id)) {
            return true;
        }
    }
    return false;
}

static bool ContainColStoreWalker(Node* node, Oid targetOid)
{
    if (node == NULL) {
        return false;
    }
    uintptr_t ptrOid = (uintptr_t)targetOid;

    /* Check range table entry */
    if (IsA(node, RangeTblEntry)) {
        RangeTblEntry* rte = (RangeTblEntry*)node;
        /* only check ordinary relation */
        if ((rte->rtekind != RTE_RELATION)) {
            List* rtable = list_make1(node);
            return range_table_walker(rtable, (bool (*)())ContainColStoreWalker, (void*)ptrOid, 0);
        }
        Assert(OidIsValid(rte->relid));
        if (rte->relid == targetOid) {
            return false;
        }
        Relation rel = relation_open(rte->relid, AccessShareLock);
        if (!RelationIsValid(rel)) {
            return false;
        }
        if (RelationIsColStore(rel) || (RelationIsView(rel) && CheckViewBasedOnCstore(rel))) {
            relation_close(rel, AccessShareLock);
            return true;
        }
        relation_close(rel, AccessShareLock);
        return false;
    }
    /* Check query */
    if (IsA(node, Query)) {
        return query_tree_walker((Query*)node, (bool (*)())ContainColStoreWalker, (void*)ptrOid, QTW_EXAMINE_RTES);
    }
    return expression_tree_walker(node, (bool (*)())ContainColStoreWalker, (void*)ptrOid);
}

static bool CheckViewBasedOnAstore(Relation targetrel)
{
    Assert(RelationIsView(targetrel));

    Query* viewquery = get_view_query(targetrel);
    ListCell* l = NULL;
    foreach (l, viewquery->rtable) {
        Node* rte = (Node*)lfirst(l);
        if (ContainAStoreWalker(rte, targetrel->rd_id)) {
            return true;
        }
    }
    return false;
}

static bool ContainAStoreWalker(Node* node, Oid targetOid)
{
    if (node == NULL) {
        return false;
    }
    uintptr_t ptrOid = (uintptr_t)targetOid;

    if (IsA(node, RangeTblEntry)) {
        RangeTblEntry* rte = (RangeTblEntry*)node;
        if ((rte->rtekind != RTE_RELATION)) {
            List* rtable = list_make1(node);
            return range_table_walker(rtable, (bool (*)())ContainAStoreWalker, (void*)ptrOid, 0);
        }
        Assert(OidIsValid(rte->relid));
        if (rte->relid == targetOid) {
            return false;
        }
        Relation rel = relation_open(rte->relid, AccessShareLock);
        if (!RelationIsValid(rel)) {
            return false;
        }
        if (RelationIsAstoreFormat(rel) || (RelationIsView(rel) && CheckViewBasedOnAstore(rel))) {
            relation_close(rel, AccessShareLock);
            return true;
        }
        relation_close(rel, AccessShareLock);
        return false;
    }
    if (IsA(node, Query)) {
        return query_tree_walker((Query*)node, (bool (*)())ContainAStoreWalker, (void*)ptrOid, QTW_EXAMINE_RTES);
    }
    return expression_tree_walker(node, (bool (*)())ContainAStoreWalker, (void*)ptrOid);
}

/*
 * Transform a FOR [KEY] UPDATE/SHARE clause
 *
 * This basically involves replacing names by integer relids.
 *
 * NB: if you need to change this, see also markQueryForLocking()
 * in rewriteHandler.c, and isLockedRefname() in parse_relation.c.
 */
static void transformLockingClause(ParseState* pstate, Query* qry, LockingClause* lc, bool pushedDown)
{
    List* lockedRels = lc->lockedRels;
    ListCell* l = NULL;
    ListCell* rt = NULL;
    Index i;
    LockingClause* allrels = NULL;
    Relation rel;

    CheckSelectLocking(qry);

    /* make a clause we can pass down to subqueries to select all rels */
    allrels = makeNode(LockingClause);
    allrels->lockedRels = NIL; /* indicates all rels */
    /* The strength of lc is not set at old version and distribution. Set it according to forUpdate. */
    if (t_thrd.proc->workingVersionNum < ENHANCED_TUPLE_LOCK_VERSION_NUM
#ifdef ENABLE_MULTIPLE_NODES
        || true
#endif
        ) {
        lc->strength = lc->forUpdate ? LCS_FORUPDATE : LCS_FORSHARE;
    }
    allrels->strength = lc->strength;
    allrels->waitSec = lc->waitSec;

    /* The processing delay of the ProcSleep function is in milliseconds. Set the delay to int_max/1000. */
    /* The processing delay of the ProcSleep function is in milliseconds. Set the delay to int_max/1000. */
    if (lc->waitSec > (MAX_INT32 / MILLISECONDS_PER_SECONDS)) {
        ereport(ERROR,
            (errmodule(MOD_OPT_PLANNER), errcode(ERRCODE_INVALID_OPTION),
                errmsg("The delay ranges from 0 to 2147483."),
                errdetail("N/A"),
                errcause("Invalid input parameter."),
                erraction("Modify SQL statement according to the manual.")));
    }

    allrels->waitPolicy = lc->waitPolicy;

    if (lockedRels == NIL) {
        /* all regular tables used in query */
        i = 0;
        foreach (rt, qry->rtable) {
            RangeTblEntry* rte = (RangeTblEntry*)lfirst(rt);

            ++i;
            switch (rte->rtekind) {
                case RTE_RELATION:
                    rel = relation_open(rte->relid, AccessShareLock);
                    if (RelationIsColStore(rel)) {
                        heap_close(rel, AccessShareLock);
                        ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE cannot be used with "
                                       "column table \"%s\"", rte->eref->aliasname)));
                    } else if (RelationIsView(rel) && CheckViewBasedOnCstore(rel)) {
                        heap_close(rel, AccessShareLock);
                        ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE cannot be used with "
                                       "view \"%s\" based on column table", rte->eref->aliasname)));
                    } else {
                        if (!(RelationIsView(rel) && CheckViewBasedOnAstore(rel)) &&
                            !RelationIsAstoreFormat(rel) && lc->waitPolicy == LockWaitSkip) {
                            ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                    errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE with skip locked "
                                        "only be used with AStore table \"%s\"", rte->eref->aliasname)));
                        }
                    }

                    heap_close(rel, AccessShareLock);

                    applyLockingClause(qry, i, lc->strength, lc->waitPolicy, pushedDown, lc->waitSec);
                    rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                    break;
                case RTE_SUBQUERY:
                    applyLockingClause(qry, i, lc->strength, lc->waitPolicy, pushedDown, lc->waitSec);

                    /*
                     * FOR [KEY] UPDATE/SHARE of subquery is propagated to all of
                     * subquery's rels, too.  We could do this later (based on
                     * the marking of the subquery RTE) but it is convenient
                     * to have local knowledge in each query level about which
                     * rels need to be opened with RowShareLock.
                     */
                    transformLockingClause(pstate, rte->subquery, allrels, true);
                    break;
                case RTE_CTE:
                    ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                            errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a WITH query")));
                    break;
                default:
                    /* ignore JOIN, SPECIAL, FUNCTION, VALUES, CTE RTEs */
                    break;
            }
        }
    } else {
        /* just the named tables */
        foreach (l, lockedRels) {
            RangeVar* thisrel = (RangeVar*)lfirst(l);

            /* For simplicity we insist on unqualified alias names here */
            if (thisrel->catalogname || thisrel->schemaname) {
                ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                        errmsg("SELECT FOR UPDATE/SHARE%s must specify unqualified "
                               "relation names", NOKEYUPDATE_KEYSHARE_ERRMSG),
                        parser_errposition(pstate, thisrel->location)));
            }

            i = 0;
            foreach (rt, qry->rtable) {
                RangeTblEntry* rte = (RangeTblEntry*)lfirst(rt);

                ++i;
                if (strcmp(rte->eref->aliasname, thisrel->relname) == 0) {
                    switch (rte->rtekind) {
                        case RTE_RELATION:
                            rel = relation_open(rte->relid, AccessShareLock);
                            if (RelationIsColStore(rel)) {
                                heap_close(rel, AccessShareLock);
                                ereport(ERROR,
                                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                        errmsg("SELECT FOR UPDATE/SHARE%s cannot be used with column table \"%s\"",
                                               NOKEYUPDATE_KEYSHARE_ERRMSG, rte->eref->aliasname),
                                        parser_errposition(pstate, thisrel->location)));
                            } else if (RelationIsView(rel) && CheckViewBasedOnCstore(rel)) {
                                heap_close(rel, AccessShareLock);
                                ereport(ERROR,
                                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                        errmsg("SELECT FOR UPDATE/SHARE%s cannot be used with view \"%s\" based on"
                                               " column table", NOKEYUPDATE_KEYSHARE_ERRMSG, rte->eref->aliasname),
                                        parser_errposition(pstate, thisrel->location)));
                            } else {
                                if (!(RelationIsView(rel) && CheckViewBasedOnAstore(rel)) &&
                                    !RelationIsAstoreFormat(rel) && lc->waitPolicy == LockWaitSkip) {
                                    ereport(ERROR,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                            errmsg("SELECT FOR UPDATE/SHARE/NO KEY UPDATE/KEY SHARE with skip locked "
                                                "only be used with row table \"%s\"", rte->eref->aliasname),
                                            parser_errposition(pstate, thisrel->location)));
                                }
                            }

                            heap_close(rel, AccessShareLock);
                            applyLockingClause(qry, i, lc->strength, lc->waitPolicy, pushedDown,
                                               lc->waitSec);
                            rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                            break;
                        case RTE_SUBQUERY:
                            applyLockingClause(qry, i, lc->strength, lc->waitPolicy, pushedDown,
                                               lc->waitSec);
                            /* see comment above */
                            transformLockingClause(pstate, rte->subquery, allrels, true);
                            break;
                        case RTE_JOIN:
                            ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                    errmsg("SELECT FOR UPDATE/SHARE%s cannot be applied to a join",
                                           NOKEYUPDATE_KEYSHARE_ERRMSG),
                                    parser_errposition(pstate, thisrel->location)));
                            break;
                        case RTE_FUNCTION:
                            ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                    errmsg("SELECT FOR UPDATE/SHARE%s cannot be applied to a function",
                                           NOKEYUPDATE_KEYSHARE_ERRMSG),
                                    parser_errposition(pstate, thisrel->location)));
                            break;
                        case RTE_VALUES:
                            ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                    errmsg("SELECT FOR UPDATE/SHARE%s cannot be applied to VALUES",
                                           NOKEYUPDATE_KEYSHARE_ERRMSG),
                                    parser_errposition(pstate, thisrel->location)));
                            break;
                        case RTE_CTE:
                            ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                    errmsg("SELECT FOR UPDATE/SHARE%s cannot be applied to a WITH query",
                                           NOKEYUPDATE_KEYSHARE_ERRMSG),
                                    parser_errposition(pstate, thisrel->location)));
                            break;
                        default:
                            ereport(ERROR,
                                (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
                                    errmsg("unrecognized RTE type: %d", (int)rte->rtekind)));
                            break;
                    }
                    break; /* out of foreach loop */
                }
            }
            if (rt == NULL) {
                ereport(ERROR,
                    (errcode(ERRCODE_UNDEFINED_TABLE),
                        errmsg("relation \"%s\" in FOR UPDATE/SHARE//NO KEY UPDATE/KEY SHARE clause not found "
                               "in FROM clause", thisrel->relname),
                        parser_errposition(pstate, thisrel->location)));
            }
        }
    }
}

/*
 * Record locking info for a single rangetable item
 */
void applyLockingClause(Query* qry, Index rtindex, LockClauseStrength strength, LockWaitPolicy waitPolicy, bool pushedDown,
                        int waitSec)
{
    RowMarkClause* rc = NULL;

    /* If it's an explicit clause, make sure hasForUpdate gets set */
    if (!pushedDown) {
        qry->hasForUpdate = true;
    }

    /* Check for pre-existing entry for same rtindex */
    if ((rc = get_parse_rowmark(qry, rtindex)) != NULL) {
        /*
         * If the same RTE is specified for more than one locking strength,
         * treat is as the strongest.  (Reasonable, since you can't take both a
         * shared and exclusive lock at the same time; it'll end up being
         * exclusive anyway.)
         *
         * Similarly, if the same RTE is specified with more than one lock wait
         * policy, consider that NOWAIT wins over SKIP LOCKED, which in turn
         * wins over waiting for the lock (the default).  This is a bit more
         * debatable but raising an error doesn't seem helpful.  (Consider for
         * instance SELECT FOR UPDATE NOWAIT from a view that internally
         * contains a plain FOR UPDATE spec.)  Having NOWAIT win over SKIP
         * LOCKED is reasonable since the former throws an error in case of
         * coming across a locked tuple, which may be undesirable in some cases
         * but it seems better than silently returning inconsistent results.
         *
         * And of course pushedDown becomes false if any clause is explicit.
         */
        rc->strength = Max(rc->strength, strength);
        rc->forUpdate = rc->strength == LCS_FORUPDATE;
        rc->waitPolicy = Max(rc->waitPolicy, waitPolicy);
        rc->waitSec = Max(rc->waitSec, waitSec);
        rc->pushedDown = rc->pushedDown && pushedDown;
        return;
    }

    /* Make a new RowMarkClause */
    rc = makeNode(RowMarkClause);
    rc->rti = rtindex;
    rc->forUpdate = strength == LCS_FORUPDATE;
    rc->strength = strength;
    rc->waitPolicy = waitPolicy;
    rc->waitSec = waitSec;
    rc->pushedDown = pushedDown;
    qry->rowMarks = lappend(qry->rowMarks, rc);
}

/*
 * Check if there is foreign table that is included in one insert statement.
 *
 * input:
 * 		rte_table_list: the list of p_rtable of ParseState.
 *
 * output:
 * 		true: there is foreign table included in insert statement.
 */
bool checkForeignTableExist(List* rte_table_list)
{
    bool ret_val = false;
    ListCell* lc = NULL;
    RangeTblEntry* rte = NULL;

    if (rte_table_list == NULL) {
        return ret_val;
    }

    foreach (lc, rte_table_list) {
        rte = (RangeTblEntry*)lfirst(lc);
        if (rte->relkind == RELKIND_FOREIGN_TABLE || rte->relkind == RELKIND_STREAM) {
            ret_val = true;
            break;
        }
    }

    return ret_val;
}

/*
 * Function name: set_subquery_is_under_insert
 * 		set p_is_in_insert for paserstate, to mark this is sub query under one insert.
 *
 * input:
 * 		subParseState: ParseState of sub query.
 *
 *
 * output:
 * 		void
 */
void set_subquery_is_under_insert(ParseState* subParseState)
{
    ParseState* ancestor_ps = NULL;
    ParseState* temp_ps = NULL;

    if (unlikely(subParseState == NULL)) {
        ereport(ERROR, 
            (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), 
                errmsg("subParseState should not be null")));
    }

    /* If the subParseState's parentParseState is NULL then do nothing. */
    if (subParseState->parentParseState == NULL) {
        return;
    }

    /* Find top level parsestate. */
    temp_ps = subParseState;
    while (temp_ps != NULL) {
        if (temp_ps->parentParseState != NULL) {
            ancestor_ps = temp_ps->parentParseState;
            /* If this is a insert statement. then mark sub parsestate is under insert. */
            if (ancestor_ps->p_is_insert) {
                subParseState->p_is_in_insert = ancestor_ps->p_is_insert;
                break;
            }
        }
        temp_ps = temp_ps->parentParseState;
    }
}

/*
 * Function name: set_ancestor_ps_contain_foreigntbl
 * 		When sub query find a foreign table. call this function to set top level parse
 * 		state that is insert type to set p_is_foreignTbl_exist to true;
 * Input:
 * 		subParseState: the ParseState of sub query.
 * Output:
 * 		void
 */
void set_ancestor_ps_contain_foreigntbl(ParseState* subParseState)
{
    ParseState* ancestor_ps = NULL;
    ParseState* temp_ps = NULL;

    if (unlikely(subParseState == NULL)) {
        ereport(ERROR,
            (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), 
                errmsg("subParseState should not be null")));
    }

    /* If the subParseState's parentParseState is NULL then do nothing. */
    if (subParseState->parentParseState == NULL) {
        return;
    }

    /* We go back through parentParseState to find top level parseState. */
    temp_ps = subParseState;
    while (temp_ps != NULL) {
        if (temp_ps->parentParseState != NULL) {
            ancestor_ps = temp_ps->parentParseState;
            /*
             * Because there should at least one insert statement on top of
             * this select statement, we mark find foreign table in all top level
             * insert statements.
             */
            if (ancestor_ps->p_is_insert) {
                ancestor_ps->p_is_foreignTbl_exist = true;
            }
        }
        temp_ps = temp_ps->parentParseState;
    }
}

/*
 * @Description: This group clause if include groupingSet.
 * @in groupClause - group clause.
 * @return - If include return true else return false.
 */
static bool include_groupingset(Node* groupClause)
{
    if (groupClause == NULL) {
        return false;
    }

    if (IsA(groupClause, List)) {
        List* group_list = (List*)groupClause;

        ListCell* lc = NULL;

        foreach (lc, group_list) {
            Node* node = (Node*)lfirst(lc);

            if (include_groupingset(node)) {
                return true;
            }
        }
    } else if (IsA(groupClause, GroupingSet)) {
        return true;
    }

    return false;
}

/*
 * @Description: Transform const expr in groupClause to columnref struct.
 * @in groupClause - group clause.
 * @in targetList - query targetlist.
 */
static void transformGroupConstToColumn(ParseState* pstate, Node* groupClause, List* targetList)
{
    if (groupClause == NULL) {
        return;
    }

    if (IsA(groupClause, List)) {
        List* group_list = (List*)groupClause;

        ListCell* lc = NULL;

        foreach (lc, group_list) {
            Node* node = (Node*)lfirst(lc);

            /* Replace this const by expr in targetlist. */
            if (IsA(node, A_Const)) {
                Value* val = &((A_Const*)node)->val;
                int location = ((A_Const*)node)->location;

                if (!IsA(val, Integer)) {
                    ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                            errmsg("non-integer constant in group clause"),
                            parser_errposition(pstate, location)));
                }

                long target_pos = intVal(val);
                if (target_pos > 0 && target_pos <= list_length(targetList)) {
                    TargetEntry* tle = (TargetEntry*)list_nth(targetList, target_pos - 1);
                    lfirst(lc) = copyObject(tle->expr);

                    pfree_ext(node);
                } else {
                    ereport(ERROR,
                        (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                            errmsg("%s position %ld is not in select list", "GROUP BY", target_pos),
                            parser_errposition(pstate, location)));
                }

            } else {
                transformGroupConstToColumn(pstate, node, targetList);
            }
        }
    } else if (IsA(groupClause, GroupingSet)) {
        GroupingSet* grouping_set = (GroupingSet*)groupClause;
        transformGroupConstToColumn(pstate, (Node*)grouping_set->content, targetList);
    }
}

static bool checkAllowedTableCombination(ParseState* pstate)
{
    RangeTblEntry* rte = NULL;
    ListCell* lc = NULL;
    bool has_ustore = false;
    bool has_else = false;

    // Check range table list for the presence of
    // relations with different storages
    foreach(lc, pstate->p_rtable) {
        rte = (RangeTblEntry*)lfirst(lc);
        if (rte && rte->rtekind == RTE_RELATION) {
            if (rte->is_ustore) {
                has_ustore = true;
            } else {
                has_else = true;
            }
        }
    }

    // Check target table for the type of storage
    foreach(lc, pstate->p_target_rangetblentry) {
        rte = (RangeTblEntry*)lfirst(lc);
        if (rte && rte->rtekind == RTE_RELATION) {
            if (rte->is_ustore) {
                has_ustore = true;
            } else {
                has_else = true;
            }
        }
    }

    Assert(has_ustore || has_else);

    return !(has_ustore && has_else);
}

static bool IsValuesCanTransformDirectly(ParseState* pstate, InsertStmt* stmt)
{
    if (!u_sess->attr.attr_sql.enableParseFusion ||
        stmt->is_dist_insertselect) {
        return false;
    }

    if (stmt->returningList || stmt->withClause
        || stmt->upsertClause || stmt->targetList) {
        return false;
    }
    
    SelectStmt* selectStmt = (SelectStmt*)stmt->selectStmt;
    if (selectStmt == nullptr || selectStmt->valuesLists == NIL) {
        return false;
    }

    foreach_cell(cell, stmt->cols) {
        ResTarget* col = (ResTarget*)lfirst(cell);
        if (col->indirection) {
            return false;
        }
    }

    const int firstCnt = list_length((List*)linitial(selectStmt->valuesLists));
    foreach_cell (rowCell, selectStmt->valuesLists) {
        List* row = (List*)lfirst(rowCell);
        int curCnt = list_length(row);
        if (unlikely(curCnt != firstCnt)) {
            ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                    errmsg("VALUES lists must all be the same length"),
                    parser_errposition(pstate, exprLocation((Node*)row))));
        }

        foreach_cell (conCell, row) {
            Node* con = (Node*)lfirst(conCell);
            if (!IsA(con, A_Const)) {
                return false;
            }
            Value* val = &((A_Const*)con)->val;
            switch (nodeTag(val)) {
                case T_Integer:
                case T_Float:
                case T_String:
                case T_Null:
                    break;
                default:
                    return false;
            }
        }
    }

    return true;
}

static void CheckColumnExprsConsistency(ParseState* pstate, List* cols, List* firstRowList, int finalTargetCnt)
{
    const int firstRowCnt = list_length(firstRowList);
    if (unlikely(firstRowCnt > finalTargetCnt)) {
        Node* posNode = (Node*)list_nth(firstRowList, finalTargetCnt);
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("INSERT has more expressions than target columns"),
                parser_errposition(pstate, exprLocation(posNode))));
    } else if (unlikely(cols && firstRowCnt < finalTargetCnt)) {
        Node* posNode = (Node*)list_nth(cols, firstRowCnt);
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
                errmsg("INSERT has more target columns than expressions"),
                parser_errposition(pstate, exprLocation(posNode))));
    }
}

static List* GetTargetColumnAttrs(ParseState* pstate, List* cols, int exprCnt)
{
    List* targetColsAttrs = NIL;
    Relation targetrel = (Relation)linitial(pstate->p_target_relation);
    FormData_pg_attribute* attrArr = targetrel->rd_att->attrs;

    if (cols == NIL) {
        const int numcol = RelationGetNumberOfAttributes(targetrel);
        bool isBlockchainRel = targetrel->rd_isblockchain;

        for (int i = 0; i < numcol && i < exprCnt; i++) {
            if (attrArr[i].attisdropped) {
                continue;
            }
            /* If the hidden column in timeseries relation, skip it */
            if (TsRelWithImplDistColumn(attrArr, i) &&
                RelationIsTsStore(targetrel)) {
                continue;
            }

            if (isBlockchainRel &&
                strcmp(NameStr(attrArr[i].attname), "hash") == 0) {
                continue;
            }
            targetColsAttrs = lappend(targetColsAttrs, &attrArr[i]);
        }
    } else {
        /* obtains columns attributes */
        Bitmapset* wholecols = NULL;
        foreach_cell(cell, cols) {
            ResTarget* col = (ResTarget*)lfirst(cell);
            char* name = col->name;
            int attrno = attnameAttNum(targetrel, name, false);
            if (attrno == InvalidAttrNumber) {
                ereport(ERROR,
                    (errcode(ERRCODE_UNDEFINED_COLUMN),
                        errmsg("column \"%s\" of relation \"%s\" does not exist",
                            name, RelationGetRelationName(targetrel)),
                        parser_errposition(pstate, col->location)));
            }

            if (bms_is_member(attrno, wholecols)) {
                ereport(ERROR,
                    (errcode(ERRCODE_DUPLICATE_COLUMN),
                        errmsg("column \"%s\" specified more than once", name),
                        parser_errposition(pstate, col->location)));
            }

            wholecols = bms_add_member(wholecols, attrno);
            targetColsAttrs = lappend(targetColsAttrs, &attrArr[attrno - 1]);
        }
    }

    return targetColsAttrs;
}

static void GenerateTargetList(Query* query, RangeTblEntry* rte, int rtindex, List* targetColsAttrs)
{
    int varattno = 0;

    query->targetList = NIL;
    foreach_cell(colCell, targetColsAttrs) {
        FormData_pg_attribute* col = (FormData_pg_attribute*)lfirst(colCell);
        varattno++;

        Var* expr = makeVar(rtindex,
                            varattno,
                            col->atttypid,  /* type oid */
                            col->atttypmod, /* type mode */
                            InvalidOid,     /* colcollation */
                            0);             /* sublevels_up */

        expr->location = -1;
        
        char* name = pstrdup(NameStr(col->attname));
        TargetEntry* entry = makeTargetEntry((Expr*)expr, col->attnum, name, false);
        query->targetList = lappend(query->targetList, entry);

        rte->insertedCols = bms_add_member(rte->insertedCols, col->attnum - FirstLowInvalidHeapAttributeNumber);
    }
}

static void GetColumnTypeAttrs(List* targetColsAttrs, ColumnTypeForm* typeItems)
{
    HeapTuple tup = nullptr;
    Form_pg_type typeStruct = nullptr;
    int colIdx = 0;

    foreach_cell(attrCell, targetColsAttrs) {
        FormData_pg_attribute* attr = (FormData_pg_attribute*)lfirst(attrCell);
        ColumnTypeForm& typeItem = typeItems[colIdx];
        typeItem.originTypOid = attr->atttypid;
        typeItem.baseTypOid = attr->atttypid;

        /*
        * We loop to find the bottom base type in a stack of domains.
        */
        for (;;) {
            tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeItem.baseTypOid));
            if (unlikely(!HeapTupleIsValid(tup))) {
                ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
                                errmsg("cache lookup failed for type %u", typeItem.baseTypOid)));
            }

            typeStruct = (Form_pg_type)GETSTRUCT(tup);
            if (unlikely(typeStruct->typtype == TYPTYPE_DOMAIN)) {
                typeItem.baseTypOid = typeStruct->typbasetype;
                ReleaseSysCache(tup);
            } else {
                typeItem.typmod = attr->atttypmod;
                typeItem.collid = typeStruct->typcollation;
                typeItem.typlen = typeStruct->typlen;
                typeItem.typbyval = typeStruct->typbyval;
                typeItem.typinput = typeStruct->typinput;
                typeItem.ioParam = getTypeIOParam(tup);
                ReleaseSysCache(tup);
                break;
            }
        }

        ++colIdx;
    }
}

Datum TransformCstringToTarget(ColumnTypeForm& typeItem, char* string, bool hasIgnore)
{
    switch (typeItem.typinput) {
        case F_DATE_IN:
            return input_date_in(string, hasIgnore);
        case F_BPCHARIN:
            return input_bpcharin(string, typeItem.ioParam, typeItem.typmod);
        case F_VARCHARIN:
            return input_varcharin(string, typeItem.ioParam, typeItem.typmod);
        case F_TIMESTAMP_IN:
            return input_timestamp_in(string, typeItem.ioParam, typeItem.typmod, hasIgnore);
        default:
            return OidInputFunctionCall(typeItem.typinput,
                                        string,
                                        typeItem.ioParam,
                                        typeItem.typmod,
                                        hasIgnore);
    }
}

static Datum ConvertIntValue(ColumnTypeForm& typeItem, int32 value, bool hasIgnore)
{
    switch (typeItem.baseTypOid) {
        case INT4OID:
            return Int32GetDatum(value);
        case INT8OID:
            return Int64GetDatum((int64)value);
        case INT1OID:
            if (unlikely(value < 0 || value > UCHAR_MAX)) {
                if (hasIgnore) {
                    ereport(WARNING, (errmsg("tinyint out of range")));
                    return UInt8GetDatum((uint8)(value < 0 ? 0 : UCHAR_MAX));
                }
                ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("tinyint out of range")));
            }
            return UInt8GetDatum((uint8)value);
        case INT2OID:
            if (unlikely(value < PG_INT16_MIN || value > PG_INT16_MAX)) {
                if (hasIgnore) {
                    ereport(WARNING, (errmsg("smallint out of range")));
                    return Int16GetDatum((int16)(value < PG_INT16_MIN ? PG_INT16_MIN : PG_INT16_MAX));
                }
                ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("smallint out of range")));
            }
            return Int16GetDatum((int16)value);
        default: {
            char* inputStr = (char*)palloc(MAX_INT32_LEN + 1);
            pg_ltoa(value, inputStr);
            return OidInputFunctionCall(typeItem.typinput,
                                        inputStr,
                                        typeItem.ioParam,
                                        typeItem.typmod,
                                        hasIgnore);
        }
    }
}

static Node* TransformAconstToTarget(A_Const* con, ColumnTypeForm& typeItem, bool hasIgnore)
{
    Value* value = &con->val;
    Const* newcon = makeNode(Const);

    newcon->consttype = typeItem.baseTypOid;
    newcon->consttypmod = typeItem.typmod;
    newcon->constcollid = typeItem.collid;
    newcon->constlen = typeItem.typlen;
    newcon->constbyval = typeItem.typbyval;
    newcon->cursor_data.cur_dno = -1;
    newcon->location = con->location;

    switch (nodeTag(value)) {
        case T_Integer: {
            int32 val = intVal(value);
            newcon->constisnull = false;
            newcon->constvalue = ConvertIntValue(typeItem, val, hasIgnore);
            break;
        }
        case T_Float:
        case T_String: {
            char* inputStr = strVal(value);
            newcon->constisnull = false;
            newcon->constvalue = TransformCstringToTarget(typeItem, inputStr, hasIgnore);
            break;
        }
        default: {
            /* only T_Null tag can come here */
            Assert(IsA(value, Null));
            newcon->constisnull = true;
            newcon->constvalue = 0;
            break;
        }
    }

    /* If target is not a domain, return directly,
     * else apply constraints.
     */
    if (likely(typeItem.baseTypOid == typeItem.originTypOid)) {
        return (Node*)newcon;
    } else {
        /*
         * Now build the domain coercion node. This represents run-time checking
         * of any constraints currently attached to the domain.  This also ensures
         * that the expression is properly labeled as to result type.
         */
        CoerceToDomain* domain = makeNode(CoerceToDomain);
        domain->arg = (Expr*)newcon;
        domain->resulttype = typeItem.originTypOid;
        domain->resulttypmod = -1;
        domain->coercionformat = COERCE_IMPLICIT_CAST;
        domain->location = con->location;
        return (Node*)domain;
    }
}

static void CheckInsertTargetRelation(ParseState* pstate, InsertStmt* stmt, Relation targetrel, bool isRelationNullOk)
{
    if (unlikely(targetrel == NULL)) {
        if (isRelationNullOk) {
            return;
        }
        ereport(ERROR, (errmodule(MOD_OPT),
                       errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                       errmsg("targetrel is NULL unexpectedly")));
    }

    /*
     * Insert into relation pg_auth_history is not allowed.
     * We update it only when some user's password has been changed.
     */
    if (unlikely(RelationGetRelid(targetrel) == AuthHistoryRelationId)) {
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_OPERATION),
             errmsg("Not allowed to insert into relation pg_auth_history.")));
    }
    if (((unsigned int)RelationGetInternalMask(targetrel) & INTERNAL_MASK_DINSERT)) {
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                errmsg("Un-support feature"),
                errdetail("internal relation doesn't allow INSERT")));
    }

#ifdef ENABLE_MULTIPLE_NODES
    if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
#endif
        if (unlikely(RelationIsMatview(targetrel) && !stmt->isRewritten)) {
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                            errmsg("Unsupported feature"),
                            errdetail("Materialized view doesn't allow INSERT")));
        }
#ifdef ENABLE_MULTIPLE_NODES
    }
#endif

    if (stmt->upsertClause != NULL) {
        /* non-supported upsert cases */
        if (unlikely(!u_sess->attr.attr_sql.enable_upsert_to_merge && RelationIsColumnFormat(targetrel))) {
            ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on column orientated table."))));
        }

        if (unlikely(RelationIsForeignTable(targetrel) || RelationIsStream(targetrel))) {
            ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on foreign table."))));
        }

        if (unlikely(RelationIsView(targetrel))) {
            ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                            errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on VIEW."))));
        }

        if (unlikely(RelationIsContquery(targetrel))) {
            ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                            errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on CONTQUERY."))));
        }
    }

    /* non-supported IGNORE cases */
    if (unlikely(pstate->p_has_ignore && RelationIsColumnFormat(targetrel))) {
        ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                         errmsg("IGNORE is not supported on INSERT column orientated table."))));
    }

    if (unlikely(!u_sess->attr.attr_sql.enable_cluster_resize &&
        RelationInClusterResizingWriteErrorMode(targetrel))) {
        ereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
                errmsg("%s is redistributing, please retry later.", targetrel->rd_rel->relname.data)));
    }
}

static void ParseColumnErrorCallback(void* arg)
{
    ParseColumnCallbackState* pcbstate = (ParseColumnCallbackState*)arg;
    if (geterrcode() != ERRCODE_QUERY_CANCELED) {
        (void)parser_errposition(pcbstate->pstate, pcbstate->location);
    }

    FormData_pg_attribute* attr = (FormData_pg_attribute*)lfirst(pcbstate->attrCell);
    char* colname = pstrdup_ext(NameStr(attr->attname));
    if (colname != NULL && strcmp(colname, "?column?")) {
        errcontext("referenced column: %s", colname);
    }
}

static List* TransformAllValuesDirectly(ParseState* pstate, SelectStmt* selectStmt, List* targetColsAttrs)
{
    List* allValuesList = nullptr;
    bool hasIgnore = pstate->p_has_ignore;
    const int colCnt = list_length(targetColsAttrs);

    ColumnTypeForm colTypeItems[colCnt];
    GetColumnTypeAttrs(targetColsAttrs, colTypeItems);

   /* init ParseColumnCallbackState */
    ParseColumnCallbackState pcbstate;
    pcbstate.pstate = pstate;
    pcbstate.location = 0;
    pcbstate.attrCell = nullptr;
    pcbstate.errcontext.callback = ParseColumnErrorCallback;
    pcbstate.errcontext.arg = (void*)&pcbstate;

    foreach_cell (recordCell, selectStmt->valuesLists) {
        List* record = (List*)lfirst(recordCell);
        List* oneValues = nullptr;
        ListCell* valCell = nullptr;
        ListCell* attrCell = nullptr;
        int colIdx = 0;
        forboth(valCell, record, attrCell, targetColsAttrs) {
            A_Const* acon = (A_Const*)lfirst(valCell);

            /*
             * set up to point at the constant's text and the column name
             * if the input routine throws an error.
             */
            pcbstate.location = acon->location;
            pcbstate.attrCell= attrCell;
            pcbstate.errcontext.previous = t_thrd.log_cxt.error_context_stack;
            t_thrd.log_cxt.error_context_stack = &pcbstate.errcontext;

            /* transform const value to target column type value */
            Node* result = TransformAconstToTarget(acon, colTypeItems[colIdx], hasIgnore);
            oneValues = lappend(oneValues, result);
            ++colIdx;

            /* Pop the error context stack */
            t_thrd.log_cxt.error_context_stack = pcbstate.errcontext.previous;
        }

        allValuesList = lappend(allValuesList, oneValues);
    }

    return allValuesList;
}

static Query* TryTransformInsertDirectly(ParseState* pstate, InsertStmt* stmt)
{
    if (!IsValuesCanTransformDirectly(pstate, stmt)) {
        return nullptr;
    }

    pstate->p_is_insert = true;
    pstate->p_has_ignore = stmt->hasIgnore;

    AclMode targetPerms = ACL_INSERT;
    if (stmt->isReplace) {
        targetPerms |= ACL_DELETE;
    }
    Query* query = makeNode(Query);
    query->commandType = CMD_INSERT;
    query->isReplace = stmt->isReplace;
    query->is_dist_insertselect = false;
    query->resultRelations = setTargetTables(pstate, list_make1(stmt->relation), false, false, targetPerms);

    Relation targetrel = (Relation)linitial(pstate->p_target_relation);
    CheckInsertTargetRelation(pstate, stmt, targetrel, false);

    SelectStmt* selectStmt = (SelectStmt*)stmt->selectStmt;
    const int exprCnt = list_length((List*)linitial(selectStmt->valuesLists));
    List* targetColsAttrs = GetTargetColumnAttrs(pstate, stmt->cols, exprCnt);
    const int colCnt = list_length(targetColsAttrs);
    CheckColumnExprsConsistency(pstate, stmt->cols, (List*)linitial(selectStmt->valuesLists), colCnt);

    List* allValuesList = NULL;
    const Oid conCollaOId = GetCollationConnection();
    const int conEncoding = get_valid_charset_by_collation(conCollaOId);
    const int dbEncoding = GetDatabaseEncoding();
    if (likely(conEncoding == dbEncoding)) {
        allValuesList = TransformAllValuesDirectly(pstate, selectStmt, targetColsAttrs);
    } else {
        DB_ENCODING_SWITCH_TO(conEncoding);
        allValuesList = TransformAllValuesDirectly(pstate, selectStmt, targetColsAttrs);
        DB_ENCODING_SWITCH_BACK(dbEncoding);
    }

    List* collations = NIL;
    for (int i = 0; i < colCnt; i++) {
        collations = lappend_oid(collations, InvalidOid);
    }

    RangeTblEntry* rte = addRangeTableEntryForValues(pstate, allValuesList, collations, NULL, true);
    const int ridx = list_length(pstate->p_rtable);
    GenerateTargetList(query, rte, ridx, targetColsAttrs);

    query->rtable = pstate->p_rtable;
    RangeTblRef* tblRef = makeNode(RangeTblRef);
    tblRef->rtindex = ridx;
    pstate->p_joinlist = lappend(pstate->p_joinlist, tblRef);
    query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
    query->tdTruncCastStatus = pstate->tdTruncCastStatus;
    query->hasIgnore = stmt->hasIgnore;
    query->hintState = stmt->hintState;
    query->hasSubLinks = false;
    query->hasTargetSRFs = false;

    return query;
}