diff --git a/config/c-compiler.m4 b/config/c-compiler.m4 index eabc89288..c0172e922 100644 --- a/config/c-compiler.m4 +++ b/config/c-compiler.m4 @@ -149,6 +149,30 @@ fi])# PGAC_CHECK_BUILTIN_FUNC +# PGAC_C_COMPUTED_GOTO +# ----------------------- +# Check if the C compiler knows computed gotos (gcc extension, also +# available in at least clang). If so, define HAVE_COMPUTED_GOTO. +# +# Checking whether computed gotos are supported syntax-wise ought to +# be enough, as the syntax is otherwise illegal. +AC_DEFUN([PGAC_C_COMPUTED_GOTO], +[AC_CACHE_CHECK(for computed goto support, pgac_cv_computed_goto, +[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], +[[void *labeladdrs[] = {&&my_label}; + goto *labeladdrs[0]; + my_label: + return 1; +]])], +[pgac_cv_computed_goto=yes], +[pgac_cv_computed_goto=no])]) +if test x"$pgac_cv_computed_goto" = xyes ; then +AC_DEFINE(HAVE_COMPUTED_GOTO, 1, + [Define to 1 if your compiler handles computed gotos.]) +fi])# PGAC_C_COMPUTED_GOTO + + + # PGAC_PROG_CC_CFLAGS_OPT # ----------------------- # Given a string, check if the compiler supports the string as a diff --git a/configure b/configure index e37058133..716762c89 100755 --- a/configure +++ b/configure @@ -15559,6 +15559,42 @@ _ACEOF fi fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for computed goto support" >&5 +$as_echo_n "checking for computed goto support... " >&6; } +if ${pgac_cv_computed_goto+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +void *labeladdrs[] = {&&my_label}; + goto *labeladdrs[0]; + my_label: + return 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + pgac_cv_computed_goto=yes +else + pgac_cv_computed_goto=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_computed_goto" >&5 +$as_echo "$pgac_cv_computed_goto" >&6; } +if test x"$pgac_cv_computed_goto" = xyes ; then + +$as_echo "#define HAVE_COMPUTED_GOTO 1" >>confdefs.h + +fi + { $as_echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 $as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } if test "${ac_cv_struct_tm+set}" = set; then diff --git a/contrib/gauss_connector/gc_fdw.cpp b/contrib/gauss_connector/gc_fdw.cpp index 8e6ab2512..15bcd2d80 100644 --- a/contrib/gauss_connector/gc_fdw.cpp +++ b/contrib/gauss_connector/gc_fdw.cpp @@ -2046,7 +2046,7 @@ static void prepare_query_params(PlanState* node, List* fdw_exprs, int numParams * benefit, and it'd require gc_fdw to know more than is desirable * about Param evaluation.) */ - *param_exprs = (List *)ExecInitExpr((Expr *)fdw_exprs, node); + *param_exprs = ExecInitExprList(fdw_exprs, node); /* Allocate buffer for text form of query parameters. */ *param_values = (const char **)palloc0(numParams * sizeof(char *)); @@ -2068,7 +2068,7 @@ static void process_query_params( bool isNull = false; /* Evaluate the parameter expression */ - expr_value = ExecEvalExpr(expr_state, econtext, &isNull, NULL); + expr_value = ExecEvalExpr(expr_state, econtext, &isNull); /* * Get string representation of each parameter value by invoking diff --git a/contrib/postgres_fdw/postgres_fdw.cpp b/contrib/postgres_fdw/postgres_fdw.cpp index 894b23823..91895286f 100644 --- a/contrib/postgres_fdw/postgres_fdw.cpp +++ b/contrib/postgres_fdw/postgres_fdw.cpp @@ -3286,7 +3286,7 @@ static HeapTuple make_tuple_from_result_row(PGresult *res, int row, Relation rel if (OidIsValid(oid)) { HeapTupleSetOid(tuple, oid); } - + /* * If we have an TABLEOID to return, install it. */ @@ -3379,7 +3379,7 @@ static void conversion_error_callback(void *arg) } } - if (relname && is_wholerow){ + if (relname && is_wholerow){ errcontext("whole-row reference to foreign table \"%s\"", relname); } else if (relname && attname) { errcontext("column \"%s\" of foreign table \"%s\"", attname, relname); @@ -3640,8 +3640,8 @@ static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType joi * * The joining sides can not have local conditions, thus no need to test * shippability of the clauses being pulled up. - * - * The foreign join path is not necessarily better than join foreign scan + * + * The foreign join path is not necessarily better than join foreign scan * path, so we should use list_concat2. */ switch (jointype) { @@ -3928,7 +3928,7 @@ static void postgresGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, * dominate the only suitable local path available. We also do it before * calling foreign_join_ok(), since that function updates fpinfo and marks * it as pushable if the join is found to be pushable. - * + * * currently not support. */ if (root->parse->commandType == CMD_DELETE || root->parse->commandType == CMD_UPDATE || root->rowMarks) { @@ -4212,12 +4212,12 @@ static void init_upperrel_paths_context(FDWUpperRelCxt* ufdwCxt, Plan* mainPlan) Assert(ufdwCxt->root != NULL && ufdwCxt->currentRel != NULL && ufdwCxt->spjExtra != NULL); PlannerInfo* root = ufdwCxt->root; - + if (root->parse->groupingSets) { ufdwCxt->state = FDW_UPPER_REL_END; return; } - + /* if the spj rel is unsafe to push down, upper rel is unsafe alse. */ PgFdwRelationInfo* fpinfo = (PgFdwRelationInfo *)ufdwCxt->currentRel->fdw_private; if (!fpinfo->pushdown_safe) { @@ -4311,7 +4311,7 @@ static void _postgresGetForeignUpperPaths(FDWUpperRelCxt *ufdw_cxt, UpperRelatio fpinfo->stage = stage; output_rel = make_upper_rel(ufdw_cxt, fpinfo); - + switch (stage) { case UPPERREL_GROUP_AGG: add_foreign_grouping_paths(ufdw_cxt->root, input_rel, output_rel, ufdw_cxt->groupExtra); @@ -4560,7 +4560,7 @@ static void add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, Re */ if (!parse->rowMarks && !extra->limit_needed) { ListCell* lc = NULL; - Path* path = NULL; + Path* path = NULL; ForeignPath *final_path = NULL; foreach(lc, input_rel->pathlist) { path = (Path*)lfirst(lc); @@ -4571,7 +4571,7 @@ static void add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, Re /* and add it to the final_rel */ add_path(root, final_rel, (Path *)final_path); - + /* Safe to push down */ fpinfo->pushdown_safe = true; } diff --git a/src/common/backend/catalog/index.cpp b/src/common/backend/catalog/index.cpp index 64e58a7ae..7e99bbf96 100644 --- a/src/common/backend/catalog/index.cpp +++ b/src/common/backend/catalog/index.cpp @@ -2834,7 +2834,7 @@ void FormIndexDatum(IndexInfo* indexInfo, TupleTableSlot* slot, EState* estate, if (indexInfo->ii_Expressions != NIL && indexInfo->ii_ExpressionsState == NIL) { /* First time through, set up expression evaluation state */ - indexInfo->ii_ExpressionsState = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Expressions, estate); + indexInfo->ii_ExpressionsState = ExecPrepareExprList(indexInfo->ii_Expressions, estate); /* Check caller has set up context correctly */ Assert(GetPerTupleExprContext(estate)->ecxt_scantuple == slot); } @@ -3843,7 +3843,12 @@ double IndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexIn econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + if (estate->es_is_flt_frame){ + predicate = (List*)ExecPrepareQualByFlatten(indexInfo->ii_Predicate, estate); + } else { + predicate = (List*)ExecPrepareExpr((Expr *)indexInfo->ii_Predicate, estate); + } + /* * Prepare for scan of the base relation. In a normal index build, we use @@ -4136,7 +4141,7 @@ double IndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexIn * predicate. */ if (predicate != NIL) { - if (!ExecQual(predicate, econtext, false)) { + if (!ExecQual(predicate, econtext)) { continue; } } @@ -4240,7 +4245,11 @@ double IndexBuildUHeapScan(Relation heapRelation, Relation indexRelation, IndexI econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *)ExecPrepareExpr((Expr *)indexInfo->ii_Predicate, estate); + if (estate->es_is_flt_frame){ + predicate = (List*)ExecPrepareQualByFlatten(indexInfo->ii_Predicate, estate); + } else { + predicate = (List*)ExecPrepareExpr((Expr *)indexInfo->ii_Predicate, estate); + } if (indexInfo->ii_Concurrent) { ereport(ERROR, @@ -4283,7 +4292,7 @@ double IndexBuildUHeapScan(Relation heapRelation, Relation indexRelation, IndexI * predicate. */ if (predicate != NIL) { - if (!ExecQual(predicate, econtext, false)) { + if (!ExecQual(predicate, econtext)) { continue; } } @@ -4493,7 +4502,11 @@ double IndexBuildVectorBatchScan(Relation heapRelation, Relation indexRelation, econtext = GetPerTupleExprContext(estate); slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation)); econtext->ecxt_scantuple = slot; - predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + if (estate->es_is_flt_frame){ + predicate = (List*)ExecPrepareQualByFlatten(indexInfo->ii_Predicate, estate); + } else { + predicate = (List*)ExecPrepareExpr((Expr *)indexInfo->ii_Predicate, estate); + } List* vars = pull_var_clause((Node*)indexInfo->ii_Expressions, PVC_RECURSE_AGGREGATES, PVC_RECURSE_PLACEHOLDERS); @@ -4528,7 +4541,7 @@ double IndexBuildVectorBatchScan(Relation heapRelation, Relation indexRelation, (void)ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); if (predicate != NIL) { - if (!ExecQual(predicate, econtext, false)) { + if (!ExecQual(predicate, econtext)) { continue; } } @@ -4613,7 +4626,11 @@ static void IndexCheckExclusion(Relation heapRelation, Relation indexRelation, I econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + if (estate->es_is_flt_frame){ + predicate = (List*)ExecPrepareQualByFlatten(indexInfo->ii_Predicate, estate); + } else { + predicate = (List*)ExecPrepareExpr((Expr *)indexInfo->ii_Predicate, estate); + } /* * Scan all live tuples in the base relation. @@ -4637,7 +4654,7 @@ static void IndexCheckExclusion(Relation heapRelation, Relation indexRelation, I * In a partial index, ignore tuples that don't satisfy the predicate. */ if (predicate != NIL) { - if (!ExecQual(predicate, econtext, false)) { + if (!ExecQual(predicate, econtext)) { continue; } } @@ -4889,7 +4906,11 @@ void validate_index_heapscan( econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + if (estate->es_is_flt_frame){ + predicate = (List*)ExecPrepareQualByFlatten(indexInfo->ii_Predicate, estate); + } else { + predicate = (List*)ExecPrepareExpr((Expr *)indexInfo->ii_Predicate, estate); + } /* * Prepare for scan of the base relation. We need just those tuples @@ -4991,7 +5012,7 @@ void validate_index_heapscan( * predicate. */ if (predicate != NIL) { - if (!ExecQual(predicate, econtext, false)) { + if (!ExecQual(predicate, econtext)) { continue; } } @@ -6347,7 +6368,11 @@ void ScanHeapInsertCBI(Relation parentRel, Relation heapRel, Relation idxRel, Oi slot = MakeSingleTupleTableSlot(RelationGetDescr(parentRel), false, parentRel->rd_tam_ops); econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List*)ExecPrepareExpr((Expr*)idxInfo->ii_Predicate, estate); + if (estate->es_is_flt_frame){ + predicate = (List*)ExecPrepareQualByFlatten(idxInfo->ii_Predicate, estate); + } else { + predicate = (List*)ExecPrepareExpr((Expr *)idxInfo->ii_Predicate, estate); + } scan = scan_handler_tbl_beginscan(heapRel, SnapshotAny, 0, NULL, NULL, true); if (scan == NULL) { @@ -6503,7 +6528,7 @@ void ScanHeapInsertCBI(Relation parentRel, Relation heapRel, Relation idxRel, Oi * predicate. */ if (predicate != NIL) { - if (!ExecQual(predicate, econtext, false)) { + if (!ExecQual(predicate, econtext)) { continue; } } diff --git a/src/common/backend/parser/parse_node.cpp b/src/common/backend/parser/parse_node.cpp index cd9c52cdc..88ed12930 100644 --- a/src/common/backend/parser/parse_node.cpp +++ b/src/common/backend/parser/parse_node.cpp @@ -60,6 +60,7 @@ ParseState* make_parsestate(ParseState* parentParseState) pstate->p_rawdefaultlist = NIL; pstate->p_has_ignore = false; pstate->p_indexhintLists = NIL; + pstate->p_is_flt_frame = false; if (parentParseState != NULL) { pstate->p_sourcetext = parentParseState->p_sourcetext; diff --git a/src/common/backend/pgxc_single/pool/execRemote.cpp b/src/common/backend/pgxc_single/pool/execRemote.cpp index 4f633d615..f67418721 100755 --- a/src/common/backend/pgxc_single/pool/execRemote.cpp +++ b/src/common/backend/pgxc_single/pool/execRemote.cpp @@ -3768,8 +3768,12 @@ RemoteQueryState* ExecInitRemoteQuery(RemoteQuery* node, EState* estate, int efl ExecAssignExprContext(estate, &remotestate->ss.ps); /* Initialise child expressions */ - remotestate->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->scan.plan.targetlist, (PlanState*)remotestate); - remotestate->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->scan.plan.qual, (PlanState*)remotestate); + if (estate->es_is_flt_frame) { + remotestate->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)remotestate); + } else { + remotestate->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)remotestate); + remotestate->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)remotestate); + } /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_MARK))); @@ -3787,9 +3791,7 @@ RemoteQueryState* ExecInitRemoteQuery(RemoteQuery* node, EState* estate, int efl ExecInitScanTupleSlot(estate, &remotestate->ss); scan_type = ExecTypeFromTL(node->base_tlist, false); ExecAssignScanType(&remotestate->ss, scan_type); - - remotestate->ss.ps.ps_TupFromTlist = false; - + remotestate->ss.ps.ps_vec_TupFromTlist = false; /* * If there are parameters supplied, get them into a form to be sent to the * Datanodes with bind message. We should not have had done this before. diff --git a/src/common/backend/utils/adt/Makefile b/src/common/backend/utils/adt/Makefile index a1bbf8c1c..0f78a5fe8 100644 --- a/src/common/backend/utils/adt/Makefile +++ b/src/common/backend/utils/adt/Makefile @@ -39,7 +39,7 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \ tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \ tsvector.o tsvector_op.o tsvector_parser.o \ txid.o uuid.o windowfuncs.o xml.o extended_statistics.o clientlogic_bytea.o clientlogicsettings.o \ - median_aggs.o expr_distinct.o nlssort.o memory_func.o first_last_agg.o encrypt_decrypt.o + median_aggs.o expr_distinct.o nlssort.o memory_func.o first_last_agg.o encrypt_decrypt.o expandeddatum.o like.o: like.cpp like_match.cpp diff --git a/src/common/backend/utils/adt/domains.cpp b/src/common/backend/utils/adt/domains.cpp index c0df770c0..09bd31e30 100644 --- a/src/common/backend/utils/adt/domains.cpp +++ b/src/common/backend/utils/adt/domains.cpp @@ -114,9 +114,6 @@ static void domain_check_input(Datum value, bool isnull, DomainIOData* my_extra) errmsg("domain %s does not allow null values", format_type_be(my_extra->domain_type)))); break; case DOM_CONSTRAINT_CHECK: { - Datum conResult; - bool conIsNull = false; - /* Make the econtext if we didn't already */ if (econtext == NULL) { MemoryContext oldcontext; @@ -136,9 +133,16 @@ static void domain_check_input(Datum value, bool isnull, DomainIOData* my_extra) econtext->domainValue_datum = value; econtext->domainValue_isNull = isnull; - conResult = ExecEvalExprSwitchContext(con->check_expr, econtext, &conIsNull, NULL); - - if (!conIsNull && !DatumGetBool(conResult)) + bool fit_check = false; + if (con->check_expr && con->check_expr->is_flt_frame){ + fit_check = ExecCheckByFlatten(con->check_expr, econtext); + } else{ + bool conIsNull = false; + Datum conResult = ExecEvalExprSwitchContext(con->check_expr, econtext, &conIsNull,NULL); + fit_check =(conIsNull || DatumGetBool(conResult)); + } + + if (!fit_check) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("value for domain %s violates check constraint \"%s\"", diff --git a/src/common/backend/utils/adt/expandeddatum.cpp b/src/common/backend/utils/adt/expandeddatum.cpp new file mode 100644 index 000000000..c12a5356a --- /dev/null +++ b/src/common/backend/utils/adt/expandeddatum.cpp @@ -0,0 +1,16 @@ +#include "postgres.h" +#include "utils/expandeddatum.h" + + +/* + * If the Datum represents a R/W expanded object, change it to R/O. + * Otherwise return the original Datum. + * + * Caller must ensure that the datum is a non-null varlena value. Typically + * this is invoked via MakeExpandedObjectReadOnly(), which checks that. + */ +Datum +MakeExpandedObjectReadOnlyInternal(Datum d) +{ + return d; +} \ No newline at end of file diff --git a/src/common/backend/utils/adt/numeric.cpp b/src/common/backend/utils/adt/numeric.cpp index 5a753ae05..17072f023 100644 --- a/src/common/backend/utils/adt/numeric.cpp +++ b/src/common/backend/utils/adt/numeric.cpp @@ -56,6 +56,37 @@ typedef struct { NumericVar step; } generate_series_numeric_fctx; +/* ---------- + * Fast sum accumulator. + * + * NumericSumAccum is used to implement SUM(), and other standard aggregates + * that track the sum of input values. It uses 32-bit integers to store the + * digits, instead of the normal 16-bit integers (with NBASE=10000). This + * way, we can safely accumulate up to NBASE - 1 values without propagating + * carry, before risking overflow of any of the digits. 'num_uncarried' + * tracks how many values have been accumulated without propagating carry. + * + * Positive and negative values are accumulated separately, in 'pos_digits' + * and 'neg_digits'. This is simpler and faster than deciding whether to add + * or subtract from the current value, for each new value (see sub_var() for + * the logic we avoid by doing this). Both buffers are of same size, and + * have the same weight and scale. In accum_sum_final(), the positive and + * negative sums are added together to produce the final result. + * + * When a new value has a larger ndigits or weight than the accumulator + * currently does, the accumulator is enlarged to accommodate the new value. + * We normally have one zero digit reserved for carry propagation, and that + * is indicated by the 'have_carry_space' flag. When accum_sum_carry() uses + * up the reserved digit, it clears the 'have_carry_space' flag. The next + * call to accum_sum_add() will enlarge the buffer, to make room for the + * extra digit, and set the flag again. + * + * To initialize a new accumulator, simply reset all fields to zeros. + * + * The accumulator does not handle NaNs. + * ---------- + */ + /* ---------- * Sort support. * ---------- @@ -233,6 +264,14 @@ static void accum_sum_rescale(NumericSumAccum *accum, NumericVar *val); static void accum_sum_carry(NumericSumAccum *accum); static void accum_sum_final(NumericSumAccum *accum, NumericVar *result); +static void accum_sum_add(NumericSumAccum *accum, NumericVar *var1); +static void accum_sum_rescale(NumericSumAccum *accum, NumericVar *val); +static void accum_sum_carry(NumericSumAccum *accum); +static void accum_sum_reset(NumericSumAccum *accum); +static void accum_sum_final(NumericSumAccum *accum, NumericVar *result); +static void accum_sum_copy(NumericSumAccum *dst, NumericSumAccum *src); +static void accum_sum_combine(NumericSumAccum *accum, NumericSumAccum *accum2); + /* * @Description: call corresponding big integer operator functions. * @@ -461,7 +500,7 @@ char* output_numeric_out(Numeric num) */ init_var_from_num(num, &x); str = output_get_str_from_var(&x); - + if (TRUNC_NUMERIC_TAIL_ZERO) { remove_tail_zero(str); } @@ -3639,7 +3678,7 @@ Datum numeric_avg_numeric(PG_FUNCTION_ARGS) init_var(&sumX_var); accum_sum_final(&state->sumX, &sumX_var); sumX_datum = NumericGetDatum(make_result(&sumX_var)); - free_var(&sumX_var); + free_var(&sumX_var); PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumX_datum, N_datum)); } @@ -3647,8 +3686,7 @@ Datum numeric_sum(PG_FUNCTION_ARGS) { NumericAggState *state; NumericVar sumX_var; - Numeric result; - + Numeric result; state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0); if (state == NULL) { PG_RETURN_NULL(); @@ -3663,7 +3701,7 @@ Datum numeric_sum(PG_FUNCTION_ARGS) result = make_result(&sumX_var); free_var(&sumX_var); - PG_RETURN_NUMERIC(result); + PG_RETURN_NUMERIC(result); } static void int8_to_numericvar(int64 val, NumericVar *var) @@ -3812,7 +3850,7 @@ static Numeric numeric_stddev_internal_numeric(NumericAggState* state, bool vari *is_null = true; return NULL; } - + *is_null = false; if (state->isNaN) { @@ -4838,18 +4876,18 @@ static char* get_str_from_var(NumericVar* var) } /* - * output_get_str_from_var() - + * output_get_str_from_var() - * * Convert a var to text representation (guts of numeric_out). * CAUTION: var's contents may be modified by rounding! * Returns a palloc'd string. - */ + */ static char* output_get_str_from_var(NumericVar* var) { int dscale; - char* str = NULL; - char* cp = NULL; - char* endcp = NULL; + char* str = NULL; + char* cp = NULL; + char* endcp = NULL; int i; int d; NumericDigit dig; @@ -4857,18 +4895,18 @@ static char* output_get_str_from_var(NumericVar* var) #if DEC_DIGITS > 1 NumericDigit d1; -#endif - +#endif + dscale = var->dscale; - + /* * Allocate space for the result. - * + * * i is set to the # of decimal digits before decimal point. dscale is the * # of decimal digits we will print after decimal point. We may generate * as many as DEC_DIGITS-1 excess digits at the end, and in addition we * need room for sign, decimal point, null terminator. - */ + */ i = (var->weight + 1) * DEC_DIGITS; if (i <= 0) i = 1; @@ -20038,6 +20076,29 @@ Datum bool_numeric(PG_FUNCTION_ARGS) PG_RETURN_NUMERIC(res); } +/* ---------------------------------------------------------------------- + * + * Fast sum accumulator functions + * + * ---------------------------------------------------------------------- + */ + +/* + * Reset the accumulator's value to zero. The buffers to hold the digits + * are not free'd. + */ +static void + accum_sum_reset(NumericSumAccum *accum) +{ + int i; + + accum->dscale = 0; + for (i = 0; i < accum->ndigits; i++) + { + accum->pos_digits[i] = 0; + accum->neg_digits[i] = 0; + } +} static void accum_sum_add(NumericSumAccum *accum, NumericVar *val) { @@ -20311,4 +20372,40 @@ void numeric_aggfn_info_change(Oid aggfn_oid, Oid *transfn_oid, Oid *transtype, { numeric_transfn_info_change(aggfn_oid, transfn_oid, transtype); numeric_finalfn_info_change(aggfn_oid, finalfn_oid); +} + +/* + * Copy an accumulator's state. + * + * 'dst' is assumed to be uninitialized beforehand. No attempt is made at + * freeing old values. + */ +static void + accum_sum_copy(NumericSumAccum *dst, NumericSumAccum *src) +{ + dst->pos_digits = (int32*)palloc(src->ndigits * sizeof(int32)); + dst->neg_digits = (int32*)palloc(src->ndigits * sizeof(int32)); + + memcpy(dst->pos_digits, src->pos_digits, src->ndigits * sizeof(int32)); + memcpy(dst->neg_digits, src->neg_digits, src->ndigits * sizeof(int32)); + dst->num_uncarried = src->num_uncarried; + dst->ndigits = src->ndigits; + dst->weight = src->weight; + dst->dscale = src->dscale; +} + +/* + * Add the current value of 'accum2' into 'accum'. + */ +static void + accum_sum_combine(NumericSumAccum *accum, NumericSumAccum *accum2) +{ + NumericVar tmp_var; + + init_var(&tmp_var); + + accum_sum_final(accum2, &tmp_var); + accum_sum_add(accum, &tmp_var); + + free_var(&tmp_var); } \ No newline at end of file diff --git a/src/common/backend/utils/adt/xml.cpp b/src/common/backend/utils/adt/xml.cpp index f7cbbaf01..8f347b75e 100644 --- a/src/common/backend/utils/adt/xml.cpp +++ b/src/common/backend/utils/adt/xml.cpp @@ -629,6 +629,120 @@ xmltype* xmlelement(XmlExprState* xmlExpr, ExprContext* econtext) #endif } +xmltype* xmlelementByFlatten(XmlExpr *xexpr, + Datum *named_argvalue, bool *named_argnull, + Datum *argvalue, bool *argnull) +{ +#ifdef USE_LIBXML + xmltype* result = NULL; + List* named_arg_strings = NIL; + List* arg_strings = NIL; + int i; + ListCell* arg = NULL; + ListCell* narg = NULL; + PgXmlErrorContext* xmlerrcxt = NULL; + volatile xmlBufferPtr buf = NULL; + volatile xmlTextWriterPtr writer = NULL; + + /* + * All arguments are already evaluated, and their values are passed in the + * named_argvalue/named_argnull or argvalue/argnull arrays. This avoids + * issues if one of the arguments involves a call to some other function + * or subsystem that wants to use libxml on its own terms. We examine the + * original XmlExpr to identify the numbers and types of the arguments. + */ + named_arg_strings = NIL; + i = 0; + foreach(arg, xexpr->named_args) + { + Expr *e = (Expr *) lfirst(arg); + char* str = NULL; + + if (named_argnull[i]) + str = NULL; + else + str = map_sql_value_to_xml_value(named_argvalue[i], exprType((Node *)e), false); + named_arg_strings = lappend(named_arg_strings, str); + i++; + } + + i = 0; + arg_strings = NIL; + i = 0; + foreach(arg, xexpr->args) + { + Expr *e = (Expr *) lfirst(arg); + char* str = NULL; + + /* here we can just forget NULL elements immediately */ + if (!argnull[i]) + { + str = map_sql_value_to_xml_value(argvalue[i], exprType((Node *)e), true); + arg_strings = lappend(arg_strings, str); + } + i++; + } + + xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL); + + PG_TRY(); + { + buf = xmlBufferCreate(); + if (buf == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, "could not allocate xmlBuffer"); + writer = xmlNewTextWriterMemory(buf, 0); + if (writer == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, "could not allocate xmlTextWriter"); + + xmlTextWriterStartElement(writer, (xmlChar*)xexpr->name); + + forboth(arg, named_arg_strings, narg, xexpr->arg_names) + { + char* str = (char*)lfirst(arg); + char* argname = strVal(lfirst(narg)); + + if (str != NULL) + xmlTextWriterWriteAttribute(writer, (xmlChar*)argname, (xmlChar*)str); + } + + foreach (arg, arg_strings) { + char* str = (char*)lfirst(arg); + + xmlTextWriterWriteRaw(writer, (xmlChar*)str); + } + + xmlTextWriterEndElement(writer); + + /* we MUST do this now to flush data out to the buffer ... */ + xmlFreeTextWriter(writer); + writer = NULL; + + result = xmlBuffer_to_xmltype(buf); + } + PG_CATCH(); + { + if (writer) + xmlFreeTextWriter(writer); + if (buf) + xmlBufferFree(buf); + + pg_xml_done(xmlerrcxt, true); + + PG_RE_THROW(); + } + PG_END_TRY(); + + xmlBufferFree(buf); + + pg_xml_done(xmlerrcxt, false); + + return result; +#else + NO_XML_SUPPORT(); + return NULL; +#endif +} + xmltype* xmlparse(text* data, XmlOptionType xmloption_arg, bool preserve_whitespace) { #ifdef USE_LIBXML diff --git a/src/common/backend/utils/misc/guc.cpp b/src/common/backend/utils/misc/guc.cpp index c3c376e70..10b0fda46 100755 --- a/src/common/backend/utils/misc/guc.cpp +++ b/src/common/backend/utils/misc/guc.cpp @@ -1,3 +1,4 @@ + /* -------------------------------------------------------------------- * guc.c * @@ -437,7 +438,8 @@ const char* sync_guc_variable_namelist[] = {"work_mem", "track_stmt_stat_level", "track_stmt_details_size", "sql_note", - "max_error_count" + "max_error_count", + "enable_expr_fusion" }; static void set_config_sourcefile(const char* name, char* sourcefile, int sourceline); @@ -711,7 +713,7 @@ static bool isOptLineCommented(char* optLine) static const struct config_enum_entry bytea_output_options[] = { {"escape", BYTEA_OUTPUT_ESCAPE, false}, {"hex", BYTEA_OUTPUT_HEX, false}, {NULL, 0, false}}; - + static const struct config_enum_entry block_encryption_mode_options[] = {{"aes-128-cbc", AES_128_CBC, false}, {"aes-192-cbc", AES_192_CBC, false}, {"aes-256-cbc", AES_256_CBC, false}, @@ -8741,7 +8743,7 @@ static char** LockAndReadConfFile(char* ConfFileName, char* ConfTmpFileName, cha file = ConfTmpFileName; } - /* + /* * Get the process lock (filelock) first, if success, acquire thread * lock (ConfigFileLock) for config file, sleep until get the lock */ @@ -8778,19 +8780,19 @@ static char** LockAndReadConfFile(char* ConfFileName, char* ConfTmpFileName, cha * * In case of an error, we leave the original automatic * configuration file (postgresql.conf.bak) intact. - * + * * There are two locks used to ensure changing of config file * is multi-thread safe and multi-process safe: * filelock: for multi-process safe * ConfigFileLock: for multi-thread safe * Both locks must be acquired before any changes of config file. - * + * * filelock: * is a ConfFileLock, creates a file "postgresql.conf.lock" - * as a lock for the config file "postgresql.conf", and uses - * flock() to ensure modification of config file is + * as a lock for the config file "postgresql.conf", and uses + * flock() to ensure modification of config file is * multi-process safe for mogdb and gs_guc processes. - * + * * ConfigFileLock: * is a LWLock, defined in lwlocknames.txt. is a global lock * in mogdb and guarantees multi-thread safe access of config file. @@ -11419,17 +11421,17 @@ static void process_set_global_transation(Oid databaseid, Oid roleid, VariableSe * meantime. */ shdepLockAndCheckObject(DatabaseRelationId, databaseid); - + /* Permission check. */ AlterDatabasePermissionCheck(databaseid, dbname); - + char* valuestr = NULL; HeapTuple tuple = NULL; Relation rel = NULL; ScanKeyData scankey[2]; SysScanDesc scan = NULL; errno_t rc = EOK; - + /* Get the old tuple, if any. */ rel = heap_open(DbRoleSettingRelationId, RowExclusiveLock); ScanKeyInit( @@ -11438,7 +11440,7 @@ static void process_set_global_transation(Oid databaseid, Oid roleid, VariableSe &scankey[1], Anum_pg_db_role_setting_setrole, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(roleid)); scan = systable_beginscan(rel, DbRoleSettingDatidRolidIndexId, true, NULL, NUM_KEYS, scankey); tuple = systable_getnext(scan); - + if (tuple == NULL) { /* non-null valuestr means it's not RESET, so insert a new tuple */ HeapTuple newtuple = NULL; @@ -11446,12 +11448,12 @@ static void process_set_global_transation(Oid databaseid, Oid roleid, VariableSe bool nulls[Natts_pg_db_role_setting]; ListCell *head = NULL; ArrayType *a = NULL; - + rc = memset_s(values, sizeof(values), 0, sizeof(values)); securec_check(rc, "", ""); rc = memset_s(nulls, sizeof(nulls), 0, sizeof(nulls)); securec_check(rc, "", ""); - + values[Anum_pg_db_role_setting_setdatabase - 1] = ObjectIdGetDatum(databaseid); values[Anum_pg_db_role_setting_setrole - 1] = ObjectIdGetDatum(roleid); foreach(head, setstmt->args) @@ -11463,7 +11465,7 @@ static void process_set_global_transation(Oid databaseid, Oid roleid, VariableSe values[Anum_pg_db_role_setting_setconfig - 1] = PointerGetDatum(a); newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); (void)simple_heap_insert(rel, newtuple); - + /* Update indexes */ CatalogUpdateIndexes(rel, newtuple); } else { @@ -11474,21 +11476,21 @@ static void process_set_global_transation(Oid databaseid, Oid roleid, VariableSe bool isnull = false; ArrayType *a = NULL; ListCell *head = NULL; - + rc = memset_s(repl_val, sizeof(repl_val), 0, sizeof(repl_val)); securec_check(rc, "", ""); rc = memset_s(repl_null, sizeof(repl_null), 0, sizeof(repl_null)); securec_check(rc, "", ""); rc = memset_s(repl_repl, sizeof(repl_repl), 0, sizeof(repl_repl)); securec_check(rc, "", ""); - + repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true; repl_null[Anum_pg_db_role_setting_setconfig - 1] = false; - + /* Extract old value of setconfig */ Datum datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig, RelationGetDescr(rel), &isnull); a = isnull ? NULL : DatumGetArrayTypeP(datum); - + foreach (head, setstmt->args) { VariableSetStmt* vss = process_set_global_trans_args(head); @@ -11498,18 +11500,18 @@ static void process_set_global_transation(Oid databaseid, Oid roleid, VariableSe repl_val[Anum_pg_db_role_setting_setconfig - 1] = PointerGetDatum(a); newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), repl_val, repl_null, repl_repl); simple_heap_update(rel, &tuple->t_self, newtuple); - + /* Update indexes */ CatalogUpdateIndexes(rel, newtuple); } - + systable_endscan(scan); /* Close pg_db_role_setting, but keep lock till commit */ heap_close(rel, NoLock); UnlockSharedObject(DatabaseRelationId, databaseid, 0, AccessShareLock); } - + static VariableSetStmt* process_set_global_trans_args(ListCell* lcell) { DefElem *item = (DefElem *)lfirst(lcell); diff --git a/src/common/pl/plpgsql/src/pl_exec.cpp b/src/common/pl/plpgsql/src/pl_exec.cpp index d57f0cb3a..3591a9bfc 100644 --- a/src/common/pl/plpgsql/src/pl_exec.cpp +++ b/src/common/pl/plpgsql/src/pl_exec.cpp @@ -70,6 +70,7 @@ #include "storage/mot/mot_fdw.h" #endif #include "commands/event_trigger.h" +#include "executor/executor.h" extern bool checkRecompileCondition(CachedPlanSource* plansource); static const char* const raise_skip_msg = "RAISE"; @@ -3080,7 +3081,7 @@ static int exec_stmt_block(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block) * Just execute the statements in the block's body */ estate->err_text = NULL; - + #ifndef ENABLE_MULTIPLE_NODES if (u_sess->attr.attr_sql.sql_compatibility == A_FORMAT && COMPAT_CURSOR) { rc = exec_stmts_savecursor(estate, block->body); @@ -10275,6 +10276,8 @@ static bool exec_eval_simple_expr( if (expr->expr_simple_lxid != curlxid) { oldcontext = MemoryContextSwitchTo(u_sess->plsql_cxt.simple_eval_estate->es_query_cxt); expr->expr_simple_state = ExecInitExpr(expr->expr_simple_expr, NULL); + // expr->expr_simple_state = ExecInitExprForPlSql(expr->expr_simple_expr, econtext->ecxt_param_list_info, + // u_sess->plsql_cxt.simple_eval_estate->es_is_flt_frame); expr->expr_simple_in_use = false; expr->expr_simple_lxid = curlxid; MemoryContextSwitchTo(oldcontext); @@ -10354,7 +10357,7 @@ static bool exec_eval_simple_expr( } plpgsql_estate->curr_nested_table_type = InvalidOid; - *result = ExecEvalExpr(expr->expr_simple_state, econtext, isNull, NULL); + *result = ExecEvalExpr(expr->expr_simple_state, econtext, isNull); /* for nested table, we need use nested table type as result type */ if (plpgsql_estate && plpgsql_estate->curr_nested_table_type != InvalidOid) { if (expr->expr_simple_state->evalfunc == (ExprStateEvalFunc)ExecEvalArrayRef) { @@ -10582,7 +10585,7 @@ static bool CheckTypeIsCursor(PLpgSQL_row *row, Oid valtype, int fnum) return false; } -/* +/* * get covert map for bulk collect * we need to support type cast, so we just match the position */ @@ -10648,7 +10651,7 @@ static TupleConversionMap *convert_tuples_for_bulk_collect(TupleDesc indesc, Tup map->inisnull = (bool *)palloc(n * sizeof(bool)); map->invalues[0] = (Datum)0; /* set up the NULL entry */ map->inisnull[0] = true; - + return map; } diff --git a/src/gausskernel/cbb/utils/partition/partitionmap.cpp b/src/gausskernel/cbb/utils/partition/partitionmap.cpp index fca8b3c66..309e1d43e 100755 --- a/src/gausskernel/cbb/utils/partition/partitionmap.cpp +++ b/src/gausskernel/cbb/utils/partition/partitionmap.cpp @@ -2804,7 +2804,7 @@ int constCompare_constType(Const* value1, Const* value2) errmsg("failed when making EQUAL expression state for constCompare"))); } - result = DatumGetBool(ExecEvalExpr(exprstate, econtext, &isNull, NULL)); + result = DatumGetBool(ExecEvalExpr(exprstate, econtext, &isNull)); FreeExecutorState(estate); @@ -2821,7 +2821,7 @@ int constCompare_constType(Const* value1, Const* value2) errmsg("failed when making GREATE-THAN expression state for constCompare"))); } - result = DatumGetBool(ExecEvalExpr(exprstate, econtext, &isNull, NULL)); + result = DatumGetBool(ExecEvalExpr(exprstate, econtext, &isNull)); if (result) { ret = 1; diff --git a/src/gausskernel/optimizer/commands/analyze.cpp b/src/gausskernel/optimizer/commands/analyze.cpp index 93d16f755..81ce2d222 100755 --- a/src/gausskernel/optimizer/commands/analyze.cpp +++ b/src/gausskernel/optimizer/commands/analyze.cpp @@ -1925,7 +1925,11 @@ static void compute_index_stats(Relation onerel, double totalrows, AnlIndexData* econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + if (estate->es_is_flt_frame){ + predicate = (List*)ExecPrepareQualByFlatten(indexInfo->ii_Predicate, estate); + } else { + predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + } /* Compute and save index expression values */ exprvals = (Datum*)palloc(numrows * attr_cnt * sizeof(Datum)); @@ -1946,7 +1950,7 @@ static void compute_index_stats(Relation onerel, double totalrows, AnlIndexData* /* If index is partial, check predicate */ if (predicate != NIL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate, econtext)) continue; } numindexrows++; diff --git a/src/gausskernel/optimizer/commands/copy.cpp b/src/gausskernel/optimizer/commands/copy.cpp index 2ad65be5b..d9094ebc4 100644 --- a/src/gausskernel/optimizer/commands/copy.cpp +++ b/src/gausskernel/optimizer/commands/copy.cpp @@ -1511,11 +1511,11 @@ void ProcessFileOptions(CopyState cstate, bool is_from, List* options, bool is_d cstate->mode = MODE_NORMAL; if (cstate->eol_type != EOL_UD && !is_from) cstate->eol_type = EOL_NL; - + if ((cstate->delim_len = strlen(cstate->delim)) > DELIM_MAX_LEN) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("FIELDS TERMINATED must be less than %d bytes", DELIM_MAX_LEN))); - + if (cstate->o_enclosed && cstate->enclosed) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("enclosed sentence can only be specified once"))); @@ -6547,7 +6547,7 @@ bool NextCopyFrom(CopyState cstate, ExprContext* econtext, Datum* values, bool* Assert(econtext != NULL); Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory); - values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext, &nulls[defmap[i]], NULL); + values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext, &nulls[defmap[i]]); } if (cstate->transexprs != NULL) @@ -6587,8 +6587,7 @@ static void ExecTransColExpr(CopyState cstate, ExprContext* econtext, int numPhy if (!cstate->transexprs[i]) continue; - values[i] = ExecEvalExpr(cstate->transexprs[i], econtext, - &nulls[i], NULL); + values[i] = ExecEvalExpr(cstate->transexprs[i], econtext, &nulls[i]); } ExecDropSingleTupleTableSlot(slot); diff --git a/src/gausskernel/optimizer/commands/prepare.cpp b/src/gausskernel/optimizer/commands/prepare.cpp index e7c273213..48538156a 100755 --- a/src/gausskernel/optimizer/commands/prepare.cpp +++ b/src/gausskernel/optimizer/commands/prepare.cpp @@ -555,7 +555,7 @@ static ParamListInfo EvaluateParams(CachedPlanSource* psrc, List* params, const } /* Prepare the expressions for execution */ - exprstates = (List*)ExecPrepareExpr((Expr*)params, estate); + exprstates = ExecPrepareExprList(params, estate); paramLI = (ParamListInfo)palloc(offsetof(ParamListInfoData, params) + num_params * sizeof(ParamExternData)); /* we have static list of params, so no hooks needed */ @@ -575,7 +575,7 @@ static ParamListInfo EvaluateParams(CachedPlanSource* psrc, List* params, const prm->ptype = param_types[i]; prm->pflags = PARAM_FLAG_CONST; - prm->value = ExecEvalExprSwitchContext(n, GetPerTupleExprContext(estate), &prm->isnull, NULL); + prm->value = ExecEvalExprSwitchContext(n, GetPerTupleExprContext(estate), &prm->isnull); prm->tabInfo = NULL; i++; diff --git a/src/gausskernel/optimizer/commands/tablecmds.cpp b/src/gausskernel/optimizer/commands/tablecmds.cpp index 2a9e92d8f..24c6eaed4 100755 --- a/src/gausskernel/optimizer/commands/tablecmds.cpp +++ b/src/gausskernel/optimizer/commands/tablecmds.cpp @@ -2550,7 +2550,7 @@ ObjectAddress DefineRelation(CreateStmt* stmt, char relkind, Oid ownerId, Object } } else if (pg_strcasecmp(storeChar, TABLE_ACCESS_METHOD_USTORE) == 0) { if (stmt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP) { - ereport(ERROR, + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("UStore tables do not support global temp table"))); } auto compression = StdRdOptionsGetStringData(std_opt, compression, COMPRESSION_NO); @@ -6229,7 +6229,7 @@ static ObjectAddress RenameTableFeature(RenameStmt* stmt) HeapTuple newtup; Form_pg_class relform; ObjectAddress address; - + Datum values[Natts_pg_class] = { 0 }; bool nulls[Natts_pg_class] = { false }; bool replaces[Natts_pg_class] = { false }; @@ -6575,7 +6575,7 @@ ObjectAddress RenameRelation(RenameStmt* stmt) } if (fdwroutine->ValidateTableDef != nullptr) { fdwroutine->ValidateTableDef((Node*)stmt); - } + } } RelationClose(rel); } @@ -6593,7 +6593,7 @@ ObjectAddress RenameRelation(RenameStmt* stmt) RelationClose(userRelaiton); } ObjectAddressSet(address, RelationRelationId, relid); - + return address; } } @@ -6652,7 +6652,7 @@ void RenameRelationInternal(Oid myrelid, const char* newrelname) relform = (Form_pg_class)GETSTRUCT(reltup); - /* + /* * Check relation name to ensure that it doesn't conflict with existing synonym. */ if (!IsInitdb && GetSynonymOid(newrelname, namespaceId, true) != InvalidOid) { @@ -7371,7 +7371,7 @@ static AT_INSTANT_DEFAULT_VALUE shouldUpdateAllTuples( MemoryContext newcxt = GetPerTupleMemoryContext(estate); MemoryContext oldcxt = MemoryContextSwitchTo(newcxt); - Datum value = ExecEvalExpr(exprstate, econtext, &isNull, NULL); + Datum value = ExecEvalExpr(exprstate, econtext, &isNull); (void)MemoryContextSwitchTo(oldcxt); if (!isNull) { @@ -8425,26 +8425,26 @@ static Node* GetGeneratedAdbin(Relation rel, AttrNumber myattnum) SysScanDesc scan; Oid exprtype; Node *expr = NULL; - + def_rel = heap_open(AttrDefaultRelationId, RowExclusiveLock); ScanKeyInit(&key[0], Anum_pg_attrdef_adrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); ScanKeyInit(&key[1], Anum_pg_attrdef_adnum, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum(myattnum)); - + scan = systable_beginscan(def_rel, AttrDefaultIndexId, true, NULL, 2, key); - + while (HeapTupleIsValid(def_tuple = systable_getnext(scan))) { bool is_null = false; Datum adbin_datum; char *adbin_string = NULL; - + adbin_datum = fastgetattr(def_tuple, Anum_pg_attrdef_adbin, def_rel->rd_att, &is_null); AssertEreport(!is_null, MOD_OPT, ""); adbin_string = TextDatumGetCString(adbin_datum); expr = (Node *)stringToNode_skip_extern_fields(adbin_string); - + exprtype = exprType(expr); - + expr = coerce_to_target_type(NULL, /* no UNKNOWN params here */ expr, exprtype, @@ -8453,18 +8453,18 @@ static Node* GetGeneratedAdbin(Relation rel, AttrNumber myattnum) COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); - + /* * If there is nextval FuncExpr, we should lock the quoted sequence to avoid deadlock, this has beed done in * transformFuncExpr. See sqlcmd_lock_nextval_on_cn for more details. */ (void)lockNextvalWalker(expr, NULL); - + pfree_ext(adbin_string); } systable_endscan(scan); heap_close(def_rel, RowExclusiveLock); - + return expr; } @@ -8475,23 +8475,23 @@ static void UpdateGeneratedExpr(AlteredTableInfo* tab) NewColumnValue* ex = (NewColumnValue*)lfirst(l); Relation rel; AttrNumber attnum; - + if (!ex->is_generated) { continue; } - + rel = relation_open(tab->relid, NoLock); - + attnum = get_attnum(RelationGetRelid(rel), ex->col_name); if (attnum <= InvalidAttrNumber) { /* shouldn't happen */ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", ex->col_name, RelationGetRelationName(rel)))); } - + Expr *defval = (Expr *)GetGeneratedAdbin(rel, attnum); ex->expr = expression_planner(defval); ex->generate_attnum = attnum; - + relation_close(rel, NoLock); } } @@ -9060,7 +9060,7 @@ static void ATExecCmd(List** wqueue, AlteredTableInfo* tab, Relation rel, AlterT * Report the subcommand to interested event triggers. */ EventTriggerCollectAlterTableSubcmd((Node *) cmd, address); - + /* take ExclusiveLock to avoid PARTITION DDL COMMIT until we finish the InitPlan. Oid info will be masked here, and * be locked in CommitTransaction. Distribute mode doesn't support partition DDL/DML parallel work, no need this @@ -9189,7 +9189,7 @@ static void ATRewriteTables(AlterTableStmt *parsetree, List** wqueue, LOCKMODE l * Fire off an Event Trigger now, before actually rewriting the * table. * - * We don't support Event Trigger for nested commands anywhere, + * We don't support Event Trigger for nested commands anywhere, * here included, and parsetree is given NULL when coming from * AlterTableInternal. * @@ -9199,7 +9199,7 @@ static void ATRewriteTables(AlterTableStmt *parsetree, List** wqueue, LOCKMODE l EventTriggerTableRewrite((Node *)parsetree, tab->relid, tab->rewrite); - + /* * We don't support rewriting of system catalogs; there are too * many corner cases and too little benefit. In particular this @@ -9373,7 +9373,7 @@ static T EvaluateGenExpr(AlteredTableInfo* tab, T tuple, return tup; } -/* +/* * update values and isnull after modify column to a new loaction. * newattnum > 0 denotes modify with first or after column or add generated column. */ @@ -9401,18 +9401,18 @@ static void UpdateValueModifyFirstAfter(NewColumnValue *ex, Datum* values, bool* static void UpdateGeneratedColumnIsnull(AlteredTableInfo* tab, bool* isnull, bool has_generated) { ListCell* l = NULL; - + if (!has_generated) { return; } - + foreach (l, tab->newvals) { NewColumnValue *ex = (NewColumnValue*)lfirst(l); - + if (!ex->is_generated) { continue; } - + isnull[ex->generate_attnum - 1] = true; } } @@ -9473,7 +9473,11 @@ static void ATRewriteTableInternal(AlteredTableInfo* tab, Relation oldrel, Relat switch (con->contype) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List*)ExecPrepareExpr((Expr*)con->qual, estate); + if (estate->es_is_flt_frame){ + con->qualstate = (List*)ExecPrepareExprList((List*)con->qual, estate); + } else { + con->qualstate = (List*)ExecPrepareExpr((Expr*)con->qual, estate); + } break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -9654,7 +9658,7 @@ static void ATRewriteTableInternal(AlteredTableInfo* tab, Relation oldrel, Relat } values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate, econtext, &isnull[ex->attnum - 1], NULL); - + if (ex->is_autoinc) { need_autoinc = (autoinc_attnum > 0); } @@ -9713,19 +9717,35 @@ static void ATRewriteTableInternal(AlteredTableInfo* tab, Relation oldrel, Relat foreach(l, tab->constraints) { NewConstraint *con = (NewConstraint*)lfirst(l); + ListCell* lc = NULL; switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + { + if (estate->es_is_flt_frame){ + foreach (lc, con->qualstate) { + ExprState* exprState = (ExprState*)lfirst(lc); + + if (!ExecCheckByFlatten(exprState, econtext)) ereport(ERROR, - (errcode(ERRCODE_CHECK_VIOLATION), + (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", con->name))); + } + } else { + if (!ExecQual(con->qualstate, econtext, true)){ + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("check constraint \"%s\" is violated by some row", + con->name))); + } + } + } break; case CONSTR_FOREIGN: - /* Nothing to do here */ - break; + /* Nothing to do here */ + break; default: { ereport(ERROR, @@ -9801,12 +9821,12 @@ static void ATRewriteTableInternal(AlteredTableInfo* tab, Relation oldrel, Relat } continue; } - - values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate, econtext, &isnull[ex->attnum - 1], NULL); + + values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate, econtext, &isnull[ex->attnum - 1]); if (ex->is_autoinc) { need_autoinc = (autoinc_attnum > 0); } - + if (tab->is_first_after) { UpdateValueModifyFirstAfter(ex, values, isnull); } @@ -9814,13 +9834,13 @@ static void ATRewriteTableInternal(AlteredTableInfo* tab, Relation oldrel, Relat /* generated column */ UpdateGeneratedColumnIsnull(tab, isnull, has_generated); - + /* auto_increment */ if (need_autoinc) { autoinc = EvaluateAutoIncrement(oldrel, newTupDesc, autoinc_attnum, &values[autoinc_attnum - 1], &isnull[autoinc_attnum - 1]); } - + /* Set dropped attributes to null in new tuple */ foreach (lc, dropped_attrs) { isnull[lfirst_int(lc)] = true; @@ -9861,14 +9881,31 @@ static void ATRewriteTableInternal(AlteredTableInfo* tab, Relation oldrel, Relat foreach (l, tab->constraints) { NewConstraint* con = (NewConstraint*)lfirst(l); + ListCell* lc = NULL; switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) - ereport(ERROR, - (errcode(ERRCODE_CHECK_VIOLATION), - errmsg("check constraint \"%s\" is violated by some row", con->name))); - break; + { + if (estate->es_is_flt_frame){ + foreach (lc, con->qualstate) { + ExprState* exprState = (ExprState*)lfirst(lc); + + if (!ExecCheckByFlatten(exprState, econtext)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("check constraint \"%s\" is violated by some row", + con->name))); + } + } else { + if (!ExecQualByRecursion(con->qualstate, econtext, true)){ + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("check constraint \"%s\" is violated by some row", + con->name))); + } + } + } + break; case CONSTR_FOREIGN: /* Nothing to do here */ break; @@ -10530,27 +10567,27 @@ static int GetAfterColumnAttnum(Oid attrelid, const char *after_name) { int afterattnum = -1; HeapTuple tuple; - + tuple = SearchSysCacheAttName(attrelid, after_name); if (!HeapTupleIsValid(tuple)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("The %s column of relation %u is not exists.", after_name, attrelid))); } - + afterattnum = ((Form_pg_attribute)GETSTRUCT(tuple))->attnum + 1; ReleaseSysCache(tuple); return afterattnum; } - + static Node *UpdateVarattnoAfterAddColumn(Node *node, int startattnum, int endattnum, bool is_increase) { if (node == NULL) { return node; } - + int curattnum = is_increase ? endattnum + 1 : startattnum - 1; int newattnum = is_increase ? startattnum : endattnum; - + switch (nodeTag(node)) { case T_Var: { Var *var = (Var *)node; @@ -10785,7 +10822,7 @@ static Node *UpdateVarattnoAfterAddColumn(Node *node, int startattnum, int endat case T_CaseExpr: { CaseExpr *expr = (CaseExpr *)node; CaseExpr *newExpr = (CaseExpr *)copyObject(expr); - + List *expr_args = (List *)UpdateVarattnoAfterAddColumn((Node *)expr->args, startattnum, endattnum, is_increase); // case_default @@ -10813,7 +10850,7 @@ static Node *UpdateVarattnoAfterAddColumn(Node *node, int startattnum, int endat case T_List: { List *reslist = NIL; ListCell *temp = NULL; - + foreach(temp, (List *)node) { reslist = lappend(reslist, UpdateVarattnoAfterAddColumn((Node *)lfirst(temp), @@ -10837,8 +10874,8 @@ static Node *UpdateVarattnoAfterAddColumn(Node *node, int startattnum, int endat } return NULL; } - -/* + +/* * update pg_attribute. * 1. add column with first or after col_name. * 2. modify column to first or after column. @@ -10850,16 +10887,16 @@ static void UpdatePgAttributeFirstAfter(Relation attr_rel, Oid attrelid, int sta HeapTuple attr_tuple; SysScanDesc scan; Form_pg_attribute attr_form; - + for (int i = (is_increase ? endattnum : startattnum); (is_increase ? i >= startattnum : i <= endattnum); (is_increase ? i-- : i++)) { AttrNumber myattnum = (AttrNumber)i; ScanKeyInit(&key[0], Anum_pg_attribute_attrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(attrelid)); ScanKeyInit(&key[1], Anum_pg_attribute_attnum, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum(myattnum)); - + scan = systable_beginscan(attr_rel, AttributeRelidNumIndexId, true, NULL, 2, key); - + /* only one */ while (HeapTupleIsValid(attr_tuple = systable_getnext(scan))) { Datum values[Natts_pg_attribute] = { 0 }; @@ -10868,9 +10905,9 @@ static void UpdatePgAttributeFirstAfter(Relation attr_rel, Oid attrelid, int sta errno_t rc = 0; HeapTuple new_attr_tuple; char newattname[NAMEDATALEN]; - + attr_form = (Form_pg_attribute)GETSTRUCT(attr_tuple); - + // update pg_attribute_attnum if (is_increase) { values[Anum_pg_attribute_attnum - 1] = Int16GetDatum(attr_form->attnum + 1); @@ -10879,7 +10916,7 @@ static void UpdatePgAttributeFirstAfter(Relation attr_rel, Oid attrelid, int sta values[Anum_pg_attribute_attnum - 1] = Int16GetDatum(attr_form->attnum - 1); replaces[Anum_pg_attribute_attnum - 1] = true; } - + // if exists dropped column, update pg_attribute_attname of dropped column if (attr_form->attisdropped) { if (is_increase) { @@ -10891,22 +10928,22 @@ static void UpdatePgAttributeFirstAfter(Relation attr_rel, Oid attrelid, int sta sizeof(newattname) - 1, "........pg.dropped.%d........", attr_form->attnum - 1); securec_check_ss(rc, "\0", "\0"); } - + values[Anum_pg_attribute_attname - 1] = NameGetDatum(newattname); replaces[Anum_pg_attribute_attname - 1] = true; } - + new_attr_tuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attr_rel), values, nulls, replaces); simple_heap_update(attr_rel, &new_attr_tuple->t_self, new_attr_tuple); CatalogUpdateIndexes(attr_rel, new_attr_tuple); - + heap_freetuple_ext(new_attr_tuple); } systable_endscan(scan); } } - -/* + +/* * update pg_index. * 1. add column with first or after col_name. * 2. modify column to first or after column. @@ -10920,13 +10957,13 @@ static void UpdatePgIndexFirstAfter(Relation rel, int startattnum, int endattnum Form_pg_index index_form; int curattnum = is_increase ? endattnum + 1 : startattnum - 1; int newattnum = is_increase ? startattnum : endattnum; - + /* Prepare to scan pg_index for entries having indrelid = this rel. */ ScanKeyInit(&key, Anum_pg_index_indrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); index_rel = heap_open(IndexRelationId, RowExclusiveLock); scan = systable_beginscan(index_rel, IndexIndrelidIndexId, true, NULL, 1, &key); - + while (HeapTupleIsValid(index_tuple = systable_getnext(scan))) { int numatts; bool is_null = false; @@ -10936,10 +10973,10 @@ static void UpdatePgIndexFirstAfter(Relation rel, int startattnum, int endattnum int2vector *indkey = NULL; int2vector *new_indkey = NULL; HeapTuple new_index_tuple; - + index_form = (Form_pg_index)GETSTRUCT(index_tuple); numatts = index_form->indnatts; - + // update pg_index_indkey Datum indkey_datum = SysCacheGetAttr(INDEXRELID, index_tuple, Anum_pg_index_indkey, &is_null); AssertEreport(!is_null, MOD_OPT, ""); @@ -10957,19 +10994,19 @@ static void UpdatePgIndexFirstAfter(Relation rel, int startattnum, int endattnum } values[Anum_pg_index_indkey - 1] = PointerGetDatum(new_indkey); replaces[Anum_pg_index_indkey - 1] = true; - + // udpate pg_index_indexprs if (!heap_attisnull(index_tuple, Anum_pg_index_indexprs, NULL)) { Datum exprs_datum; List *indexprs = NIL; List *new_indexprs = NIL; char* exprs_string = NULL; - + exprs_datum = SysCacheGetAttr(INDEXRELID, index_tuple, Anum_pg_index_indexprs, &is_null); AssertEreport(!is_null, MOD_OPT, ""); exprs_string = TextDatumGetCString(exprs_datum); indexprs = (List *)stringToNode(exprs_string); - + new_indexprs = (List *)UpdateVarattnoAfterAddColumn((Node *)indexprs, startattnum, endattnum, is_increase); exprs_string = nodeToString(new_indexprs); @@ -10977,19 +11014,19 @@ static void UpdatePgIndexFirstAfter(Relation rel, int startattnum, int endattnum replaces[Anum_pg_index_indexprs - 1] = true; pfree_ext(exprs_string); } - + // update pg_index_indpred if (!heap_attisnull(index_tuple, Anum_pg_index_indpred, NULL)) { Datum pred_datum; List *indpred = NIL; List *new_indpred = NIL; char *pred_string = NULL; - + pred_datum = SysCacheGetAttr(INDEXRELID, index_tuple, Anum_pg_index_indpred, &is_null); AssertEreport(!is_null, MOD_OPT, ""); pred_string = TextDatumGetCString(pred_datum); indpred = (List *)stringToNode(pred_string); - + new_indpred = (List *)UpdateVarattnoAfterAddColumn((Node *)indpred, startattnum, endattnum, is_increase); pred_string = nodeToString(new_indpred); @@ -10997,20 +11034,20 @@ static void UpdatePgIndexFirstAfter(Relation rel, int startattnum, int endattnum replaces[Anum_pg_index_indpred - 1] = true; pfree_ext(pred_string); } - + new_index_tuple = heap_modify_tuple(index_tuple, RelationGetDescr(index_rel), values, nulls, replaces); simple_heap_update(index_rel, &new_index_tuple->t_self, new_index_tuple); CatalogUpdateIndexes(index_rel, new_index_tuple); - + pfree_ext(new_indkey); heap_freetuple_ext(new_index_tuple); } - + systable_endscan(scan); heap_close(index_rel, RowExclusiveLock); } - -/* + +/* * update pg_constraint. * 1. add column with first or after col_name. * 2. modify column to first or after column. @@ -11023,12 +11060,12 @@ static void UpdatePgConstraintFirstAfter(Relation rel, int startattnum, int enda SysScanDesc scan; int curattnum = is_increase ? endattnum + 1 : startattnum - 1; int newattnum = is_increase ? startattnum : endattnum; - + ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); con_rel = heap_open(ConstraintRelationId, RowExclusiveLock); scan = systable_beginscan(con_rel, ConstraintRelidIndexId, true, NULL, 1, &key); - + while (HeapTupleIsValid(con_tuple = systable_getnext(scan))) { bool is_null = false; ArrayType *conkey_array = NULL; @@ -11037,7 +11074,7 @@ static void UpdatePgConstraintFirstAfter(Relation rel, int startattnum, int enda bool nulls[Natts_pg_constraint] = { 0 }; bool replaces[Natts_pg_constraint] = { 0 }; HeapTuple new_con_tuple; - + // update pg_constraint_conkey Datum conkeyDatum = SysCacheGetAttr(CONSTROID, con_tuple, Anum_pg_constraint_conkey, &is_null); if (!is_null) { @@ -11045,7 +11082,7 @@ static void UpdatePgConstraintFirstAfter(Relation rel, int startattnum, int enda int con_key_num = ARR_DIMS(con_key_arr)[0]; int16 *con_key_attnums = (int16 *)ARR_DATA_PTR(con_key_arr); Datum *conkey = (Datum *)palloc(con_key_num * sizeof(Datum)); - + for (int i = 0; i < con_key_num; i++) { if (con_key_attnums[i] >= startattnum && con_key_attnums[i] <= endattnum) { con_key_attnums[i] = is_increase ? (con_key_attnums[i] + 1) : (con_key_attnums[i] - 1); @@ -11058,7 +11095,7 @@ static void UpdatePgConstraintFirstAfter(Relation rel, int startattnum, int enda values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkey_array); replaces[Anum_pg_constraint_conkey - 1] = true; } - + // update pg_constraint_conincluding Datum con_including_datum = SysCacheGetAttr(CONSTROID, con_tuple, Anum_pg_constraint_conincluding, &is_null); if (!is_null) { @@ -11066,7 +11103,7 @@ static void UpdatePgConstraintFirstAfter(Relation rel, int startattnum, int enda int con_including_num = ARR_DIMS(con_including_arr)[0]; int16* con_including_attnums = (int16 *)ARR_DATA_PTR(con_including_arr); Datum* conincluding = (Datum *)palloc(con_including_num * sizeof(Datum)); - + for (int i = 0; i < con_including_num; i++) { if (con_including_attnums[i] >= startattnum && con_including_attnums[i] <= endattnum) { con_including_attnums[i] = is_increase ? @@ -11080,38 +11117,38 @@ static void UpdatePgConstraintFirstAfter(Relation rel, int startattnum, int enda values[Anum_pg_constraint_conincluding - 1] = PointerGetDatum(conincluding_array); replaces[Anum_pg_constraint_conincluding - 1] = true; } - + // update pg_constraint_conbin Datum conbin_datum = SysCacheGetAttr(CONSTROID, con_tuple, Anum_pg_constraint_conbin, &is_null); if (!is_null) { char *conbin_string = NULL; Node *conbin = NULL; Node *new_conbin = NULL; - + conbin_string = TextDatumGetCString(conbin_datum); conbin = (Node*)stringToNode(conbin_string); - + new_conbin = UpdateVarattnoAfterAddColumn(conbin, startattnum, endattnum, is_increase); conbin_string = nodeToString(new_conbin); values[Anum_pg_constraint_conbin - 1] = CStringGetTextDatum(conbin_string); replaces[Anum_pg_constraint_conbin - 1] = true; pfree_ext(conbin_string); } - + new_con_tuple = heap_modify_tuple(con_tuple, RelationGetDescr(con_rel), values, nulls, replaces); simple_heap_update(con_rel, &new_con_tuple->t_self, new_con_tuple); CatalogUpdateIndexes(con_rel, new_con_tuple); - + pfree_ext(conkey_array); pfree_ext(conincluding_array); heap_freetuple_ext(new_con_tuple); } - + systable_endscan(scan); heap_close(con_rel, RowExclusiveLock); } - -/* + +/* * update pg_constraint confkey. * 1. add column with first or after col_name. * 2. modify column to first or after column. @@ -11125,12 +11162,12 @@ static void UpdatePgConstraintConfkeyFirstAfter(Relation rel, int startattnum, i SysScanDesc scan; int curattnum = is_increase ? endattnum + 1 : startattnum - 1; int newattnum = is_increase ? startattnum : endattnum; - + ScanKeyInit(&key, Anum_pg_constraint_confrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); con_rel = heap_open(ConstraintRelationId, RowExclusiveLock); scan = systable_beginscan(con_rel, InvalidOid, false, NULL, 1, &key); - + while (HeapTupleIsValid(con_tuple = systable_getnext(scan))) { bool is_null = false; ArrayType* confkey_array = NULL; @@ -11138,7 +11175,7 @@ static void UpdatePgConstraintConfkeyFirstAfter(Relation rel, int startattnum, i bool nulls[Natts_pg_constraint] = { 0 }; bool replaces[Natts_pg_constraint] = { 0 }; HeapTuple new_con_tuple; - + // update pg_constraint_confkey Datum confkey_datum = SysCacheGetAttr(CONSTROID, con_tuple, Anum_pg_constraint_confkey, &is_null); if (!is_null) { @@ -11146,7 +11183,7 @@ static void UpdatePgConstraintConfkeyFirstAfter(Relation rel, int startattnum, i int confkey_num = ARR_DIMS(conf_key_rr)[0]; int16 *confkey_attnums = (int16 *)ARR_DATA_PTR(conf_key_rr); Datum *confkey = (Datum *)palloc(confkey_num * sizeof(Datum)); - + for (int i = 0; i < confkey_num; i++) { if (confkey_attnums[i] >= startattnum && confkey_attnums[i] <= endattnum) { confkey_attnums[i] = is_increase ? (confkey_attnums[i] + 1) : (confkey_attnums[i] - 1); @@ -11159,20 +11196,20 @@ static void UpdatePgConstraintConfkeyFirstAfter(Relation rel, int startattnum, i values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkey_array); replaces[Anum_pg_constraint_confkey - 1] = true; } - + new_con_tuple = heap_modify_tuple(con_tuple, RelationGetDescr(con_rel), values, nulls, replaces); simple_heap_update(con_rel, &new_con_tuple->t_self, new_con_tuple); CatalogUpdateIndexes(con_rel, new_con_tuple); - + pfree_ext(confkey_array); heap_freetuple_ext(new_con_tuple); } - + systable_endscan(scan); heap_close(con_rel, RowExclusiveLock); } - -/* + +/* * update generated column information for pg_attrdef. * 1. add column with first or after col_name. * 2. modify column to first or after column. @@ -11183,7 +11220,7 @@ static void UpdateGenerateColFirstAfter(Relation rel, int startattnum, int endat HeapTuple def_tuple; Relation def_rel; SysScanDesc scan; - + def_rel = heap_open(AttrDefaultRelationId, RowExclusiveLock); ScanKeyInit(&key, Anum_pg_attrdef_adrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); @@ -11195,24 +11232,24 @@ static void UpdateGenerateColFirstAfter(Relation rel, int startattnum, int endat bool nulls[Natts_pg_attrdef] = { 0 }; bool replaces[Natts_pg_attrdef] = { 0 }; HeapTuple new_def_tuple; - + Datum adgencol = fastgetattr(def_tuple, Anum_pg_attrdef_adgencol, def_rel->rd_att, &is_null); if (!is_null) { generated_col = DatumGetChar(adgencol); } - + // update pg_attrdef_adbin if (generated_col == ATTRIBUTE_GENERATED_STORED) { Datum adbin_datum; Node *adbin = NULL; Node *new_adbin = NULL; char *adbin_string = NULL; - + adbin_datum = fastgetattr(def_tuple, Anum_pg_attrdef_adbin, def_rel->rd_att, &is_null); AssertEreport(!is_null, MOD_OPT, ""); adbin_string = TextDatumGetCString(adbin_datum); adbin = (Node *)stringToNode(adbin_string); - + new_adbin = UpdateVarattnoAfterAddColumn(adbin, startattnum, endattnum, is_increase); adbin_string = nodeToString(new_adbin); values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin_string); @@ -11221,19 +11258,19 @@ static void UpdateGenerateColFirstAfter(Relation rel, int startattnum, int endat } else { continue; } - + new_def_tuple = heap_modify_tuple(def_tuple, RelationGetDescr(def_rel), values, nulls, replaces); simple_heap_update(def_rel, &new_def_tuple->t_self, new_def_tuple); CatalogUpdateIndexes(def_rel, new_def_tuple); - + heap_freetuple_ext(new_def_tuple); } systable_endscan(scan); heap_close(def_rel, RowExclusiveLock); } - - -/* + + +/* * update the exists index information. * 1. add column with first or after col_name. */ @@ -11244,27 +11281,27 @@ static void UpdateIndexFirstAfter(Relation rel) ScanKeyData key; SysScanDesc scan; Form_pg_index index_form; - + /* Prepare to scan pg_index for entries having indrelid = this rel. */ ScanKeyInit(&key, Anum_pg_index_indrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); pg_index_rel = heap_open(IndexRelationId, RowExclusiveLock); scan = systable_beginscan(pg_index_rel, IndexIndrelidIndexId, true, NULL, 1, &key); - + while (HeapTupleIsValid(index_tuple = systable_getnext(scan))) { index_form = (Form_pg_index)GETSTRUCT(index_tuple); - + table_index_rel = index_open(index_form->indexrelid, RowExclusiveLock); - + table_index_rel->rd_index = index_form; - + index_close(table_index_rel, RowExclusiveLock); } systable_endscan(scan); heap_close(pg_index_rel, RowExclusiveLock); } - -/* + +/* * update pg_attrdef. * 1. add column with first or after col_name. * 2. modify column to first or after column. @@ -11276,43 +11313,43 @@ static void UpdatePgAttrdefFirstAfter(Relation rel, int startattnum, int endattn Relation def_rel; SysScanDesc scan; Form_pg_attrdef def_form; - + def_rel = heap_open(AttrDefaultRelationId, RowExclusiveLock); - + for (int i = (is_increase ? endattnum : startattnum); (is_increase ? i >= startattnum : i <= endattnum); (is_increase ? i-- : i++)) { AttrNumber myattnum = (AttrNumber)i; ScanKeyInit(&key[0], Anum_pg_attrdef_adrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); ScanKeyInit(&key[1], Anum_pg_attrdef_adnum, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum(myattnum)); - + scan = systable_beginscan(def_rel, AttrDefaultIndexId, true, NULL, 2, key); - + // only one while (HeapTupleIsValid(def_tuple = systable_getnext(scan))) { Datum values[Natts_pg_attrdef] = { 0 }; bool nulls[Natts_pg_attrdef] = { 0 }; bool replaces[Natts_pg_attrdef] = { 0 }; HeapTuple new_def_tuple; - + def_form = (Form_pg_attrdef)GETSTRUCT(def_tuple); - + values[Anum_pg_attrdef_adnum - 1] = is_increase ? Int16GetDatum(def_form->adnum + 1) : Int16GetDatum(def_form->adnum - 1); replaces[Anum_pg_attrdef_adnum - 1] = true; - + new_def_tuple = heap_modify_tuple(def_tuple, RelationGetDescr(def_rel), values, nulls, replaces); simple_heap_update(def_rel, &new_def_tuple->t_self, new_def_tuple); CatalogUpdateIndexes(def_rel, new_def_tuple); - + heap_freetuple_ext(new_def_tuple); } systable_endscan(scan); } heap_close(def_rel, RowExclusiveLock); } - -/* + +/* * update pg_depend. * 1. add column with first or after col_name. * 2. modify column to first or after column. @@ -11324,26 +11361,26 @@ static void UpdatePgDependFirstAfter(Relation rel, int startattnum, int endattnu Relation dep_rel; SysScanDesc scan; Form_pg_depend dep_form; - + int curattnum = is_increase ? endattnum + 1 : startattnum - 1; int newattnum = is_increase ? startattnum : endattnum; - + dep_rel = heap_open(DependRelationId, RowExclusiveLock); - + // find pg_depend based on refobjid and refobjsubid, then update refobjsubid ScanKeyInit(&key[0], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); ScanKeyInit(&key[1], Anum_pg_depend_refobjsubid, BTGreaterStrategyNumber, F_INT4GT, Int32GetDatum(0)); - + scan = systable_beginscan(dep_rel, DependReferenceIndexId, true, NULL, 2, key); while (HeapTupleIsValid(dep_tuple = systable_getnext(scan))) { Datum values[Natts_pg_depend] = { 0 }; bool nulls[Natts_pg_depend] = { 0 }; bool replaces[Natts_pg_depend] = { 0 }; HeapTuple new_dep_tuple; - + dep_form = (Form_pg_depend)GETSTRUCT(dep_tuple); - + if (dep_form->refobjsubid >= startattnum && dep_form->refobjsubid <= endattnum) { values[Anum_pg_depend_refobjsubid - 1] = is_increase ? Int32GetDatum(dep_form->refobjsubid + 1) : Int32GetDatum(dep_form->refobjsubid - 1); @@ -11352,11 +11389,11 @@ static void UpdatePgDependFirstAfter(Relation rel, int startattnum, int endattnu values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(newattnum); replaces[Anum_pg_depend_refobjsubid - 1] = true; } - + new_dep_tuple = heap_modify_tuple(dep_tuple, RelationGetDescr(dep_rel), values, nulls, replaces); simple_heap_update(dep_rel, &new_dep_tuple->t_self, new_dep_tuple); CatalogUpdateIndexes(dep_rel, new_dep_tuple); - + heap_freetuple_ext(new_dep_tuple); } systable_endscan(scan); @@ -11365,12 +11402,12 @@ static void UpdatePgDependFirstAfter(Relation rel, int startattnum, int endattnu CommandCounterIncrement(); dep_rel = heap_open(DependRelationId, RowExclusiveLock); - + // find pg_depend based on objid and objsubid, then update objsubid ScanKeyInit(&key[0], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); ScanKeyInit(&key[1], Anum_pg_depend_objsubid, BTGreaterStrategyNumber, F_INT4GT, Int32GetDatum(0)); - + scan = systable_beginscan(dep_rel, DependDependerIndexId, true, NULL, 2, key); while (HeapTupleIsValid(dep_tuple = systable_getnext(scan))) { Datum values[Natts_pg_depend] = { 0 }; @@ -11403,8 +11440,8 @@ static void UpdatePgDependFirstAfter(Relation rel, int startattnum, int endattnu systable_endscan(scan); heap_close(dep_rel, RowExclusiveLock); } - -/* + +/* * update pg_partition. * 1. add column with first or after col_name. * 2. modify column to first or after column. @@ -11418,16 +11455,16 @@ static void UpdatePgPartitionFirstAfter(Relation rel, int startattnum, int endat SysScanDesc scan; int curattnum = is_increase ? endattnum + 1 : startattnum - 1; int newattnum = is_increase ? startattnum : endattnum; - + par_rel = heap_open(PartitionRelationId, RowExclusiveLock); - + ScanKeyInit(&key, Anum_pg_partition_parentid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); - + scan = systable_beginscan(par_rel, PartitionParentOidIndexId, true, NULL, 1, &key); while (HeapTupleIsValid(par_tuple = systable_getnext(scan))) { bool is_null = false; - + // update pg_partition_partkey Datum partkey_datum = SysCacheGetAttr(PARTRELID, par_tuple, Anum_pg_partition_partkey, &is_null); if (!is_null) { @@ -11437,7 +11474,7 @@ static void UpdatePgPartitionFirstAfter(Relation rel, int startattnum, int endat int2vector *partkey = NULL; int2vector *new_partKey = NULL; HeapTuple new_par_tuple; - + partkey = (int2vector *)DatumGetPointer(partkey_datum); new_partKey = buildint2vector(NULL, partkey->dim1); for (int i = 0; i < partkey->dim1; i++) { @@ -11458,11 +11495,11 @@ static void UpdatePgPartitionFirstAfter(Relation rel, int startattnum, int endat } values[Anum_pg_partition_partkey - 1] = PointerGetDatum(new_partKey); replaces[Anum_pg_partition_partkey - 1] = true; - + new_par_tuple = heap_modify_tuple(par_tuple, RelationGetDescr(par_rel), values, nulls, replaces); simple_heap_update(par_rel, &new_par_tuple->t_self, new_par_tuple); CatalogUpdateIndexes(par_rel, new_par_tuple); - + pfree_ext(new_partKey); heap_freetuple_ext(new_par_tuple); } @@ -11470,38 +11507,38 @@ static void UpdatePgPartitionFirstAfter(Relation rel, int startattnum, int endat systable_endscan(scan); heap_close(par_rel, RowExclusiveLock); } - - + + static ViewInfoForAdd *GetViewInfoFirstAfter(Relation rel, Oid objid, bool keep_star) { ScanKeyData entry; ViewInfoForAdd *info = NULL; - + ScanKeyInit(&entry, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(objid)); - + Relation rewrite_rel = heap_open(RewriteRelationId, AccessShareLock); - + SysScanDesc rewrite_scan = systable_beginscan(rewrite_rel, RewriteOidIndexId, true, NULL, 1, &entry); - + HeapTuple rewrite_tup = systable_getnext(rewrite_scan); - + if (HeapTupleIsValid(rewrite_tup)) { Form_pg_rewrite rewrite_form = (Form_pg_rewrite)GETSTRUCT(rewrite_tup); - + if (strcmp(NameStr(rewrite_form->rulename), ViewSelectRuleName) == 0) { bool is_null = false; - + Datum ev_actiom_datum = fastgetattr(rewrite_tup, Anum_pg_rewrite_ev_action, rewrite_rel->rd_att, &is_null); if (!is_null) { StringInfoData buf; - + initStringInfo(&buf); - + Relation ev_relation = heap_open(rewrite_form->ev_class, AccessShareLock); char *ev_action_string = TextDatumGetCString(ev_actiom_datum); List *ev_action = (List *)stringToNode(ev_action_string); Query* query = (Query*)linitial(ev_action); - + get_query_def(query, &buf, NIL, @@ -11517,13 +11554,13 @@ static ViewInfoForAdd *GetViewInfoFirstAfter(Relation rel, Oid objid, bool keep_ false, keep_star); appendStringInfo(&buf, ";"); - + info = (ViewInfoForAdd *)palloc0(sizeof(ViewInfoForAdd)); info->ev_class = rewrite_form->ev_class; info->query_string = pstrdup(buf.data); - + heap_close(ev_relation, AccessShareLock); - + FreeStringInfo(&buf); pfree_ext(ev_action_string); } @@ -11537,11 +11574,11 @@ static ViewInfoForAdd *GetViewInfoFirstAfter(Relation rel, Oid objid, bool keep_ } systable_endscan(rewrite_scan); heap_close(rewrite_rel, AccessShareLock); - + return info; } - -/* + +/* * get sql for view. * 1. add column with first or after col_name. * 2. modify column to first or after column. @@ -11554,31 +11591,31 @@ static List *CheckPgRewriteFirstAfter(Relation rel) Form_pg_depend dep_form; Oid pre_objid = 0; List *query_str = NIL; - + Relation dep_rel = heap_open(DependRelationId, AccessShareLock); - + ScanKeyInit( &key[0], Anum_pg_depend_refclassid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationRelationId)); ScanKeyInit( &key[1], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); - + dep_scan = systable_beginscan(dep_rel, DependReferenceIndexId, true, NULL, 2, key); - + while (HeapTupleIsValid(dep_tuple = systable_getnext(dep_scan))) { dep_form = (Form_pg_depend)GETSTRUCT(dep_tuple); - + if (dep_form->classid == RewriteRelationId) { ListCell* viewinfo = NULL; bool is_exist = false; - + if (dep_form->objid == pre_objid) { continue; } - + pre_objid = dep_form->objid; - + ViewInfoForAdd *info = GetViewInfoFirstAfter(rel, dep_form->objid); - + foreach (viewinfo, query_str) { ViewInfoForAdd *oldInfo = (ViewInfoForAdd *)lfirst(viewinfo); if (info != NULL && oldInfo->ev_class == info->ev_class) { @@ -11586,7 +11623,7 @@ static List *CheckPgRewriteFirstAfter(Relation rel) break; } } - + if (info != NULL && !is_exist) { query_str = lappend(query_str, info); } @@ -11596,7 +11633,7 @@ static List *CheckPgRewriteFirstAfter(Relation rel) heap_close(dep_rel, AccessShareLock); return query_str; } - + /* * create or replace view when the table has view. * 1. add column with first or after col_name. @@ -11606,27 +11643,27 @@ static void ReplaceViewQueryFirstAfter(List *query_str) { if (query_str != NIL) { ListCell* viewinfo = NULL; - + foreach (viewinfo, query_str) { Query* query = NULL; List* parsetree_list = NULL; Node* parsetree = NULL; - + ViewInfoForAdd *info = (ViewInfoForAdd *)lfirst(viewinfo); parsetree_list = pg_parse_query(info->query_string); if (list_length(parsetree_list) != 1) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("this is not a view"))); } - + parsetree = (Node *)linitial(parsetree_list); query = parse_analyze(parsetree, info->query_string, NULL, 0); StoreViewQuery(info->ev_class, query, true); } } } - -/* + +/* * update pg_trigger * 1. add column with first or after col_name. * 2. modify column to first or after column. @@ -11642,12 +11679,12 @@ static void UpdatePgTriggerFirstAfter(Relation rel, int startattnum, int endattn SysScanDesc scan; int curattnum = is_increase ? endattnum + 1 : startattnum - 1; int newattnum = is_increase ? startattnum : endattnum; - + tri_rel = heap_open(TriggerRelationId, RowExclusiveLock); - + ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); - + scan = systable_beginscan(tri_rel, TriggerRelidNameIndexId, true, NULL, 1, &key); while (HeapTupleIsValid(tri_tuple = systable_getnext(scan))) { bool is_null = false; @@ -11655,7 +11692,7 @@ static void UpdatePgTriggerFirstAfter(Relation rel, int startattnum, int endattn bool nulls[Natts_pg_trigger] = { 0 }; bool replaces[Natts_pg_trigger] = { 0 }; HeapTuple new_tri_tuple; - + Datum tgattr_datum = fastgetattr(tri_tuple, Anum_pg_trigger_tgattr, tri_rel->rd_att, &is_null); if (!is_null) { int2vector *tgattr = (int2vector *)DatumGetPointer(tgattr_datum); @@ -11669,37 +11706,37 @@ static void UpdatePgTriggerFirstAfter(Relation rel, int startattnum, int endattn new_tgattr->values[i] = tgattr->values[i]; } } - + values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(new_tgattr); replaces[Anum_pg_trigger_tgattr - 1] = true; } - + Datum tgqual_datum = fastgetattr(tri_tuple, Anum_pg_trigger_tgqual, tri_rel->rd_att, &is_null); if (!is_null) { char *tgqual_string = NULL; Node *tgqual = NULL; Node *new_tgqual = NULL; - + tgqual_string = TextDatumGetCString(tgqual_datum); tgqual = (Node *)stringToNode(tgqual_string); - + new_tgqual = UpdateVarattnoAfterAddColumn(tgqual, startattnum, endattnum, is_increase); tgqual_string = nodeToString(new_tgqual); values[Anum_pg_trigger_tgqual - 1] = CStringGetTextDatum(tgqual_string); replaces[Anum_pg_trigger_tgqual - 1] = true; pfree_ext(tgqual_string); } - + new_tri_tuple = heap_modify_tuple(tri_tuple, RelationGetDescr(tri_rel), values, nulls, replaces); simple_heap_update(tri_rel, &new_tri_tuple->t_self, new_tri_tuple); CatalogUpdateIndexes(tri_rel, new_tri_tuple); } - + systable_endscan(scan); heap_close(tri_rel, RowExclusiveLock); } - -/* + +/* * update pg_rlspolicy * 1. add column with first or after col_name. * 2. modify column to first or after column. @@ -11710,12 +11747,12 @@ static void UpdatePgRlspolicyFirstAfter(Relation rel, int startattnum, int endat HeapTuple rls_tuple; Relation rls_rel; SysScanDesc scan; - + rls_rel = heap_open(RlsPolicyRelationId, RowExclusiveLock); - + ScanKeyInit(&key, Anum_pg_rlspolicy_polrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); - + scan = systable_beginscan(rls_rel, PgRlspolicyPolrelidPolnameIndex, true, NULL, 1, &key); while (HeapTupleIsValid(rls_tuple = systable_getnext(scan))) { bool is_null = false; @@ -11723,28 +11760,28 @@ static void UpdatePgRlspolicyFirstAfter(Relation rel, int startattnum, int endat bool nulls[Natts_pg_rlspolicy] = { 0 }; bool replaces[Natts_pg_rlspolicy] = { 0 }; HeapTuple new_rls_tuple; - + Datum polqual_datum = heap_getattr(rls_tuple, Anum_pg_rlspolicy_polqual, rls_rel->rd_att, &is_null); if (!is_null) { char *polqual_string = NULL; Node *polqual = NULL; Node *new_polqual = NULL; - + polqual_string = TextDatumGetCString(polqual_datum); polqual = (Node *)stringToNode(polqual_string); - + new_polqual = UpdateVarattnoAfterAddColumn(polqual, startattnum, endattnum, is_increase); polqual_string = nodeToString(new_polqual); values[Anum_pg_rlspolicy_polqual - 1] = CStringGetTextDatum(polqual_string); replaces[Anum_pg_rlspolicy_polqual - 1] = true; pfree_ext(polqual_string); } - + new_rls_tuple = heap_modify_tuple(rls_tuple, RelationGetDescr(rls_rel), values, nulls, replaces); simple_heap_update(rls_rel, &new_rls_tuple->t_self, new_rls_tuple); CatalogUpdateIndexes(rls_rel, new_rls_tuple); } - + systable_endscan(scan); heap_close(rls_rel, RowExclusiveLock); } @@ -11797,14 +11834,14 @@ static ObjectAddress ATExecAddColumn(List** wqueue, AlteredTableInfo* tab, Relat ATSimplePermissions(rel, ATT_TABLE); attrdesc = heap_open(AttributeRelationId, RowExclusiveLock); - + if (is_addloc) { if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Un-supported feature"), errdetail("foreign table is not supported for add column first|after columnName"))); } - + if (RelationIsColumnFormat(rel)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Un-supported feature"), @@ -11946,7 +11983,7 @@ static ObjectAddress ATExecAddColumn(List** wqueue, AlteredTableInfo* tab, Relat #endif bool isDelta = RELATION_IS_DELTA(rel); - + /* construct new attribute's pg_attribute entry */ attribute.attrelid = myrelid; (void)namestrcpy(&(attribute.attname), colDef->colname); @@ -12034,7 +12071,7 @@ static ObjectAddress ATExecAddColumn(List** wqueue, AlteredTableInfo* tab, Relat UpdatePgDependFirstAfter(rel, newattnum, currattnum, true); UpdateGenerateColFirstAfter(rel, newattnum, currattnum, true); UpdateIndexFirstAfter(rel); - + /* create or replace view */ ReplaceViewQueryFirstAfter(query_str); } @@ -12552,7 +12589,7 @@ static ObjectAddress ATExecDropNotNull(Relation rel, const char* colName, LOCKMO CatalogUpdateIndexes(attr_rel, tuple); ObjectAddressSubSet(address, RelationRelationId, RelationGetRelid(rel), attnum); - + } else address = InvalidObjectAddress; @@ -12717,7 +12754,7 @@ static ObjectAddress ATExecColumnDefault(Relation rel, const char* colName, Node ObjectAddressSubSet(address, RelationRelationId, RelationGetRelid(rel), attnum); return address; - + } /* @@ -12744,7 +12781,7 @@ static void ATPrepSetStatistics(Relation rel) } } -/* +/* * Return value is the address of the modified column */ static ObjectAddress ATExecSetStatistics( @@ -12944,7 +12981,7 @@ static ObjectAddress ATExecSetOptions(Relation rel, const char* colName, Node* o /* * ALTER TABLE ALTER COLUMN SET STORAGE * - * Return value is the address of the modified column + * Return value is the address of the modified column */ static ObjectAddress ATExecSetStorage(Relation rel, const char* colName, Node* newValue, LOCKMODE lockmode) { @@ -13013,7 +13050,7 @@ static ObjectAddress ATExecSetStorage(Relation rel, const char* colName, Node* n heap_close(attrelation, RowExclusiveLock); ObjectAddressSubSet(address, RelationRelationId, RelationGetRelid(rel), attnum); - return address; + return address; } /* @@ -13570,7 +13607,7 @@ static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo* tab, Relation re (g_instance.attr.attr_common.allowSystemTableMods || u_sess->attr.attr_common.IsInplaceUpgrade)); /* index constraint */ CreateNonColumnComment(index_oid, stmt->indexOptions, RelationRelationId); - + index_close(indexRel, NoLock); return address; } @@ -14221,7 +14258,7 @@ static ObjectAddress ATAddForeignKeyConstraint(AlteredTableInfo* tab, Relation r ObjectAddressSet(address, ConstraintRelationId, constrOid); /* foreign key constraint */ CreateNonColumnComment(constrOid, fkconstraint->constraintOptions, ConstraintRelationId); - + /* * Create the triggers that will enforce the constraint. */ @@ -14449,12 +14486,12 @@ static ObjectAddress ATExecValidateConstraint(Relation rel, char* constrName, bo ObjectAddressSet(address, ConstraintRelationId, HeapTupleGetOid(tuple)); } else - address = InvalidObjectAddress; /* already validated */ + address = InvalidObjectAddress; /* already validated */ systable_endscan(scan); heap_close(conrel, RowExclusiveLock); - return address; + return address; } /* @@ -14765,6 +14802,7 @@ static void validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTuple tuple = NULL; bool isnull = false; MemoryContext oldcxt; + ListCell* lc = NULL; Form_pg_constraint constrForm = (Form_pg_constraint)GETSTRUCT(constrtup); EState* estate = CreateExecutorState(); @@ -14781,7 +14819,7 @@ static void validateCheckConstraint(Relation rel, HeapTuple constrtup) errmsg("null conbin for constraint %u", HeapTupleGetOid(constrtup)))); char* conbin = TextDatumGetCString(val); Expr* origexpr = (Expr*)stringToNode(conbin); - List* exprstate = (List*)ExecPrepareExpr((Expr*)make_ands_implicit(origexpr), estate); + List* exprstate = ExecPrepareExprList(make_ands_implicit(origexpr), estate); ExprContext* econtext = GetPerTupleExprContext(estate); TupleDesc tupdesc = RelationGetDescr(rel); TupleTableSlot* slot = MakeSingleTupleTableSlot(tupdesc, false, rel->rd_tam_ops); @@ -14798,11 +14836,22 @@ static void validateCheckConstraint(Relation rel, HeapTuple constrtup) * produced, so we don't leak memory. */ oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); + if (estate->es_is_flt_frame){ + foreach (lc, exprstate) { + ExprState* exprState = (ExprState*)lfirst(lc); - if (!ExecQual(exprstate, econtext, true)) - ereport(ERROR, - (errcode(ERRCODE_CHECK_VIOLATION), - errmsg("check constraint \"%s\" is violated by some row", NameStr(constrForm->conname)))); + if (!ExecCheckByFlatten(exprState, econtext)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("check constraint \"%s\" is violated by some row", NameStr(constrForm->conname)))); + } + } else { + if (!ExecQualByRecursion(exprstate, econtext, true)){ + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("check constraint \"%s\" is violated by some row", NameStr(constrForm->conname)))); + } + } ResetExprContext(econtext); MemoryContextSwitchTo(oldcxt); @@ -15668,7 +15717,7 @@ static void DelDependencONDataType(const Relation rel, Relation depRel, const Fo systable_endscan(scan); } -/* +/* * update pg_attrdef adnum for the modified column with first or after column. */ static void UpdateAttrdefAdnumFirstAfter(Relation rel, Oid myrelid, int curattnum, int newattnum, @@ -15677,37 +15726,37 @@ static void UpdateAttrdefAdnumFirstAfter(Relation rel, Oid myrelid, int curattnu ScanKeyData key[2]; HeapTuple def_tuple; SysScanDesc scan; - + ScanKeyInit(&key[0], Anum_pg_attrdef_adrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(myrelid)); ScanKeyInit(&key[1], Anum_pg_attrdef_adnum, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum(curattnum)); - + scan = systable_beginscan(rel, AttrDefaultIndexId, true, NULL, 2, key); - + def_tuple = systable_getnext(scan); if (HeapTupleIsValid(def_tuple)) { Datum values[Natts_pg_attrdef] = { 0 }; bool nulls[Natts_pg_attrdef] = { 0 }; bool replaces[Natts_pg_attrdef] = { 0 }; HeapTuple new_def_tuple; - + if (has_default != NULL) { *has_default = true; } - + values[Anum_pg_attrdef_adnum - 1] = Int16GetDatum(newattnum); replaces[Anum_pg_attrdef_adnum - 1] = true; - + new_def_tuple = heap_modify_tuple(def_tuple, RelationGetDescr(rel), values, nulls, replaces); simple_heap_update(rel, &new_def_tuple->t_self, new_def_tuple); CatalogUpdateIndexes(rel, new_def_tuple); - + heap_freetuple_ext(new_def_tuple); } - + systable_endscan(scan); } - -/* + +/* * update pg_depend refobjsubid for the modified column with first or after column. */ static void UpdateDependRefobjsubidFirstAfter(Relation rel, Oid myrelid, int curattnum, int newattnum, @@ -15717,26 +15766,26 @@ static void UpdateDependRefobjsubidFirstAfter(Relation rel, Oid myrelid, int cur HeapTuple dep_tuple; Form_pg_depend dep_form; SysScanDesc scan; - + ScanKeyInit(&key[0], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(myrelid)); ScanKeyInit(&key[1], Anum_pg_depend_refobjsubid, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(curattnum)); - + scan = systable_beginscan(rel, DependReferenceIndexId, true, NULL, 2, key); while (HeapTupleIsValid(dep_tuple = systable_getnext(scan))) { Datum values[Natts_pg_depend] = { 0 }; bool nulls[Natts_pg_depend] = { 0 }; bool replaces[Natts_pg_depend] = { 0 }; HeapTuple new_dep_tuple; - + dep_form = (Form_pg_depend)GETSTRUCT(dep_tuple); - + if (has_depend != NULL) { *has_depend = true; } - + values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(-1); replaces[Anum_pg_depend_refobjsubid - 1] = true; - + if (dep_form->objid == myrelid) { int startattnum; int endattnum; @@ -15758,17 +15807,17 @@ static void UpdateDependRefobjsubidFirstAfter(Relation rel, Oid myrelid, int cur replaces[Anum_pg_depend_objsubid - 1] = true; } } - + new_dep_tuple = heap_modify_tuple(dep_tuple, RelationGetDescr(rel), values, nulls, replaces); simple_heap_update(rel, &new_dep_tuple->t_self, new_dep_tuple); CatalogUpdateIndexes(rel, new_dep_tuple); - + heap_freetuple_ext(new_dep_tuple); } systable_endscan(scan); } - -/* + +/* * update pg_depend refobjsubid for the modified column with first or after column. */ static void UpdateDependRefobjsubidToNewattnum(Relation rel, Oid myrelid, int curattnum, int newattnum) @@ -15777,10 +15826,10 @@ static void UpdateDependRefobjsubidToNewattnum(Relation rel, Oid myrelid, int cu HeapTuple dep_tuple; Form_pg_depend dep_form; SysScanDesc scan; - + ScanKeyInit(&key[0], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(myrelid)); ScanKeyInit(&key[1], Anum_pg_depend_refobjsubid, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(curattnum)); - + scan = systable_beginscan(rel, DependReferenceIndexId, true, NULL, 2, key); while (HeapTupleIsValid(dep_tuple = systable_getnext(scan))) { Datum values[Natts_pg_depend] = { 0 }; @@ -15788,21 +15837,21 @@ static void UpdateDependRefobjsubidToNewattnum(Relation rel, Oid myrelid, int cu bool replaces[Natts_pg_depend] = { 0 }; HeapTuple new_dep_tuple; dep_form = (Form_pg_depend)GETSTRUCT(dep_tuple); - + values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(newattnum); replaces[Anum_pg_depend_refobjsubid - 1] = true; - + new_dep_tuple = heap_modify_tuple(dep_tuple, RelationGetDescr(rel), values, nulls, replaces); simple_heap_update(rel, &new_dep_tuple->t_self, new_dep_tuple); CatalogUpdateIndexes(rel, new_dep_tuple); - + heap_freetuple_ext(new_dep_tuple); } - + systable_endscan(scan); } - -/* + +/* * update pg_partition partkey for the modified column with first or after column. */ static void UpdatePartitionPartkeyFirstAfter(Oid myrelid, int curattnum, int newattnum) @@ -15811,15 +15860,15 @@ static void UpdatePartitionPartkeyFirstAfter(Oid myrelid, int curattnum, int new HeapTuple par_tuple; Relation par_rel; SysScanDesc scan; - + par_rel = heap_open(PartitionRelationId, RowExclusiveLock); - + ScanKeyInit(&skey, Anum_pg_partition_parentid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(myrelid)); - + scan = systable_beginscan(par_rel, PartitionParentOidIndexId, true, NULL, 1, &skey); while (HeapTupleIsValid(par_tuple = systable_getnext(scan))) { bool is_null = false; - + // update pg_partition_partkey Datum partkey_datum = SysCacheGetAttr(PARTRELID, par_tuple, Anum_pg_partition_partkey, &is_null); if (!is_null) { @@ -15829,7 +15878,7 @@ static void UpdatePartitionPartkeyFirstAfter(Oid myrelid, int curattnum, int new int2vector *partkey = NULL; int2vector *new_partKey = NULL; HeapTuple new_par_tuple; - + partkey = (int2vector *)DatumGetPointer(partkey_datum); new_partKey = buildint2vector(NULL, partkey->dim1); for (int i = 0; i < partkey->dim1; i++) { @@ -15841,11 +15890,11 @@ static void UpdatePartitionPartkeyFirstAfter(Oid myrelid, int curattnum, int new } values[Anum_pg_partition_partkey - 1] = PointerGetDatum(new_partKey); replaces[Anum_pg_partition_partkey - 1] = true; - + new_par_tuple = heap_modify_tuple(par_tuple, RelationGetDescr(par_rel), values, nulls, replaces); simple_heap_update(par_rel, &new_par_tuple->t_self, new_par_tuple); CatalogUpdateIndexes(par_rel, new_par_tuple); - + pfree_ext(new_partKey); heap_freetuple_ext(new_par_tuple); } @@ -15853,17 +15902,17 @@ static void UpdatePartitionPartkeyFirstAfter(Oid myrelid, int curattnum, int new systable_endscan(scan); heap_close(par_rel, RowExclusiveLock); } - + static int GetNewattnumFirstAfter(Relation rel, AlterTableCmd* cmd, int curattnum) { bool is_first = cmd->is_first; char *after_name = cmd->after_name; int newattnum = 0; - + if (is_first && curattnum == 1) { return 0; } - + if (is_first) { newattnum = 1; } else if (after_name != NULL) { @@ -15871,7 +15920,7 @@ static int GetNewattnumFirstAfter(Relation rel, AlterTableCmd* cmd, int curattnu if (newattnum + 1 == curattnum) { return 0; } - + if (newattnum == curattnum) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("Unknown column \"%s\" in \"%s\"", after_name, RelationGetRelationName(rel)))); @@ -15881,7 +15930,7 @@ static int GetNewattnumFirstAfter(Relation rel, AlterTableCmd* cmd, int curattnu } return newattnum; } - + static void AlterColumnToFirstAfter(AlteredTableInfo* tab, Relation rel, AlterTableCmd* cmd, int curattnum) { @@ -15896,36 +15945,36 @@ static void AlterColumnToFirstAfter(AlteredTableInfo* tab, Relation rel, AlterTa bool has_partition = false; bool is_increase = false; List *query_str = NIL; - + newattnum = GetNewattnumFirstAfter(rel, cmd, curattnum); if (newattnum == 0) { return; } - + tab->rewrite = true; - + attr_rel = heap_open(AttributeRelationId, RowExclusiveLock); - + att_tuple_old = SearchSysCacheCopy2(ATTNUM, ObjectIdGetDatum(myrelid), Int16GetDatum(curattnum)); if (!HeapTupleIsValid(att_tuple_old)) { ereport(ERROR, (errmodule(MOD_SEC), errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for attribute %d of relation %u", curattnum, myrelid), errdetail("N/A"), errcause("System error."), erraction("Contact engineer to support."))); } - + att_form_old = (Form_pg_attribute)GETSTRUCT(att_tuple_old); - + att_form_old->attnum = 0; - + simple_heap_update(attr_rel, &att_tuple_old->t_self, att_tuple_old); CatalogUpdateIndexes(attr_rel, att_tuple_old); - + Relation def_rel = heap_open(AttrDefaultRelationId, RowExclusiveLock); UpdateAttrdefAdnumFirstAfter(def_rel, myrelid, curattnum, 0, &has_default); - + Relation dep_rel = heap_open(DependRelationId, RowExclusiveLock); UpdateDependRefobjsubidFirstAfter(dep_rel, myrelid, curattnum, newattnum, &has_depend); - + if (newattnum <= curattnum - 1) { startattnum = newattnum; endattnum = curattnum - 1; @@ -15934,7 +15983,7 @@ static void AlterColumnToFirstAfter(AlteredTableInfo* tab, Relation rel, AlterTa startattnum = curattnum + 1; endattnum = newattnum; } - + UpdatePgPartitionFirstAfter(rel, startattnum, endattnum, is_increase, true, &has_partition); UpdatePgAttributeFirstAfter(attr_rel, myrelid, startattnum, endattnum, is_increase); UpdatePgIndexFirstAfter(rel, startattnum, endattnum, is_increase); @@ -15945,11 +15994,11 @@ static void AlterColumnToFirstAfter(AlteredTableInfo* tab, Relation rel, AlterTa UpdatePgTriggerFirstAfter(rel, startattnum, endattnum, is_increase); UpdatePgRlspolicyFirstAfter(rel, startattnum, endattnum, is_increase); CommandCounterIncrement(); - + UpdateGenerateColFirstAfter(rel, startattnum, endattnum, is_increase); UpdatePgDependFirstAfter(rel, startattnum, endattnum, is_increase); CommandCounterIncrement(); - + att_tuple_new = SearchSysCacheCopy2(ATTNUM, ObjectIdGetDatum(myrelid), Int16GetDatum(0)); if (!HeapTupleIsValid(att_tuple_new)) { ereport(ERROR, (errmodule(MOD_SEC), errcode(ERRCODE_CACHE_LOOKUP_FAILED), @@ -15957,36 +16006,36 @@ static void AlterColumnToFirstAfter(AlteredTableInfo* tab, Relation rel, AlterTa errcause("System error."), erraction("Contact engineer to support."))); } attr_form_new = (Form_pg_attribute)GETSTRUCT(att_tuple_new); - + attr_form_new->attnum = newattnum; simple_heap_update(attr_rel, &att_tuple_new->t_self, att_tuple_new); // keep system catalog indexes current CatalogUpdateIndexes(attr_rel, att_tuple_new); - + heap_close(attr_rel, RowExclusiveLock); heap_freetuple_ext(att_tuple_old); heap_freetuple_ext(att_tuple_new); - + if (has_default) { UpdateAttrdefAdnumFirstAfter(def_rel, myrelid, 0, newattnum, NULL); } heap_close(def_rel, RowExclusiveLock); - + if (has_depend) { UpdateDependRefobjsubidToNewattnum(dep_rel, myrelid, -1, newattnum); } heap_close(dep_rel, RowExclusiveLock); - + if (has_partition) { UpdatePartitionPartkeyFirstAfter(myrelid, 0, newattnum); } - + CommandCounterIncrement(); - + /* create or replace view */ ReplaceViewQueryFirstAfter(query_str); } - + static bool CheckIndexIsConstraint(Relation dep_rel, Oid objid, Oid *refobjid) { ScanKeyData key[2]; @@ -15994,12 +16043,12 @@ static bool CheckIndexIsConstraint(Relation dep_rel, Oid objid, Oid *refobjid) SysScanDesc scan; Form_pg_depend dep_form; bool is_constraint = false; - + ScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationRelationId)); ScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(objid)); scan = systable_beginscan(dep_rel, DependDependerIndexId, true, NULL, 2, key); - + while (HeapTupleIsValid(dep_tuple = systable_getnext(scan))) { dep_form = (Form_pg_depend)GETSTRUCT(dep_tuple); if (dep_form->refclassid == ConstraintRelationId && dep_form->refobjsubid == 0) { @@ -16011,21 +16060,21 @@ static bool CheckIndexIsConstraint(Relation dep_rel, Oid objid, Oid *refobjid) systable_endscan(scan); return is_constraint; } - + static void UpdateNewvalsAttnum(AlteredTableInfo* tab, Relation rel, AlterTableCmd* cmd, char* col_name) { ListCell* l = NULL; foreach(l, tab->newvals) { NewColumnValue* ex = (NewColumnValue*)lfirst(l); - + if (ex->col_name == NULL) { continue; } - + if (strcmp(ex->col_name, col_name) == 0) { HeapTuple heap_tup; Form_pg_attribute att_tup; - + heap_tup = SearchSysCacheCopyAttName(RelationGetRelid(rel), col_name); if (!HeapTupleIsValid(heap_tup)) { /* shouldn't happen */ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), @@ -16034,7 +16083,7 @@ static void UpdateNewvalsAttnum(AlteredTableInfo* tab, Relation rel, AlterTableC att_tup = (Form_pg_attribute)GETSTRUCT(heap_tup); ex->attnum = att_tup->attnum; ex->newattnum = GetNewattnumFirstAfter(rel, cmd, ex->attnum); - + tableam_tops_free_tuple(heap_tup); } } @@ -16565,7 +16614,7 @@ static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char* c ObjectAddressSubSet(address, RelationRelationId, RelationGetRelid(rel), attnum); - + ReleaseSysCache(tuple); simple_heap_update(attrel, &newtuple->t_self, newtuple); @@ -16575,7 +16624,7 @@ static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char* c tableam_tops_free_tuple(newtuple); - return address; + return address; } // delete record about psort oid and index oid from pg_depend, @@ -16756,14 +16805,14 @@ static void setPrimaryNotnull(Oid relid, IndexStmt *stmt, AlteredTableInfo* tab) if (stmt->primary && !stmt->internal_flag) { ListCell* columns = NULL; IndexElem* iparam = NULL; - + tab->is_modify_primary = true; foreach (columns, stmt->indexParams) { HeapTuple atttuple; Form_pg_attribute attform; - + iparam = (IndexElem*)lfirst(columns); - + atttuple = SearchSysCacheAttName(relid, iparam->name); if (!HeapTupleIsValid(atttuple)) { ereport(ERROR, @@ -16771,17 +16820,17 @@ static void setPrimaryNotnull(Oid relid, IndexStmt *stmt, AlteredTableInfo* tab) errmsg("cache lookup failed for attribute %s of relation %u", iparam->name, relid))); } attform = (Form_pg_attribute)GETSTRUCT(atttuple); - + if (!attform->attnotnull) { /* Add a subcommand to make this one NOT NULL */ AlterTableCmd* cmd = makeNode(AlterTableCmd); - + cmd->subtype = AT_SetNotNull; cmd->name = pstrdup(NameStr(attform->attname)); tab->subcmds[AT_PASS_ADD_CONSTR] = lappend(tab->subcmds[AT_PASS_ADD_CONSTR], cmd); } - + ReleaseSysCache(atttuple); } } @@ -17634,7 +17683,7 @@ static ObjectAddress ATExecClusterOn(Relation rel, const char* indexName, LOCKMO mark_index_clustered(rel, indexOid); ObjectAddressSet(address, RelationRelationId, indexOid); - + return address; } @@ -18125,7 +18174,7 @@ static void ATExecSetRelOptions(Relation rel, List* defList, AlterTableType oper errmsg("WITH CHECK OPTION is supported only on auto-updatable views"), errhint("%s", view_updatable_error))); - /* + /* * Views based on MySQL foreign table is not allowed to add check option, * because returning clause which check option dependend on is not supported * on MySQL. @@ -19270,7 +19319,7 @@ static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar* parent, LOCK HeapTuple inheritsTuple; int32 inhseqno; List* children = NIL; - ObjectAddress address; + ObjectAddress address; if (RELATION_IS_PARTITIONED(child_rel)) { ereport(ERROR, @@ -19387,7 +19436,7 @@ static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar* parent, LOCK ObjectAddressSet(address, RelationRelationId, RelationGetRelid(parent_rel)); - + /* Now we're done with pg_inherits */ heap_close(catalogRelation, RowExclusiveLock); @@ -19647,7 +19696,7 @@ static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel static ObjectAddress ATExecDropInherit(Relation rel, RangeVar* parent, LOCKMODE lockmode) { Relation parent_rel; - Oid parent_oid; + Oid parent_oid; Relation catalogRelation; SysScanDesc scan; ScanKeyData key[3]; @@ -19807,7 +19856,7 @@ static ObjectAddress ATExecDropInherit(Relation rel, RangeVar* parent, LOCKMODE heap_close(parent_rel, NoLock); ObjectAddressSet(address, RelationRelationId, parent_oid); - + return address; } @@ -21072,7 +21121,7 @@ ObjectAddress AlterTableNamespace(AlterObjectSchemaStmt* stmt, Oid *oldschema) RangeVar* newrv = NULL; ObjectAddresses* objsMoved = NULL; ObjectAddress myself; - + relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, stmt->missing_ok, @@ -21093,7 +21142,7 @@ ObjectAddress AlterTableNamespace(AlterObjectSchemaStmt* stmt, Oid *oldschema) errdetail("target table is a mot table"))); } #endif - + TrForbidAccessRbObject(RelationRelationId, relid, stmt->relation->relname); rel = relation_open(relid, NoLock); @@ -21153,10 +21202,10 @@ ObjectAddress AlterTableNamespace(AlterObjectSchemaStmt* stmt, Oid *oldschema) free_object_addresses(objsMoved); ObjectAddressSet(myself, RelationRelationId, relid); - + if (oldschema) *oldschema = oldNspOid; - + /* close rel, but keep lock until commit */ relation_close(rel, NoLock); return myself; @@ -21228,7 +21277,7 @@ void AlterRelationNamespaceInternal( * Do nothing when there's nothing to do. */ if (!object_address_present(&thisobj, objsMoved)) { - /* + /* * Check relation name to ensure that it doesn't conflict with existing synonym. */ if (!IsInitdb && GetSynonymOid(NameStr(classForm->relname), newNspOid, true) != InvalidOid) { @@ -31200,7 +31249,7 @@ static int128 EvaluateAutoIncrement(Relation rel, TupleDesc desc, AttrNumber att ConstrAutoInc* cons_autoinc = desc->constr->cons_autoinc; int128 autoinc; bool modify_value = false; - + if (*is_null) { autoinc = 0; modify_value = desc->attrs[attnum - 1].attnotnull; @@ -31222,7 +31271,7 @@ static int128 EvaluateAutoIncrement(Relation rel, TupleDesc desc, AttrNumber att } return autoinc; } - + static void SetRelAutoIncrement(Relation rel, TupleDesc desc, int128 autoinc) { if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP) { @@ -31300,7 +31349,7 @@ void CheckRelAutoIncrementIndex(Oid relid, LOCKMODE lockmode) foreach_cell(l, idxoidlist) { Relation idxrel = index_open(lfirst_oid(l), AccessShareLock); Form_pg_index index = idxrel->rd_index; - + if (IndexIsValid(index) && (index->indisunique || index->indisprimary) && index->indkey.values[0] == autoinc_attnum) { found = true; @@ -31860,18 +31909,18 @@ static bool ModifiedColumnIsPrimaryKey(AlteredTableInfo* tab, AttrNumber attrnum (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for constraint %u", constraint_oid))); } - + if (((Form_pg_constraint)GETSTRUCT(tuple))->contype != CONSTRAINT_PRIMARY) { ReleaseSysCache(tuple); continue; } - + conkey_datum = SysCacheGetAttr(CONSTROID, tuple, Anum_pg_constraint_conkey, &isnull); if (isnull) { ReleaseSysCache(tuple); continue; } - + deconstruct_array(DatumGetArrayTypeP(conkey_datum), INT2OID, sizeof(int16), true, 's', &keys, NULL, &key_count); for (int i = 0; i < key_count; i++) { if (DatumGetInt16(keys[i]) == attrnum) { @@ -31880,11 +31929,11 @@ static bool ModifiedColumnIsPrimaryKey(AlteredTableInfo* tab, AttrNumber attrnum return true; } } - + pfree_ext(keys); ReleaseSysCache(tuple); } - + return false; } diff --git a/src/gausskernel/optimizer/commands/trigger.cpp b/src/gausskernel/optimizer/commands/trigger.cpp index d1676dfbb..2b7f2572b 100644 --- a/src/gausskernel/optimizer/commands/trigger.cpp +++ b/src/gausskernel/optimizer/commands/trigger.cpp @@ -3399,7 +3399,11 @@ static bool TriggerEnabled(EState* estate, ResultRelInfo* relinfo, Trigger* trig ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node*)make_ands_implicit((Expr*)tgqual); - *predicate = (List*)ExecPrepareExpr((Expr*)tgqual, estate); + if (estate->es_is_flt_frame){ + *predicate = (List*)ExecPrepareQualByFlatten((List*)tgqual, estate); + } else { + *predicate = (List*)ExecPrepareExpr((Expr*)tgqual, estate); + } (void)MemoryContextSwitchTo(oldContext); pfree_ext(tgqual); } @@ -3446,7 +3450,7 @@ static bool TriggerEnabled(EState* estate, ResultRelInfo* relinfo, Trigger* trig */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate, econtext)) return false; } diff --git a/src/gausskernel/optimizer/commands/typecmds.cpp b/src/gausskernel/optimizer/commands/typecmds.cpp index 1554b6154..59aa3a891 100644 --- a/src/gausskernel/optimizer/commands/typecmds.cpp +++ b/src/gausskernel/optimizer/commands/typecmds.cpp @@ -2765,7 +2765,7 @@ static void validateDomainConstraint(Oid domainoid, char* ccbin) econtext->domainValue_datum = d; econtext->domainValue_isNull = isNull; - conResult = ExecEvalExprSwitchContext(exprstate, econtext, &isNull, NULL); + conResult = ExecEvalExprSwitchContext(exprstate, econtext, &isNull); if (!isNull && !DatumGetBool(conResult)) ereport(ERROR, @@ -3171,6 +3171,8 @@ List* GetDomainConstraints(Oid typeOid) check_expr = expression_planner(check_expr); r = makeNode(DomainConstraintState); + r->check_node = check_expr; /* for flattened expression frame */ + r->constrainttype = DOM_CONSTRAINT_CHECK; r->name = pstrdup(NameStr(c->conname)); r->check_expr = ExecInitExpr(check_expr, NULL); @@ -3201,6 +3203,7 @@ List* GetDomainConstraints(Oid typeOid) r->constrainttype = DOM_CONSTRAINT_NOTNULL; r->name = pstrdup("NOT NULL"); r->check_expr = NULL; + r->check_node = NULL; /* lcons to apply the nullness check FIRST */ result = lcons(r, result); diff --git a/src/gausskernel/optimizer/plan/createplan.cpp b/src/gausskernel/optimizer/plan/createplan.cpp index d00c16a0d..19bed05b2 100755 --- a/src/gausskernel/optimizer/plan/createplan.cpp +++ b/src/gausskernel/optimizer/plan/createplan.cpp @@ -6904,7 +6904,6 @@ ProjectSet *make_project_set(List *tlist, Plan *subplan) ProjectSet *node = makeNode(ProjectSet); Plan *plan = &node->plan; - #ifdef STREAMPLAN if (IS_STREAM_PLAN && subplan) { inherit_plan_locator_info(plan, subplan); diff --git a/src/gausskernel/optimizer/plan/planner.cpp b/src/gausskernel/optimizer/plan/planner.cpp index 050a4053d..d94dc470b 100755 --- a/src/gausskernel/optimizer/plan/planner.cpp +++ b/src/gausskernel/optimizer/plan/planner.cpp @@ -10283,8 +10283,7 @@ bool CheckColumnsSuportedByBatchMode(List *targetList, List *qual) /* Consider the targetList */ foreach (l, targetList) { ListCell *vl = NULL; - GenericExprState *gstate = (GenericExprState *)lfirst(l); - TargetEntry *tle = (TargetEntry *)gstate->xprstate.expr; + TargetEntry *tle = lfirst_node(TargetEntry, l); /* if have set-returning function, not support. */ if (vector_engine_setfunc_walker((Node*)tle, NULL)) { diff --git a/src/gausskernel/optimizer/util/bucketpruning.cpp b/src/gausskernel/optimizer/util/bucketpruning.cpp index cafac741a..f233e7c18 100644 --- a/src/gausskernel/optimizer/util/bucketpruning.cpp +++ b/src/gausskernel/optimizer/util/bucketpruning.cpp @@ -806,7 +806,7 @@ static int GetExecBucketId(ExecNodes* exec_nodes, ParamListInfo params) ExprState* exprstate = ExecInitExpr(expr, NULL); - Datum partvalue = ExecEvalExpr(exprstate, exprcontext, &isnull, NULL); + Datum partvalue = ExecEvalExpr(exprstate, exprcontext, &isnull); MemoryContextSwitchTo(oldContext); diff --git a/src/gausskernel/optimizer/util/clauses.cpp b/src/gausskernel/optimizer/util/clauses.cpp index b4352a68a..f7c0d1bc1 100644 --- a/src/gausskernel/optimizer/util/clauses.cpp +++ b/src/gausskernel/optimizer/util/clauses.cpp @@ -692,7 +692,7 @@ static void count_agg_clauses_walker_isa(Node* node, count_agg_clauses_context* costs->aggWidth += avgwidth; } else if (aggtranstype == INTERNALOID) { #ifndef ENABLE_MULTIPLE_NODES - /* + /* * XXX: we apply the pg commit 69c8fbac201652282e18b0e2e301d4ada991fbde * but the difference of pg_aggregate bwtween pg and og * we have to do some hard code here(og's pg_aggregate doesn't have the column aggtransspace) @@ -2485,7 +2485,7 @@ Node* simplify_select_into_expression(Node* node, ParamListInfo boundParams) /* If the user-defined variable has been defined, * then find the existed value. */ - optbase_eval_user_var_in_opexpr(((OpExpr *)tn)->args); + optbase_eval_user_var_in_opexpr(((OpExpr *)tn)->args); } tn = eval_const_expressions_mutator(tn, &context); te = makeTargetEntry((Expr *)tn, attno++, te->resname, false); @@ -4792,7 +4792,7 @@ Expr* evaluate_expr(Expr* expr, Oid result_type, int32 result_typmod, Oid result if (econtext != NULL) { econtext->can_ignore = can_ignore; } - const_val = ExecEvalExprSwitchContext(exprstate, econtext, &const_is_null, NULL); + const_val = ExecEvalExprSwitchContext(exprstate, econtext, &const_is_null); /* Get info needed about result datatype */ get_typlenbyval(result_type, &resultTypLen, &resultTypByVal); diff --git a/src/gausskernel/optimizer/util/dataskew.cpp b/src/gausskernel/optimizer/util/dataskew.cpp index 210f15c63..c2d7cd7be 100755 --- a/src/gausskernel/optimizer/util/dataskew.cpp +++ b/src/gausskernel/optimizer/util/dataskew.cpp @@ -1488,7 +1488,7 @@ bool SkewInfo::ExecSkewvalExpr(Expr* expr, TupleTableSlot* slot) econtext->ecxt_per_tuple_memory = m_context; econtext->ecxt_scantuple = slot; - canpass = ExecEvalExpr(exprstate, econtext, &isNull, NULL); + canpass = ExecEvalExpr(exprstate, econtext, &isNull); return canpass; } diff --git a/src/gausskernel/optimizer/util/joinskewinfo.cpp b/src/gausskernel/optimizer/util/joinskewinfo.cpp index ba4ca9dc7..0ed7090d4 100644 --- a/src/gausskernel/optimizer/util/joinskewinfo.cpp +++ b/src/gausskernel/optimizer/util/joinskewinfo.cpp @@ -1792,7 +1792,11 @@ void StreamSkew::init(bool isVec) if (isVec) { qsstate->skew_quals_state = (List*)ExecInitVecExpr((Expr*)(qsinfo->skew_quals), NULL); } else { - qsstate->skew_quals_state = (List*)ExecInitExpr((Expr*)(qsinfo->skew_quals), NULL); + if (m_estate->es_is_flt_frame) { + qsstate->skew_quals_state = (List*)ExecInitQualByFlatten(qsinfo->skew_quals, NULL); + } else { + qsstate->skew_quals_state = (List*)ExecInitExprByRecursion((Expr*)(qsinfo->skew_quals), NULL); + } } if (qsstate->skew_quals_state != NIL) @@ -1821,7 +1825,7 @@ int StreamSkew::chooseStreamType(TupleTableSlot* tuple) foreach(lc, m_skewQual) { qsstate = (QualSkewState*)lfirst(lc); - if (ExecQual(qsstate->skew_quals_state, m_econtext, false)) { + if (ExecQual(qsstate->skew_quals_state, m_econtext)) { switch (qsstate->skew_stream_type) { case PART_REDISTRIBUTE_PART_BROADCAST: case PART_LOCAL_PART_BROADCAST: diff --git a/src/gausskernel/optimizer/util/predtest.cpp b/src/gausskernel/optimizer/util/predtest.cpp index 97f8f9887..b72ef73fe 100644 --- a/src/gausskernel/optimizer/util/predtest.cpp +++ b/src/gausskernel/optimizer/util/predtest.cpp @@ -1372,7 +1372,7 @@ static bool btree_predicate_proof(const Expr* predicate, const Node* clause, boo test_exprstate = ExecInitExpr(test_expr, NULL); /* And execute it. */ - test_result = ExecEvalExprSwitchContext(test_exprstate, GetPerTupleExprContext(estate), &isNull, NULL); + test_result = ExecEvalExprSwitchContext(test_exprstate, GetPerTupleExprContext(estate), &isNull); /* Get back to outer memory context */ (void)MemoryContextSwitchTo(oldcontext); diff --git a/src/gausskernel/optimizer/util/relnode.cpp b/src/gausskernel/optimizer/util/relnode.cpp index 777a0c5bc..450f5e816 100755 --- a/src/gausskernel/optimizer/util/relnode.cpp +++ b/src/gausskernel/optimizer/util/relnode.cpp @@ -34,6 +34,7 @@ #include "access/transam.h" #include "utils/selfuncs.h" #include "utils/lsyscache.h" +#include "optimizer/tlist.h" #ifdef PGXC #include "pgxc/pgxc.h" diff --git a/src/gausskernel/process/stream/streamMain.cpp b/src/gausskernel/process/stream/streamMain.cpp index 80980db6e..e5d1bb041 100755 --- a/src/gausskernel/process/stream/streamMain.cpp +++ b/src/gausskernel/process/stream/streamMain.cpp @@ -185,6 +185,7 @@ static void InitStreamThread() if (!IS_THREAD_POOL_STREAM) { ExtractProduerInfo(); + ExtractProduerSkewInfo(); t_thrd.proc_cxt.PostInit->SetDatabaseAndUser( u_sess->stream_cxt.producer_obj->getDbName(), InvalidOid, u_sess->stream_cxt.producer_obj->getUserName()); repair_guc_variables(); @@ -294,9 +295,6 @@ void ExtractProduerInfo() u_sess->stream_cxt.producer_obj->netInit(); u_sess->stream_cxt.producer_obj->setThreadInit(true); -#ifdef ENABLE_MULTIPLE_NODES - u_sess->stream_cxt.producer_obj->initSkewState(); -#endif if (u_sess->stream_cxt.producer_obj->isDummy()) { u_sess->exec_cxt.executorStopFlag = true; @@ -321,6 +319,12 @@ void ExtractProduerInfo() u_sess->stream_cxt.producer_obj->getKey().planNodeId, u_sess->stream_cxt.producer_obj->getKey().smpIdentifier); } +void ExtractProduerSkewInfo() +{ +#ifdef ENABLE_MULTIPLE_NODES + u_sess->stream_cxt.producer_obj->initSkewState(); +#endif +} static void HandleStreamSigjmp() { diff --git a/src/gausskernel/process/tcop/postgres.cpp b/src/gausskernel/process/tcop/postgres.cpp index 8ce3c992c..1bc421d69 100755 --- a/src/gausskernel/process/tcop/postgres.cpp +++ b/src/gausskernel/process/tcop/postgres.cpp @@ -493,7 +493,7 @@ static int interactive_getc(void) return c; } else { /* - * With light_comm, do not process catchup interrupts or notifications + * With light_comm, do not process catchup interrupts or notifications * while reading. */ CHECK_FOR_INTERRUPTS(); @@ -868,7 +868,7 @@ void process_client_write_interrupt (bool blocked) } CHECK_FOR_INTERRUPTS(); } - + errno = save_errno; } @@ -1435,7 +1435,7 @@ PlannedStmt* pg_plan_query(Query* querytree, int cursorOptions, ParamListInfo bo plan->planTree->rightRefState = nullptr; } } - + return plan; } @@ -8182,7 +8182,7 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam if (MyProcPort && MyProcPort->protocol_config->fn_comm_reset) { MyProcPort->protocol_config->fn_comm_reset(); } - + /* statement retry phase : long jump */ if (IsStmtRetryEnabled()) { bool is_extended_query = u_sess->postgres_cxt.doing_extended_query_message; @@ -8283,7 +8283,7 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam } ReleaseResownerOutOfTransaction(); - + /* release resource held by lsc */ AtEOXact_SysDBCache(false); /* Notice: at the most time it isn't necessary to call because @@ -8629,7 +8629,7 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam } template0_locked = true; } - + if (isRestoreMode && !IsAbortedTransactionBlockState()) { ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; @@ -9076,7 +9076,7 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam u_sess->debug_query_id = 0; send_ready_for_query = true; } break; - + case 'o': { // switch role if (unlikely(!IsConnFromCoord())) { @@ -9098,7 +9098,7 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam int save_sec_context = 0; int sec_context = 0; GetUserIdAndSecContext(&save_userid, &save_sec_context); - + if (is_reset) { /* reset to origin role */ sec_context = save_sec_context & (~RECEIVER_LOCAL_USERID_CHANGE); diff --git a/src/gausskernel/process/threadpool/threadpool_stream.cpp b/src/gausskernel/process/threadpool/threadpool_stream.cpp index 8531af532..5ba6d6284 100644 --- a/src/gausskernel/process/threadpool/threadpool_stream.cpp +++ b/src/gausskernel/process/threadpool/threadpool_stream.cpp @@ -152,6 +152,7 @@ void ThreadPoolStream::InitStream() repair_guc_variables(); RestoreStreamSyncParam(&m_producer->m_syncParam); + ExtractProduerSkewInfo(); u_sess->exec_cxt.under_stream_runtime = true; u_sess->attr.attr_common.remoteConnType = REMOTE_CONN_DATANODE; diff --git a/src/gausskernel/runtime/executor/Makefile b/src/gausskernel/runtime/executor/Makefile index ea3ee6333..af1fee525 100644 --- a/src/gausskernel/runtime/executor/Makefile +++ b/src/gausskernel/runtime/executor/Makefile @@ -35,6 +35,7 @@ endif OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \ execProcnode.o execQual.o execReplication.o execScan.o execTuples.o \ + execExprInterp.o execExpr.o execSRF.o\ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \ diff --git a/src/gausskernel/runtime/executor/execExpr.cpp b/src/gausskernel/runtime/executor/execExpr.cpp new file mode 100644 index 000000000..a107c2c60 --- /dev/null +++ b/src/gausskernel/runtime/executor/execExpr.cpp @@ -0,0 +1,2784 @@ +#include "postgres.h" +#include "nodes/execnodes.h" +#include "nodes/execExpr.h" +#include "executor/node/nodeSubplan.h" +#include "executor/executor.h" +#include "access/nbtree.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "optimizer/clauses.h" +#include "optimizer/planner.h" +#include "pgstat.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/typcache.h" +#include "utils/acl.h" +#include "knl/knl_session.h" +#include "fmgr.h" +#include "commands/typecmds.h" +#include "parser/parse_expr.h" + +typedef struct LastAttnumInfo +{ + AttrNumber last_inner; + AttrNumber last_outer; + AttrNumber last_scan; +} LastAttnumInfo; + +static void ExecReadyExpr(ExprState *state); +static void ExecInitExprRec(Expr *node, ExprState *state, + Datum *resv, bool *resnull, Expr *parent_node); +static void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s); +static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, + Oid funcid, Oid inputcollid, + ExprState *state); +static void ExecInitExprSlots(ExprState *state, Node *node); +static void ExecPushExprSlots(ExprState *state, LastAttnumInfo *info); +static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info); +static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, + ExprState *state); +static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, + ExprState *state, + Datum *resv, bool *resnull,Expr *node); +static bool isAssignmentIndirectionExpr(Expr *expr); +static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest, + ExprState *state, + Datum *resv, bool *resnull, Expr *node); + +/* + * ExecInitExpr: prepare an expression tree for execution + * + * This function builds and returns an ExprState implementing the given + * Expr node tree. The return ExprState can then be handed to ExecEvalExpr + * for execution. Because the Expr tree itself is read-only as far as + * ExecInitExpr and ExecEvalExpr are concerned, several different executions + * of the same plan tree can occur concurrently. (But note that an ExprState + * does mutate at runtime, so it can't be re-used concurrently.) + * + * This must be called in a memory context that will last as long as repeated + * executions of the expression are needed. Typically the context will be + * the same as the per-query context of the associated ExprContext. + * + * Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to + * the lists of such nodes held by the parent PlanState (or more accurately, + * the AggrefExprState etc. nodes created for them are added). + * + * Note: there is no ExecEndExpr function; we assume that any resource + * cleanup needed will be handled by just releasing the memory context + * in which the state tree is built. Functions that require additional + * cleanup work can register a shutdown callback in the ExprContext. + * + * 'node' is the root of the expression tree to compile. + * 'parent' is the PlanState node that owns the expression. + * + * 'parent' may be NULL if we are preparing an expression that is not + * associated with a plan tree. (If so, it can't have aggs or subplans.) + * Such cases should usually come through ExecPrepareExpr, not directly here. + * + * Also, if 'node' is NULL, we just return NULL. This is convenient for some + * callers that may or may not have an expression that needs to be compiled. + * Note that a NULL ExprState pointer *cannot* be handed to ExecEvalExpr, + * although ExecQual and ExecCheck will accept one (and treat it as "true"). + */ +ExprState * +ExecInitExprByFlatten(Expr *node, PlanState *parent) +{ + ExprState *state; + ExprEvalStep scratch; + + /* Special case: NULL expression produces a NULL ExprState pointer */ + if (node == NULL) + return NULL; + + /* Initialize ExprState with empty step list */ + state = makeNode(ExprState); + state->expr = node; + state->is_flt_frame = true; + state->parent = parent; + + if (nodeTag(node) != T_TargetEntry) { + state->resultType = exprType((Node*)node); + } + + /* Insert EEOP_*_FETCHSOME steps as needed */ + ExecInitExprSlots(state, (Node *) node); + + /* Compile the expression proper */ + ExecInitExprRec(node, state, &state->resvalue, &state->resnull, NULL); + + /* Finally, append a DONE step */ + scratch.opcode = EEOP_DONE; + ExprEvalPushStep(state, &scratch); + + ExecReadyExpr(state); + + return state; //FIXME +} + +/* + * ExecInitQual: prepare a qual for execution by ExecQual + * + * Prepares for the evaluation of a conjunctive boolean expression (qual list + * with implicit AND semantics) that returns true if none of the + * subexpressions are false. + * + * We must return true if the list is empty. Since that's a very common case, + * we optimize it a bit further by translating to a NULL ExprState pointer + * rather than setting up an ExprState that computes constant TRUE. (Some + * especially hot-spot callers of ExecQual detect this and avoid calling + * ExecQual at all.) + * + * If any of the subexpressions yield NULL, then the result of the conjunction + * is false. This makes ExecQual primarily useful for evaluating WHERE + * clauses, since SQL specifies that tuples with null WHERE results do not + * get selected. + */ +ExprState * +ExecInitQualByFlatten(List *qual, PlanState *parent) +{ + ExprState *state; + ExprEvalStep scratch; + List *adjust_jumps = NIL; + ListCell *lc; + + /* short-circuit (here and in ExecQual) for empty restriction list */ + if (qual == NIL) + return NULL; + + Assert(IsA(qual, List)); + + state = makeNode(ExprState); + state->expr = (Expr *) qual; + state->is_flt_frame = true; + state->parent = parent; + + /* mark expression as to be used with ExecQual() */ + state->flags = (uint8)EEO_FLAG_IS_QUAL; + + /* Insert EEOP_*_FETCHSOME steps as needed */ + ExecInitExprSlots(state, (Node *) qual); + + /* + * ExecQual() needs to return false for an expression returning NULL. That + * allows us to short-circuit the evaluation the first time a NULL is + * encountered. As qual evaluation is a hot-path this warrants using a + * special opcode for qual evaluation that's simpler than BOOL_AND (which + * has more complex NULL handling). + */ + scratch.opcode = (intptr_t)EEOP_QUAL; + + /* + * We can use ExprState's resvalue/resnull as target for each qual expr. + */ + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + + foreach(lc, qual) + { + Expr *node = (Expr *) lfirst(lc); + + /* first evaluate expression */ + ExecInitExprRec(node, state, &state->resvalue, &state->resnull, NULL); + + /* then emit EEOP_QUAL to detect if it's false (or null) */ + scratch.d.qualexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_jumps = lappend_int(adjust_jumps, + state->steps_len - 1); + } + + /* adjust jump targets */ + foreach(lc, adjust_jumps) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + + Assert(as->opcode == EEOP_QUAL); + Assert(as->d.qualexpr.jumpdone == -1); + as->d.qualexpr.jumpdone = state->steps_len; + } + + /* + * At the end, we don't need to do anything more. The last qual expr must + * have yielded TRUE, and since its result is stored in the desired output + * location, we're done. + */ + scratch.opcode = (intptr_t)EEOP_DONE; + ExprEvalPushStep(state, &scratch); + + ExecReadyExpr(state); + + return state; +} + +/* + * ExecInitCheck: prepare a check constraint for execution by ExecCheck + * + * This is much like ExecInitQual/ExecQual, except that a null result from + * the conjunction is treated as TRUE. This behavior is appropriate for + * evaluating CHECK constraints, since SQL specifies that NULL constraint + * conditions are not failures. + * + * Note that like ExecInitQual, this expects input in implicit-AND format. + * Users of ExecCheck that have expressions in normal explicit-AND format + * can just apply ExecInitExpr to produce suitable input for ExecCheck. + */ +ExprState * +ExecInitCheck(List *qual, PlanState *parent) +{ + /* short-circuit (here and in ExecCheck) for empty restriction list */ + if (qual == NIL) + return NULL; + + Assert(IsA(qual, List)); + + /* + * Just convert the implicit-AND list to an explicit AND (if there's more + * than one entry), and compile normally. Unlike ExecQual, we can't + * short-circuit on NULL results, so the regular AND behavior is needed. + */ + return ExecInitExpr(make_ands_explicit(qual), parent); +} + +/* + * ExecBuildProjectionInfo + * + * Build a ProjectionInfo node for evaluating the given tlist in the given + * econtext, and storing the result into the tuple slot. (Caller must have + * ensured that tuple slot has a descriptor matching the tlist!) + * + * inputDesc can be NULL, but if it is not, we check to see whether simple + * Vars in the tlist match the descriptor. It is important to provide + * inputDesc for relation-scan plan nodes, as a cross check that the relation + * hasn't been changed since the plan was made. At higher levels of a plan, + * there is no need to recheck. + * + * This is implemented by internally building an ExprState that performs the + * whole projection in one go. + * + * Caution: before PG v10, the targetList was a list of ExprStates; now it + * should be the planner-created targetlist, since we do the compilation here. + */ +ProjectionInfo * +ExecBuildProjectionInfoByFlatten(List *targetList, + ExprContext *econtext, + TupleTableSlot *slot, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprState *state; + ExprEvalStep scratch = {0}; + ListCell *lc; + + projInfo->pi_exprContext = econtext; + /* We embed ExprState into ProjectionInfo instead of doing extra palloc */ + projInfo->pi_state.type = T_ExprState; + state = &projInfo->pi_state; + state->expr = (Expr *) targetList; + state->is_flt_frame = true; + state->parent = parent; + state->resultslot = slot; + + /* Insert EEOP_*_FETCHSOME steps as needed */ + ExecInitExprSlots(state, (Node *) targetList); + + /* Now compile each tlist column */ + foreach(lc, targetList) + { + TargetEntry *tle = lfirst_node(TargetEntry, lc); + Var *variable = NULL; + AttrNumber attnum = 0; + bool isSafeVar = false; + + /* + * If tlist expression is a safe non-system Var, use the fast-path + * ASSIGN_*_VAR opcodes. "Safe" means that we don't need to apply + * CheckVarSlotCompatibility() during plan startup. If a source slot + * was provided, we make the equivalent tests here; if a slot was not + * provided, we assume that no check is needed because we're dealing + * with a non-relation-scan-level expression. + */ + if (tle->expr != NULL && + IsA(tle->expr, Var) && + ((Var *) tle->expr)->varattno > 0) + { + /* Non-system Var, but how safe is it? */ + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (inputDesc == NULL) + isSafeVar = true; /* can't check, just assume OK */ + else if (attnum <= inputDesc->natts) + { + Form_pg_attribute attr = TupleDescAttr(inputDesc, attnum - 1); + + /* + * If user attribute is dropped or has a type mismatch, don't + * use ASSIGN_*_VAR. Instead let the normal expression + * machinery handle it (which'll possibly error out). + */ + if (!attr->attisdropped && variable->vartype == attr->atttypid) + { + isSafeVar = true; + } + } + } + + if (isSafeVar) + { + /* Fast-path: just generate an EEOP_ASSIGN_*_VAR step */ + switch (variable->varno) + { + case INNER_VAR: + /* get the tuple from the inner node */ + scratch.opcode = EEOP_ASSIGN_INNER_VAR; + break; + + case OUTER_VAR: + /* get the tuple from the outer node */ + scratch.opcode = EEOP_ASSIGN_OUTER_VAR; + break; + + /* INDEX_VAR is handled by default case */ + + default: + /* get the tuple from the relation being scanned */ + scratch.opcode = EEOP_ASSIGN_SCAN_VAR; + break; + } + + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + /* + * Otherwise, compile the column expression normally. + * + * We can't tell the expression to evaluate directly into the + * result slot, as the result slot (and the exprstate for that + * matter) can change between executions. We instead evaluate + * into the ExprState's resvalue/resnull and then move. + */ + ExecInitExprRec(tle->expr, state, + &state->resvalue, &state->resnull, NULL); + + /* + * Column might be referenced multiple times in upper nodes, so + * force value to R/O - but only if it could be an expanded datum. + */ + if (get_typlen(exprType((Node *) tle->expr)) == -1) + scratch.opcode = EEOP_ASSIGN_TMP_MAKE_RO; + else + scratch.opcode = EEOP_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + + scratch.opcode = EEOP_DONE; + ExprEvalPushStep(state, &scratch); + + ExecReadyExpr(state); + + return projInfo; +} + +/* + * ExecPrepareQualByFlatten --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitQual in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ + +ExprState * + ExecPrepareQualByFlatten(List *qual, EState *estate) +{ + ExprState *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQualByFlatten(qual, NULL); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + +/* + * ExecPrepareCheck -- initialize check constraint for execution outside a + * normal Plan tree context. + * + * See ExecPrepareExpr() and ExecInitCheck() for details. + */ +ExprState * +ExecPrepareCheck(List *qual, EState *estate) +{ + ExprState *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitCheck(qual, NULL); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + +/* + * Call ExecPrepareExpr() on each member of a list of Exprs, and return + * a list of ExprStates. + * + * See ExecPrepareExpr() for details. + */ +List * +ExecPrepareExprList(List *nodes, EState *estate) +{ + List *result = NIL; + MemoryContext oldcontext; + ListCell *lc; + + /* Ensure that the list cell nodes are in the right context too */ + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + foreach(lc, nodes) + { + Expr *e = (Expr *) lfirst(lc); + + result = lappend(result, ExecPrepareExpr(e, estate)); + } + + MemoryContextSwitchTo(oldcontext); + + return result; +} + +/* + * ExecCheck - evaluate a check constraint + * + * For check constraints, a null result is taken as TRUE, ie the constraint + * passes. + * + * The check constraint may have been prepared with ExecInitCheck + * (possibly via ExecPrepareCheck) if the caller had it in implicit-AND + * format, but a regular boolean expression prepared with ExecInitExpr or + * ExecPrepareExpr works too. + */ +bool +ExecCheckByFlatten(ExprState *state, ExprContext *econtext) +{ + Datum ret; + bool isnull; + ExprDoneCond isDone = ExprSingleResult; + + /* short-circuit (here and in ExecInitCheck) for empty restriction list */ + if (state == NULL) + return true; + + /* verify that expression was not compiled using ExecInitQual */ + Assert(!(state->flags & EEO_FLAG_IS_QUAL)); + + ret = ExecEvalExprSwitchContext(state, econtext, &isnull, &isDone); + + if (isnull) + return true; + + return DatumGetBool(ret); +} + +/* + * Prepare a compiled expression for execution. This has to be called for + * every ExprState before it can be executed. + * + * NB: While this currently only calls ExecReadyInterpretedExpr(), + * this will likely get extended to further expression evaluation methods. + * Therefore this should be used instead of directly calling + * ExecReadyInterpretedExpr(). + */ +static void +ExecReadyExpr(ExprState *state) +{ + ExecReadyInterpretedExpr(state); +} + +/* + * Append the steps necessary for the evaluation of node to ExprState->steps, + * possibly recursing into sub-expressions of node. + * + * node - expression to evaluate + * state - ExprState to whose ->steps to append the necessary operations + * resv / resnull - where to store the result of the node into + */ +static void +ExecInitExprRec(Expr *node, ExprState *state, + Datum *resv, bool *resnull, Expr *parent_node) +{ + ExprEvalStep scratch; + + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); + + /* Step's output location is always what the caller gave us */ + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + + /* cases should be ordered as they are in enum NodeTag */ + switch (nodeTag(node)) + { + case T_Var: + { + Var *variable = (Var *) node; + + if (variable->varattno == InvalidAttrNumber) + { + /* whole-row Var */ + ExecInitWholeRowVar(&scratch, variable, state); + } + else if (variable->varattno <= 0) + { + /* system column */ + scratch.d.var.attnum = variable->varattno; + scratch.d.var.vartype = variable->vartype; + switch (variable->varno) + { + case INNER_VAR: + scratch.opcode = EEOP_INNER_SYSVAR; + break; + case OUTER_VAR: + scratch.opcode = EEOP_OUTER_SYSVAR; + break; + + /* INDEX_VAR is handled by default case */ + + default: + scratch.opcode = EEOP_SCAN_SYSVAR; + break; + } + } + else + { + /* regular user column */ + scratch.d.var.attnum = variable->varattno - 1; + scratch.d.var.vartype = variable->vartype; + switch (variable->varno) + { + case INNER_VAR: + scratch.opcode = EEOP_INNER_VAR; + break; + case OUTER_VAR: + scratch.opcode = EEOP_OUTER_VAR; + break; + + /* INDEX_VAR is handled by default case */ + + default: + scratch.opcode = EEOP_SCAN_VAR; + break; + } + } + + ExprEvalPushStep(state, &scratch); + break; + } + case T_Const: + { + Const *con = (Const *) node; + + scratch.opcode = EEOP_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + scratch.d.constval.con = (Const *) node; + + if (parent_node && IsA(parent_node, FuncExpr) && + expr_func_has_refcursor_args(((FuncExpr*)parent_node)->funcid)) + scratch.d.constval.is_cursor = true; + else + scratch.d.constval.is_cursor = false; + + ExprEvalPushStep(state, &scratch); + break; + } + case T_UserVar: + case T_SetVariableExpr: + { + scratch.opcode = EEOP_USERVAR_OR_SETVARIABLE; + + Const *con = NULL; + if (IsA((Expr*)node, UserVar)) { + bool found = false; + UserVar *uservar = (UserVar *)node; + GucUserParamsEntry *entry = (GucUserParamsEntry *)hash_search(u_sess->utils_cxt.set_user_params_htab, + uservar->name, HASH_FIND, &found); + + if (found) { + if (entry->isParse) { + con = (Const *)uservar->value; + } else { + Node *node_tmp = coerce_type(NULL, (Node *)entry->value, entry->value->consttype, + ((Const *)uservar->value)->consttype, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); + node_tmp = eval_const_expression_value(NULL, node_tmp, NULL); + if (!IsA(node_tmp, Const)) { + ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), + errmsg("The value of a user_defined variable must be convertible to a constant."))); + } + con = (Const *)node_tmp; + } + } else { + constexpr int32 consttypmod = -1; + constexpr int constlen = -2; + con = makeConst(UNKNOWNOID, consttypmod, InvalidOid, constlen, (Datum)0, true, false); + } + } else { + SetVariableExpr* setvar = (SetVariableExpr*)transformSetVariableExpr((SetVariableExpr*)node); + con = (Const*)setvar->value; + } + + scratch.d.uservar.con = con; + ExprEvalPushStep(state, &scratch); + + break; + } + case T_UserSetElem: + { + UserSetElem* useexpr = (UserSetElem*)node; + // UserSetElemState* usestate = (UserSetElemState*)makeNode(UserSetElemState); + // usestate->use = useexpr; + scratch.d.userset.useexpr = useexpr; + // state = (ExprState*)usestate; + // state->evalfunc = (ExprStateEvalFunc)ExecEvalUserSetElm; + scratch.opcode = EEOP_USERSET_ELEM; + ExprEvalPushStep(state, &scratch); + break; + } + case T_Param: + { + Param *param = (Param *) node; + + switch (param->paramkind) + { + case PARAM_EXEC: + scratch.opcode = EEOP_PARAM_EXEC; + scratch.d.param.paramid = param->paramid; + scratch.d.param.paramtype = param->paramtype; + ExprEvalPushStep(state, &scratch); + break; + case PARAM_EXTERN: + scratch.opcode = EEOP_PARAM_EXTERN; + scratch.d.param.paramid = param->paramid; + scratch.d.param.paramtype = param->paramtype; + + if (parent_node && IsA(parent_node, FuncExpr) && + expr_func_has_refcursor_args(((FuncExpr*)parent_node)->funcid)) + scratch.d.param.is_cursor = true; + else + scratch.d.param.is_cursor = false; + + ExprEvalPushStep(state, &scratch); + break; + default: + elog(ERROR, "unrecognized paramkind: %d", + (int) param->paramkind); + break; + } + break; + } + case T_Aggref: + { + Aggref* aggref = (Aggref*)node; + AggrefExprState* astate = makeNode(AggrefExprState); + scratch.opcode = EEOP_AGGREF; + scratch.d.aggref.astate = astate; + astate->aggref = aggref; + astate->xprstate.expr = node; + + if (state->parent && (IsA(state->parent, AggState) || IsA(state->parent, VecAggState))) { + AggState* aggstate = (AggState*)state->parent; + int naggs; + + aggstate->aggs = lcons(astate, aggstate->aggs); + naggs = ++aggstate->numaggs; + + astate->aggdirectargs = ExecInitExprList(aggref->aggdirectargs, state->parent); + + /* + * Complain if the aggregate's arguments contain any + * aggregates; nested agg functions are semantically + * nonsensical. (This should have been caught earlier, + * but we defend against it here anyway.) + */ + if (naggs != aggstate->numaggs) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("aggregate function calls cannot be nested"))); + } else { + /* planner messed up */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_AGG), errmodule(MOD_OPT), errmsg("Aggref found in non-Agg plan node"))); + } + ExprEvalPushStep(state, &scratch); + break; + } + case T_GroupingFunc: + { + GroupingFunc *grp_node = (GroupingFunc *) node; + Agg *agg; + + if (!state->parent + || (!IsA(state->parent, AggState) && !IsA(state->parent, VecAggState)) + || !IsA(state->parent->plan, Agg) && !IsA(state->parent->plan, VecAgg)) + elog(ERROR, "GroupingFunc found in non-Agg plan node"); + + scratch.opcode = EEOP_GROUPING_FUNC; + + agg = (Agg *) (state->parent->plan); + + if (agg->groupingSets) + scratch.d.grouping_func.clauses = grp_node->cols; + else + scratch.d.grouping_func.clauses = NIL; + + ExprEvalPushStep(state, &scratch); + break; + } + case T_WindowFunc: + { + WindowFunc* wfunc = (WindowFunc*)node; + WindowFuncExprState* wfstate = makeNode(WindowFuncExprState); + wfstate->wfunc = wfunc; + wfstate->xprstate.expr = node; + + if (state->parent && (IsA(state->parent, WindowAggState) || IsA(state->parent, VecWindowAggState))) { + WindowAggState* winstate = (WindowAggState*)state->parent; + int nfuncs; + + winstate->funcs = lappend(winstate->funcs, wfstate); + nfuncs = ++winstate->numfuncs; + if (wfunc->winagg) + winstate->numaggs++; + + wfstate->args = ExecInitExprList(wfunc->args, state->parent); + + /* + * Complain if the windowfunc's arguments contain any + * windowfuncs; nested window functions are semantically + * nonsensical. (This should have been caught earlier, + * but we defend against it here anyway.) + */ + if (nfuncs != winstate->numfuncs) + ereport( + ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("window function calls cannot be nested"))); + } else { + /* planner messed up */ + ereport( + ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("WindowFunc found in non-WindowAgg plan node"))); + } + + scratch.opcode = EEOP_WINDOW_FUNC; + scratch.d.window_func.wfstate = wfstate; + ExprEvalPushStep(state, &scratch); + break; + } + case T_ArrayRef: + { + ArrayRef *aref = (ArrayRef *) node; + + ExecInitArrayRef(&scratch, aref, state, resv, resnull, node); + break; + } + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + ExecInitFunc(&scratch, node, + func->args, func->funcid, func->inputcollid, + state); + ExprEvalPushStep(state, &scratch); + break; + } + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + + ExecInitFunc(&scratch, node, + op->args, op->opfuncid, op->inputcollid, + state); + ExprEvalPushStep(state, &scratch); + break; + } + case T_DistinctExpr: + { + DistinctExpr *op = (DistinctExpr *) node; + + ExecInitFunc(&scratch, node, + op->args, op->opfuncid, op->inputcollid, + state); + + /* + * Change opcode of call instruction to EEOP_DISTINCT. + * + * XXX: historically we've not called the function usage + * pgstat infrastructure - that seems inconsistent given that + * we do so for normal function *and* operator evaluation. If + * we decided to do that here, we'd probably want separate + * opcodes for FUSAGE or not. + */ + scratch.opcode = EEOP_DISTINCT; + ExprEvalPushStep(state, &scratch); + break; + } + case T_NullIfExpr: + { + NullIfExpr *op = (NullIfExpr *) node; + + ExecInitFunc(&scratch, node, + op->args, op->opfuncid, op->inputcollid, + state); + + /* + * Change opcode of call instruction to EEOP_NULLIF. + * + * XXX: historically we've not called the function usage + * pgstat infrastructure - that seems inconsistent given that + * we do so for normal function *and* operator evaluation. If + * we decided to do that here, we'd probably want separate + * opcodes for FUSAGE or not. + */ + scratch.opcode = EEOP_NULLIF; + ExprEvalPushStep(state, &scratch); + break; + } + case T_ScalarArrayOpExpr: + { + ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node; + Expr *scalararg; + Expr *arrayarg; + FmgrInfo *finfo; + FunctionCallInfo fcinfo; + AclResult aclresult; + + Assert(list_length(opexpr->args) == 2); + scalararg = (Expr *) linitial(opexpr->args); + arrayarg = (Expr *) lsecond(opexpr->args); + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(opexpr->opfuncid, + GetUserId(), + ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(opexpr->opfuncid)); + InvokeFunctionExecuteHook(opexpr->opfuncid); + + /* Set up the primary fmgr lookup information */ + finfo = (FmgrInfo*)palloc0(sizeof(FmgrInfo)); + fcinfo = (FunctionCallInfo)palloc0(sizeof(FunctionCallInfoData)); + fmgr_info(opexpr->opfuncid, finfo); + fmgr_info_set_expr((Node *) node, finfo); + InitFunctionCallInfoData(*fcinfo, finfo, 2, + opexpr->inputcollid, NULL, NULL); + + /* Evaluate scalar directly into left function argument */ + ExecInitExprRec(scalararg, state, + &fcinfo->arg[0], &fcinfo->argnull[0], node); + + /* + * Evaluate array argument into our return value. There's no + * danger in that, because the return value is guaranteed to + * be overwritten by EEOP_SCALARARRAYOP, and will not be + * passed to any other expression. + */ + ExecInitExprRec(arrayarg, state, resv, resnull, node); + + /* And perform the operation */ + scratch.opcode = EEOP_SCALARARRAYOP; + scratch.d.scalararrayop.element_type = InvalidOid; + scratch.d.scalararrayop.useOr = opexpr->useOr; + scratch.d.scalararrayop.finfo = finfo; + scratch.d.scalararrayop.fcinfo_data = fcinfo; + scratch.d.scalararrayop.fn_addr = finfo->fn_addr; + ExprEvalPushStep(state, &scratch); + break; + } + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + int nargs = list_length(boolexpr->args); + List *adjust_jumps = NIL; + int off; + ListCell *lc; + + /* allocate scratch memory used by all steps of AND/OR */ + if (boolexpr->boolop != NOT_EXPR) + scratch.d.boolexpr.anynull = (bool *) palloc(sizeof(bool)); + + /* + * For each argument evaluate the argument itself, then + * perform the bool operation's appropriate handling. + * + * We can evaluate each argument into our result area, since + * the short-circuiting logic means we only need to remember + * previous NULL values. + * + * AND/OR is split into separate STEP_FIRST (one) / STEP (zero + * or more) / STEP_LAST (one) steps, as each of those has to + * perform different work. The FIRST/LAST split is valid + * because AND/OR have at least two arguments. + */ + off = 0; + foreach(lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + /* Evaluate argument into our output variable */ + ExecInitExprRec(arg, state, resv, resnull, node); + + /* Perform the appropriate step type */ + switch (boolexpr->boolop) + { + case AND_EXPR: + Assert(nargs >= 2); + + if (off == 0) + scratch.opcode = EEOP_BOOL_AND_STEP_FIRST; + else if (off + 1 == nargs) + scratch.opcode = EEOP_BOOL_AND_STEP_LAST; + else + scratch.opcode = EEOP_BOOL_AND_STEP; + break; + case OR_EXPR: + Assert(nargs >= 2); + + if (off == 0) + scratch.opcode = EEOP_BOOL_OR_STEP_FIRST; + else if (off + 1 == nargs) + scratch.opcode = EEOP_BOOL_OR_STEP_LAST; + else + scratch.opcode = EEOP_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(nargs == 1); + + scratch.opcode = EEOP_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_jumps = lappend_int(adjust_jumps, + state->steps_len - 1); + off++; + } + + /* adjust jump targets */ + foreach(lc, adjust_jumps) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + + break; + } + case T_SubPlan: + { + SubPlan *subplan = (SubPlan *) node; + SubPlanState *sstate; + + if (!state->parent) + elog(ERROR, "SubPlan found with no parent plan"); + + sstate = ExecInitSubPlan(subplan, state->parent); + + /* add SubPlanState nodes to state->parent->subPlan */ + state->parent->subPlan = lappend(state->parent->subPlan, + sstate); + + scratch.opcode = EEOP_SUBPLAN; + scratch.d.subplan.sstate = sstate; + + ExprEvalPushStep(state, &scratch); + break; + } + case T_AlternativeSubPlan: + { + AlternativeSubPlan *asplan = (AlternativeSubPlan *) node; + AlternativeSubPlanState *asstate; + + if (!state->parent) + elog(ERROR, "AlternativeSubPlan found with no parent plan"); + + asstate = ExecInitAlternativeSubPlan(asplan, state->parent); + + scratch.opcode = EEOP_ALTERNATIVE_SUBPLAN; + scratch.d.alternative_subplan.asstate = asstate; + + ExprEvalPushStep(state, &scratch); + break; + } + case T_FieldSelect: + { + FieldSelect *fselect = (FieldSelect *) node; + + /* evaluate row/record argument into result area */ + ExecInitExprRec(fselect->arg, state, resv, resnull, node); + + /* and extract field */ + scratch.opcode = EEOP_FIELDSELECT; + scratch.d.fieldselect.fieldnum = fselect->fieldnum; + scratch.d.fieldselect.resulttype = fselect->resulttype; + scratch.d.fieldselect.argdesc = NULL; + + ExprEvalPushStep(state, &scratch); + break; + } + case T_FieldStore: + { + FieldStore *fstore = (FieldStore *) node; + TupleDesc tupDesc; + TupleDesc *descp; + Datum *values; + bool *nulls; + int ncolumns; + ListCell *l1, + *l2; + + /* find out the number of columns in the composite type */ + tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1); + ncolumns = tupDesc->natts; + DecrTupleDescRefCount(tupDesc); + + /* create workspace for column values */ + values = (Datum *) palloc(sizeof(Datum) * ncolumns); + nulls = (bool *) palloc(sizeof(bool) * ncolumns); + + /* create workspace for runtime tupdesc cache */ + descp = (TupleDesc *) palloc(sizeof(TupleDesc)); + *descp = NULL; + + /* emit code to evaluate the composite input value */ + ExecInitExprRec(fstore->arg, state, resv, resnull, node); + + /* next, deform the input tuple into our workspace */ + scratch.opcode = EEOP_FIELDSTORE_DEFORM; + scratch.d.fieldstore.fstore = fstore; + scratch.d.fieldstore.argdesc = descp; + scratch.d.fieldstore.values = values; + scratch.d.fieldstore.nulls = nulls; + scratch.d.fieldstore.ncolumns = ncolumns; + ExprEvalPushStep(state, &scratch); + + /* evaluate new field values, store in workspace columns */ + forboth(l1, fstore->newvals, l2, fstore->fieldnums) + { + Expr *e = (Expr *) lfirst(l1); + AttrNumber fieldnum = lfirst_int(l2); + Datum *save_innermost_caseval; + bool *save_innermost_casenull; + + if (fieldnum <= 0 || fieldnum > ncolumns) + elog(ERROR, "field number %d is out of range in FieldStore", + fieldnum); + + /* + * Use the CaseTestExpr mechanism to pass down the old + * value of the field being replaced; this is needed in + * case the newval is itself a FieldStore or ArrayRef that + * has to obtain and modify the old value. It's safe to + * reuse the CASE mechanism because there cannot be a CASE + * between here and where the value would be needed, and a + * field assignment can't be within a CASE either. (So + * saving and restoring innermost_caseval is just + * paranoia, but let's do it anyway.) + */ + save_innermost_caseval = state->innermost_caseval; + save_innermost_casenull = state->innermost_casenull; + state->innermost_caseval = &values[fieldnum - 1]; + state->innermost_casenull = &nulls[fieldnum - 1]; + + ExecInitExprRec(e, state, + &values[fieldnum - 1], + &nulls[fieldnum - 1], node); + + state->innermost_caseval = save_innermost_caseval; + state->innermost_casenull = save_innermost_casenull; + } + + /* finally, form result tuple */ + scratch.opcode = EEOP_FIELDSTORE_FORM; + scratch.d.fieldstore.fstore = fstore; + scratch.d.fieldstore.argdesc = descp; + scratch.d.fieldstore.values = values; + scratch.d.fieldstore.nulls = nulls; + scratch.d.fieldstore.ncolumns = ncolumns; + ExprEvalPushStep(state, &scratch); + break; + } + case T_RelabelType: + { + /* relabel doesn't need to do anything at runtime */ + RelabelType *relabel = (RelabelType *) node; + + ExecInitExprRec(relabel->arg, state, resv, resnull, node); + break; + } + case T_CoerceViaIO: + { + CoerceViaIO *iocoerce = (CoerceViaIO *) node; + Oid iofunc; + bool typisvarlena; + Oid typioparam; + FunctionCallInfo fcinfo_in; + + /* evaluate argument into step's result area */ + ExecInitExprRec(iocoerce->arg, state, resv, resnull, node); + + /* + * Prepare both output and input function calls, to be + * evaluated inside a single evaluation step for speed - this + * can be a very common operation. + * + * We don't check permissions here as a type's input/output + * function are assumed to be executable by everyone. + */ + scratch.opcode = EEOP_IOCOERCE; + + /* lookup the source type's output function */ + scratch.d.iocoerce.finfo_out = (FmgrInfo*)palloc0(sizeof(FmgrInfo)); + scratch.d.iocoerce.fcinfo_data_out = (FunctionCallInfo)palloc0(sizeof(FunctionCallInfoData)); + + getTypeOutputInfo(exprType((Node *) iocoerce->arg), + &iofunc, &typisvarlena); + fmgr_info(iofunc, scratch.d.iocoerce.finfo_out); + fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_out); + InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_out, + scratch.d.iocoerce.finfo_out, + 1, InvalidOid, NULL, NULL); + + /* lookup the result type's input function */ + scratch.d.iocoerce.finfo_in = (FmgrInfo*)palloc0(sizeof(FmgrInfo)); + scratch.d.iocoerce.fcinfo_data_in = (FunctionCallInfo)palloc0(sizeof(FunctionCallInfoData)); + + getTypeInputInfo(iocoerce->resulttype, + &iofunc, &typioparam); + fmgr_info(iofunc, scratch.d.iocoerce.finfo_in); + fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in); + InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in, + scratch.d.iocoerce.finfo_in, + 3, InvalidOid, NULL, NULL); + + /* + * We can preload the second and third arguments for the input + * function, since they're constants. + */ + fcinfo_in = scratch.d.iocoerce.fcinfo_data_in; + fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam); + fcinfo_in->argnull[1] = false; + fcinfo_in->arg[2] = Int32GetDatum(-1); + fcinfo_in->argnull[2] = false; + + ExprEvalPushStep(state, &scratch); + break; + } + case T_ArrayCoerceExpr: + { + ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; + Oid resultelemtype; + + /* evaluate argument into step's result area */ + ExecInitExprRec(acoerce->arg, state, resv, resnull, node); + + resultelemtype = get_element_type(acoerce->resulttype); + if (!OidIsValid(resultelemtype)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("target type is not an array"))); + /* Arrays over domains aren't supported yet */ + Assert(getBaseType(resultelemtype) == resultelemtype); + + scratch.opcode = EEOP_ARRAYCOERCE; + scratch.d.arraycoerce.coerceexpr = acoerce; + scratch.d.arraycoerce.resultelemtype = resultelemtype; + + if (OidIsValid(acoerce->elemfuncid)) + { + AclResult aclresult; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(acoerce->elemfuncid, + GetUserId(), + ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(acoerce->elemfuncid)); + InvokeFunctionExecuteHook(acoerce->elemfuncid); + + /* Set up the primary fmgr lookup information */ + scratch.d.arraycoerce.elemfunc = + (FmgrInfo *) palloc0(sizeof(FmgrInfo)); + fmgr_info(acoerce->elemfuncid, + scratch.d.arraycoerce.elemfunc); + fmgr_info_set_expr((Node *) acoerce, + scratch.d.arraycoerce.elemfunc); + + /* Set up workspace for array_map */ + scratch.d.arraycoerce.amstate = + (ArrayMapState *) palloc0(sizeof(ArrayMapState)); + } + else + { + /* Don't need workspace if there's no conversion func */ + scratch.d.arraycoerce.elemfunc = NULL; + scratch.d.arraycoerce.amstate = NULL; + } + + ExprEvalPushStep(state, &scratch); + break; + } + + case T_ConvertRowtypeExpr: + { + ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node; + + /* evaluate argument into step's result area */ + ExecInitExprRec(convert->arg, state, resv, resnull, node); + + /* and push conversion step */ + scratch.opcode = EEOP_CONVERT_ROWTYPE; + scratch.d.convert_rowtype.convert = convert; + scratch.d.convert_rowtype.indesc = NULL; + scratch.d.convert_rowtype.outdesc = NULL; + scratch.d.convert_rowtype.map = NULL; + scratch.d.convert_rowtype.initialized = false; + + ExprEvalPushStep(state, &scratch); + break; + } + + /* note that CaseWhen expressions are handled within this block */ + case T_CaseExpr: + { + CaseExpr *caseExpr = (CaseExpr *) node; + List *adjust_jumps = NIL; + Datum *caseval = NULL; + bool *casenull = NULL; + ListCell *lc; + + /* + * If there's a test expression, we have to evaluate it and + * save the value where the CaseTestExpr placeholders can find + * it. + */ + if (caseExpr->arg != NULL) + { + /* Evaluate testexpr into caseval/casenull workspace */ + caseval = (Datum*)palloc(sizeof(Datum)); + casenull = (bool*)palloc(sizeof(bool)); + + ExecInitExprRec(caseExpr->arg, state, + caseval, casenull, node); + + /* + * Since value might be read multiple times, force to R/O + * - but only if it could be an expanded datum. + */ + if (get_typlen(exprType((Node *) caseExpr->arg)) == -1) + { + /* change caseval in-place */ + scratch.opcode = EEOP_MAKE_READONLY; + scratch.resvalue = caseval; + scratch.resnull = casenull; + scratch.d.make_readonly.value = caseval; + scratch.d.make_readonly.isnull = casenull; + ExprEvalPushStep(state, &scratch); + /* restore normal settings of scratch fields */ + scratch.resvalue = resv; + scratch.resnull = resnull; + } + } + + /* + * Prepare to evaluate each of the WHEN clauses in turn; as + * soon as one is true we return the value of the + * corresponding THEN clause. If none are true then we return + * the value of the ELSE clause, or NULL if there is none. + */ + foreach(lc, caseExpr->args) + { + CaseWhen *when = (CaseWhen *) lfirst(lc); + Datum *save_innermost_caseval; + bool *save_innermost_casenull; + int whenstep; + + /* + * Make testexpr result available to CaseTestExpr nodes + * within the condition. We must save and restore prior + * setting of innermost_caseval fields, in case this node + * is itself within a larger CASE. + * + * If there's no test expression, we don't actually need + * to save and restore these fields; but it's less code to + * just do so unconditionally. + */ + save_innermost_caseval = state->innermost_caseval; + save_innermost_casenull = state->innermost_casenull; + state->innermost_caseval = caseval; + state->innermost_casenull = casenull; + + /* evaluate condition into CASE's result variables */ + ExecInitExprRec(when->expr, state, resv, resnull, node); + + state->innermost_caseval = save_innermost_caseval; + state->innermost_casenull = save_innermost_casenull; + + /* If WHEN result isn't true, jump to next CASE arm */ + scratch.opcode = EEOP_JUMP_IF_NOT_TRUE; + scratch.d.jump.jumpdone = -1; /* computed later */ + ExprEvalPushStep(state, &scratch); + whenstep = state->steps_len - 1; + + /* + * If WHEN result is true, evaluate THEN result, storing + * it into the CASE's result variables. + */ + ExecInitExprRec(when->result, state, resv, resnull, node); + + /* Emit JUMP step to jump to end of CASE's code */ + scratch.opcode = EEOP_JUMP; + scratch.d.jump.jumpdone = -1; /* computed later */ + ExprEvalPushStep(state, &scratch); + + /* + * Don't know address for that jump yet, compute once the + * whole CASE expression is built. + */ + adjust_jumps = lappend_int(adjust_jumps, + state->steps_len - 1); + + /* + * But we can set WHEN test's jump target now, to make it + * jump to the next WHEN subexpression or the ELSE. + */ + state->steps[whenstep].d.jump.jumpdone = state->steps_len; + } + + if (caseExpr->defresult) + { + /* evaluate ELSE expr into CASE's result variables */ + ExecInitExprRec(caseExpr->defresult, state, + resv, resnull, node); + } + else + { + /* default ELSE is to return NULL */ + scratch.opcode = EEOP_CONST; + scratch.d.constval.value = (Datum) 0; + scratch.d.constval.isnull = true; + scratch.d.constval.con = NULL; + + ExprEvalPushStep(state, &scratch); + } + + /* adjust jump targets */ + foreach(lc, adjust_jumps) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + + Assert(as->opcode == EEOP_JUMP); + Assert(as->d.jump.jumpdone == -1); + as->d.jump.jumpdone = state->steps_len; + } + + break; + } + case T_CaseTestExpr: + { + /* + * Read from location identified by innermost_caseval. Note + * that innermost_caseval could be NULL, if this node isn't + * actually within a CaseExpr, ArrayCoerceExpr, etc structure. + * That can happen because some parts of the system abuse + * CaseTestExpr to cause a read of a value externally supplied + * in econtext->caseValue_datum. We'll take care of that + * scenario at runtime. + */ + scratch.opcode = EEOP_CASE_TESTVAL; + scratch.d.casetest.value = state->innermost_caseval; + scratch.d.casetest.isnull = state->innermost_casenull; + + ExprEvalPushStep(state, &scratch); + break; + } + case T_ArrayExpr: + { + ArrayExpr *arrayexpr = (ArrayExpr *) node; + int nelems = list_length(arrayexpr->elements); + ListCell *lc; + int elemoff; + + /* + * Evaluate by computing each element, and then forming the + * array. Elements are computed into scratch arrays + * associated with the ARRAYEXPR step. + */ + scratch.opcode = EEOP_ARRAYEXPR; + scratch.d.arrayexpr.elemvalues = + (Datum *) palloc(sizeof(Datum) * nelems); + scratch.d.arrayexpr.elemnulls = + (bool *) palloc(sizeof(bool) * nelems); + scratch.d.arrayexpr.nelems = nelems; + + /* fill remaining fields of step */ + scratch.d.arrayexpr.multidims = arrayexpr->multidims; + scratch.d.arrayexpr.elemtype = arrayexpr->element_typeid; + + /* do one-time catalog lookup for type info */ + get_typlenbyvalalign(arrayexpr->element_typeid, + &scratch.d.arrayexpr.elemlength, + &scratch.d.arrayexpr.elembyval, + &scratch.d.arrayexpr.elemalign); + + /* prepare to evaluate all arguments */ + elemoff = 0; + foreach(lc, arrayexpr->elements) + { + Expr *e = (Expr *) lfirst(lc); + + ExecInitExprRec(e, state, + &scratch.d.arrayexpr.elemvalues[elemoff], + &scratch.d.arrayexpr.elemnulls[elemoff], node); + elemoff++; + } + + /* and then collect all into an array */ + ExprEvalPushStep(state, &scratch); + break; + } + case T_RowExpr: + { + RowExpr *rowexpr = (RowExpr *) node; + int nelems = list_length(rowexpr->args); + TupleDesc tupdesc; + FormData_pg_attribute* attrs = NULL; + int i; + ListCell *l; + + /* Build tupdesc to describe result tuples */ + if (rowexpr->row_typeid == RECORDOID) + { + /* generic record, use types of given expressions */ + tupdesc = ExecTypeFromExprList(rowexpr->args, rowexpr->colnames); + BlessTupleDesc(tupdesc); + } + else + { + /* it's been cast to a named type, use that */ + tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1); + } + + /* + * In the named-type case, the tupdesc could have more columns + * than are in the args list, since the type might have had + * columns added since the ROW() was parsed. We want those + * extra columns to go to nulls, so we make sure that the + * workspace arrays are large enough and then initialize any + * extra columns to read as NULLs. + */ + Assert(nelems <= tupdesc->natts); + nelems = Max(nelems, tupdesc->natts); + + /* + * Evaluate by first building datums for each field, and then + * a final step forming the composite datum. + */ + scratch.opcode = EEOP_ROW; + scratch.d.row.tupdesc = tupdesc; + + /* space for the individual field datums */ + scratch.d.row.elemvalues = + (Datum *) palloc(sizeof(Datum) * nelems); + scratch.d.row.elemnulls = + (bool *) palloc(sizeof(bool) * nelems); + /* as explained above, make sure any extra columns are null */ + memset(scratch.d.row.elemnulls, true, sizeof(bool) * nelems); + + /* Set up evaluation, skipping any deleted columns */ + if (tupdesc->attrs){ + attrs = tupdesc->attrs; + } + i = 0; + foreach(l, rowexpr->args) + { + Expr *e = (Expr *) lfirst(l); + + if (!attrs[i].attisdropped) + { + /* + * Guard against ALTER COLUMN TYPE on rowtype since + * the RowExpr was created. XXX should we check + * typmod too? Not sure we can be sure it'll be the + * same. + */ + if (exprType((Node *) e) != attrs[i].atttypid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("ROW() column has type %s instead of type %s", + format_type_be(exprType((Node *) e)), + format_type_be(attrs[i].atttypid)))); + } + else + { + /* + * Ignore original expression and insert a NULL. We + * don't really care what type of NULL it is, so + * always make an int4 NULL. + */ + e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid); + } + + /* Evaluate column expr into appropriate workspace slot */ + ExecInitExprRec(e, state, + &scratch.d.row.elemvalues[i], + &scratch.d.row.elemnulls[i], node); + i++; + } + + /* And finally build the row value */ + ExprEvalPushStep(state, &scratch); + break; + } + case T_RowCompareExpr: + { + RowCompareExpr *rcexpr = (RowCompareExpr *) node; + int nopers = list_length(rcexpr->opnos); + List *adjust_jumps = NIL; + ListCell *l_left_expr, + *l_right_expr, + *l_opno, + *l_opfamily, + *l_inputcollid; + ListCell *lc; + int off; + + /* + * Iterate over each field, prepare comparisons. To handle + * NULL results, prepare jumps to after the expression. If a + * comparison yields a != 0 result, jump to the final step. + */ + Assert(list_length(rcexpr->largs) == nopers); + Assert(list_length(rcexpr->rargs) == nopers); + Assert(list_length(rcexpr->opfamilies) == nopers); + Assert(list_length(rcexpr->inputcollids) == nopers); + + off = 0; + for (off = 0, + l_left_expr = list_head(rcexpr->largs), + l_right_expr = list_head(rcexpr->rargs), + l_opno = list_head(rcexpr->opnos), + l_opfamily = list_head(rcexpr->opfamilies), + l_inputcollid = list_head(rcexpr->inputcollids); + off < nopers; + off++, + l_left_expr = lnext(l_left_expr), + l_right_expr = lnext(l_right_expr), + l_opno = lnext(l_opno), + l_opfamily = lnext(l_opfamily), + l_inputcollid = lnext(l_inputcollid)) + { + Expr *left_expr = (Expr *) lfirst(l_left_expr); + Expr *right_expr = (Expr *) lfirst(l_right_expr); + Oid opno = lfirst_oid(l_opno); + Oid opfamily = lfirst_oid(l_opfamily); + Oid inputcollid = lfirst_oid(l_inputcollid); + int strategy; + Oid lefttype; + Oid righttype; + Oid proc; + FmgrInfo *finfo; + FunctionCallInfo fcinfo; + + get_op_opfamily_properties(opno, opfamily, false, + &strategy, + &lefttype, + &righttype); + proc = get_opfamily_proc(opfamily, + lefttype, + righttype, + BTORDER_PROC); + + /* Set up the primary fmgr lookup information */ + finfo = (FmgrInfo *)palloc0(sizeof(FmgrInfo)); + fcinfo = (FunctionCallInfo)palloc0(sizeof(FunctionCallInfoData)); + fmgr_info(proc, finfo); + fmgr_info_set_expr((Node *) node, finfo); + InitFunctionCallInfoData(*fcinfo, finfo, 2, + inputcollid, NULL, NULL); + + /* + * If we enforced permissions checks on index support + * functions, we'd need to make a check here. But the + * index support machinery doesn't do that, and thus + * neither does this code. + */ + + /* evaluate left and right args directly into fcinfo */ + ExecInitExprRec(left_expr, state, + &fcinfo->arg[0], &fcinfo->argnull[0], node); + ExecInitExprRec(right_expr, state, + &fcinfo->arg[1], &fcinfo->argnull[1], node); + + scratch.opcode = EEOP_ROWCOMPARE_STEP; + scratch.d.rowcompare_step.finfo = finfo; + scratch.d.rowcompare_step.fcinfo_data = fcinfo; + scratch.d.rowcompare_step.fn_addr = finfo->fn_addr; + /* jump targets filled below */ + scratch.d.rowcompare_step.jumpnull = -1; + scratch.d.rowcompare_step.jumpdone = -1; + + ExprEvalPushStep(state, &scratch); + adjust_jumps = lappend_int(adjust_jumps, + state->steps_len - 1); + } + + /* + * We could have a zero-column rowtype, in which case the rows + * necessarily compare equal. + */ + if (nopers == 0) + { + scratch.opcode = EEOP_CONST; + scratch.d.constval.value = Int32GetDatum(0); + scratch.d.constval.isnull = false; + scratch.d.constval.con = NULL; + + ExprEvalPushStep(state, &scratch); + } + + /* Finally, examine the last comparison result */ + scratch.opcode = EEOP_ROWCOMPARE_FINAL; + scratch.d.rowcompare_final.rctype = rcexpr->rctype; + ExprEvalPushStep(state, &scratch); + + /* adjust jump targetss */ + foreach(lc, adjust_jumps) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + + Assert(as->opcode == EEOP_ROWCOMPARE_STEP); + Assert(as->d.rowcompare_step.jumpdone == -1); + Assert(as->d.rowcompare_step.jumpnull == -1); + + /* jump to comparison evaluation */ + as->d.rowcompare_step.jumpdone = state->steps_len - 1; + /* jump to the following expression */ + as->d.rowcompare_step.jumpnull = state->steps_len; + } + + break; + } + case T_CoalesceExpr: + { + CoalesceExpr *coalesce = (CoalesceExpr *) node; + List *adjust_jumps = NIL; + ListCell *lc; + + /* We assume there's at least one arg */ + Assert(coalesce->args != NIL); + + /* + * Prepare evaluation of all coalesced arguments, after each + * one push a step that short-circuits if not null. + */ + foreach(lc, coalesce->args) + { + Expr *e = (Expr *) lfirst(lc); + + /* evaluate argument, directly into result datum */ + ExecInitExprRec(e, state, resv, resnull, node); + + /* if it's not null, skip to end of COALESCE expr */ + scratch.opcode = EEOP_JUMP_IF_NOT_NULL; + scratch.d.jump.jumpdone = -1; /* adjust later */ + ExprEvalPushStep(state, &scratch); + + adjust_jumps = lappend_int(adjust_jumps, + state->steps_len - 1); + } + + /* + * No need to add a constant NULL return - we only can get to + * the end of the expression if a NULL already is being + * returned. + */ + + /* adjust jump targets */ + foreach(lc, adjust_jumps) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + + Assert(as->opcode == EEOP_JUMP_IF_NOT_NULL); + Assert(as->d.jump.jumpdone == -1); + as->d.jump.jumpdone = state->steps_len; + } + + break; + } + case T_MinMaxExpr: + { + MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; + int nelems = list_length(minmaxexpr->args); + TypeCacheEntry *typentry; + FmgrInfo *finfo; + FunctionCallInfo fcinfo; + ListCell *lc; + int off; + + /* Look up the btree comparison function for the datatype */ + typentry = lookup_type_cache(minmaxexpr->minmaxtype, + TYPECACHE_CMP_PROC); + if (!OidIsValid(typentry->cmp_proc)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify a comparison function for type %s", + format_type_be(minmaxexpr->minmaxtype)))); + + /* + * If we enforced permissions checks on index support + * functions, we'd need to make a check here. But the index + * support machinery doesn't do that, and thus neither does + * this code. + */ + + /* Perform function lookup */ + finfo = (FmgrInfo *)palloc0(sizeof(FmgrInfo)); + fcinfo = (FunctionCallInfo)palloc0(sizeof(FunctionCallInfoData)); + fmgr_info(typentry->cmp_proc, finfo); + fmgr_info_set_expr((Node *) node, finfo); + InitFunctionCallInfoData(*fcinfo, finfo, 2, + minmaxexpr->inputcollid, NULL, NULL); + + scratch.opcode = EEOP_MINMAX; + /* allocate space to store arguments */ + scratch.d.minmax.values = + (Datum *) palloc(sizeof(Datum) * nelems); + scratch.d.minmax.nulls = + (bool *) palloc(sizeof(bool) * nelems); + scratch.d.minmax.nelems = nelems; + + scratch.d.minmax.op = minmaxexpr->op; + scratch.d.minmax.finfo = finfo; + scratch.d.minmax.fcinfo_data = fcinfo; + + /* evaluate expressions into minmax->values/nulls */ + off = 0; + foreach(lc, minmaxexpr->args) + { + Expr *e = (Expr *) lfirst(lc); + + ExecInitExprRec(e, state, + &scratch.d.minmax.values[off], + &scratch.d.minmax.nulls[off], node); + off++; + } + + /* and push the final comparison */ + ExprEvalPushStep(state, &scratch); + break; + } + case T_XmlExpr: + { + XmlExpr *xexpr = (XmlExpr *) node; + int nnamed = list_length(xexpr->named_args); + int nargs = list_length(xexpr->args); + int off; + ListCell *arg; + + scratch.opcode = EEOP_XMLEXPR; + scratch.d.xmlexpr.xexpr = xexpr; + + /* allocate space for storing all the arguments */ + if (nnamed) + { + scratch.d.xmlexpr.named_argvalue = + (Datum *) palloc(sizeof(Datum) * nnamed); + scratch.d.xmlexpr.named_argnull = + (bool *) palloc(sizeof(bool) * nnamed); + } + else + { + scratch.d.xmlexpr.named_argvalue = NULL; + scratch.d.xmlexpr.named_argnull = NULL; + } + + if (nargs) + { + scratch.d.xmlexpr.argvalue = + (Datum *) palloc(sizeof(Datum) * nargs); + scratch.d.xmlexpr.argnull = + (bool *) palloc(sizeof(bool) * nargs); + } + else + { + scratch.d.xmlexpr.argvalue = NULL; + scratch.d.xmlexpr.argnull = NULL; + } + + /* prepare argument execution */ + off = 0; + foreach(arg, xexpr->named_args) + { + Expr *e = (Expr *) lfirst(arg); + + ExecInitExprRec(e, state, + &scratch.d.xmlexpr.named_argvalue[off], + &scratch.d.xmlexpr.named_argnull[off], node); + off++; + } + + off = 0; + foreach(arg, xexpr->args) + { + Expr *e = (Expr *) lfirst(arg); + + ExecInitExprRec(e, state, + &scratch.d.xmlexpr.argvalue[off], + &scratch.d.xmlexpr.argnull[off], node); + off++; + } + + /* and evaluate the actual XML expression */ + ExprEvalPushStep(state, &scratch); + break; + } + case T_NullTest: + { + NullTest *ntest = (NullTest *) node; + + if (ntest->nulltesttype == IS_NULL) + { + if (ntest->argisrow) + scratch.opcode = EEOP_NULLTEST_ROWISNULL; + else + scratch.opcode = EEOP_NULLTEST_ISNULL; + } + else if (ntest->nulltesttype == IS_NOT_NULL) + { + if (ntest->argisrow) + scratch.opcode = EEOP_NULLTEST_ROWISNOTNULL; + else + scratch.opcode = EEOP_NULLTEST_ISNOTNULL; + } + else + { + elog(ERROR, "unrecognized nulltesttype: %d", + (int) ntest->nulltesttype); + } + /* initialize cache in case it's a row test */ + scratch.d.nulltest_row.argdesc = NULL; + + /* first evaluate argument into result variable */ + ExecInitExprRec(ntest->arg, state, + resv, resnull, node); + + /* then push the test of that argument */ + ExprEvalPushStep(state, &scratch); + break; + } + case T_BooleanTest: + { + BooleanTest *btest = (BooleanTest *) node; + + /* + * Evaluate argument, directly into result datum. That's ok, + * because resv/resnull is definitely not used anywhere else, + * and will get overwritten by the below EEOP_BOOLTEST_IS_* + * step. + */ + ExecInitExprRec(btest->arg, state, resv, resnull, node); + + switch (btest->booltesttype) + { + case IS_TRUE: + scratch.opcode = EEOP_BOOLTEST_IS_TRUE; + break; + case IS_NOT_TRUE: + scratch.opcode = EEOP_BOOLTEST_IS_NOT_TRUE; + break; + case IS_FALSE: + scratch.opcode = EEOP_BOOLTEST_IS_FALSE; + break; + case IS_NOT_FALSE: + scratch.opcode = EEOP_BOOLTEST_IS_NOT_FALSE; + break; + case IS_UNKNOWN: + /* Same as scalar IS NULL test */ + scratch.opcode = EEOP_NULLTEST_ISNULL; + break; + case IS_NOT_UNKNOWN: + /* Same as scalar IS NOT NULL test */ + scratch.opcode = EEOP_NULLTEST_ISNOTNULL; + break; + default: + elog(ERROR, "unrecognized booltesttype: %d", + (int) btest->booltesttype); + } + + ExprEvalPushStep(state, &scratch); + break; + } + case T_CoerceToDomain: + { + CoerceToDomain *ctest = (CoerceToDomain *) node; + + ExecInitCoerceToDomain(&scratch, ctest, state, + resv, resnull, node); + break; + } + case T_CoerceToDomainValue: + { + /* + * Read from location identified by innermost_domainval. Note + * that innermost_domainval could be NULL, if we're compiling + * a standalone domain check rather than one embedded in a + * larger expression. In that case we must read from + * econtext->domainValue_datum. We'll take care of that + * scenario at runtime. + */ + scratch.opcode = EEOP_DOMAIN_TESTVAL; + /* we share instruction union variant with case testval */ + scratch.d.casetest.value = state->innermost_domainval; + scratch.d.casetest.isnull = state->innermost_domainnull; + + ExprEvalPushStep(state, &scratch); + break; + } + case T_CurrentOfExpr: + { + scratch.opcode = EEOP_CURRENTOFEXPR; + ExprEvalPushStep(state, &scratch); + break; + } + case T_Rownum: + { + scratch.d.rownum.RownumState = state->parent; + scratch.d.rownum.typeCompat = (u_sess->utils_cxt.behavior_compat_flags & OPT_ROWNUM_TYPE_COMPAT) > 0; + scratch.opcode = EEOP_ROWNUM; + ExprEvalPushStep(state, &scratch); + break; + } + case T_PrefixKey: + { + PrefixKey *pkey = (PrefixKey*)node; + scratch.d.prefix_key.pkey = pkey; + Oid argtype = exprType((Node*)pkey->arg); + if (argtype == BYTEAOID || argtype == RAWOID || argtype == BLOBOID) { + scratch.opcode = EEOP_PREFIX_BTYEA; + } else { + scratch.opcode = EEOP_PREFIX_TEXT; + } + ExecInitExprRec(pkey->arg, state, resv, resnull, node); + ExprEvalPushStep(state, &scratch); + break; + } + case T_GroupingId: + { + scratch.d.grouping_id.GroupingIdState = (AggState*)state->parent; + scratch.opcode = EEOP_GROUPING_ID; + ExprEvalPushStep(state, &scratch); + break; + } + case T_HashFilter: + { + HashFilter* htest = (HashFilter*)node; + int nargs = list_length(htest->arg); + List* outlist = NIL; + ListCell* l = NULL; + int idx = 0; + + scratch.opcode = EEOP_HASH_FILTER; + + if (nargs) { + scratch.d.hash_filter.argvalue = + (Datum *) palloc(sizeof(Datum) * nargs); + scratch.d.hash_filter.argnull = + (bool *) palloc(sizeof(bool) * nargs); + } + else { + scratch.d.hash_filter.argvalue = NULL; + scratch.d.hash_filter.argnull = NULL; + } + + foreach (l, htest->arg) { + Expr *e = (Expr *) lfirst(l); + + ExecInitExprRec(e, state, + &scratch.d.hash_filter.argvalue[idx], + &scratch.d.hash_filter.argnull[idx], node); + idx++; + } + + scratch.d.hash_filter.arg = outlist; + scratch.d.hash_filter.bucketMap = get_bucketmap_by_execnode(state->parent->plan->exec_nodes, + state->parent->state->es_plannedstmt, + &scratch.d.hash_filter.bucketCnt); + scratch.d.hash_filter.nodelist = (uint2*)palloc(list_length(htest->nodeList) * sizeof(uint2)); + idx = 0; + foreach (l, htest->nodeList) + scratch.d.hash_filter.nodelist[idx++] = lfirst_int(l); + + scratch.d.hash_filter.typeOids = htest->typeOids; + ExprEvalPushStep(state, &scratch); + break; + } + default: + elog(ERROR, "unrecognized node type: %d, line=%d, func:%s", + (int) nodeTag(node), __LINE__, __func__); + break; + } +} + +/* + * Add another expression evaluation step to ExprState->steps. + * + * Note that this potentially re-allocates es->steps, therefore no pointer + * into that array may be used while the expression is still being built. + */ +void +ExprEvalPushStep(ExprState *es, const ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = (ExprEvalStep*)palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = (ExprEvalStep*)repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +/* + * Perform setup necessary for the evaluation of a function-like expression, + * appending argument evaluation steps to the steps list in *state, and + * setting up *scratch so it is ready to be pushed. + * + * *scratch is not pushed here, so that callers may override the opcode, + * which is useful for function-like cases like DISTINCT. + */ +static void +ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, + Oid inputcollid, ExprState *state) +{ + int nargs = list_length(args); + AclResult aclresult; + FmgrInfo *flinfo; + FunctionCallInfo fcinfo; + int argno; + ListCell *lc; + int i; + + FunctionScanState *fssnode = NULL; + bool savedIsSTP = u_sess->SPI_cxt.is_stp; + bool supportTranaction = false; + bool needResetErrMsg = (u_sess->SPI_cxt.forbidden_commit_rollback_err_msg[0] == '\0'); + scratch->d.func.needResetErrMsg = needResetErrMsg; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + +#ifdef ENABLE_MULTIPLE_NODES + if (IS_PGXC_COORDINATOR && (t_thrd.proc->workingVersionNum >= STP_SUPPORT_COMMIT_ROLLBACK)) { + supportTranaction = true; + } +#else + supportTranaction = true; +#endif + + /* Only allow commit at CN, therefore only need to set atomic and + * relevant check at CN level. + */ + if (supportTranaction && IsA(node, FuncExpr)) { + fssnode = makeNode(FunctionScanState); + if (!u_sess->SPI_cxt.is_allow_commit_rollback) { + fssnode->atomic = true; + } + else if (IsAfterTriggerBegin()) { + fssnode->atomic = true; + stp_set_commit_rollback_err_msg(STP_XACT_AFTER_TRIGGER_BEGIN); + } + /* + * If proconfig is set we can't allow transaction commands because of the + * way the GUC stacking works: The transaction boundary would have to pop + * the proconfig setting off the stack. That restriction could be lifted + * by redesigning the GUC nesting mechanism a bit. + */ + HeapTuple tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); + if (!HeapTupleIsValid(tp)) { + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("cache lookup failed for function %u", funcid))); + } + if (!heap_attisnull(tp, Anum_pg_proc_proconfig, NULL) || u_sess->SPI_cxt.is_proconfig_set) { + u_sess->SPI_cxt.is_proconfig_set = true; + fssnode->atomic = true; + stp_set_commit_rollback_err_msg(STP_XACT_GUC_IN_OPT_CLAUSE); + } + /* immutable or stable function should not support commit/rollback */ + bool isNullVolatile = false; + Datum provolatile = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_provolatile, &isNullVolatile); + if (!isNullVolatile && CharGetDatum(provolatile) != PROVOLATILE_VOLATILE) { + fssnode->atomic = true; + stp_set_commit_rollback_err_msg(STP_XACT_IMMUTABLE); + } + + /* if proIsProcedure is ture means it was a stored procedure */ + u_sess->SPI_cxt.is_stp = savedIsSTP; + ReleaseSysCache(tp); + } + + /* Allocate function lookup data and parameter workspace for this call */ + scratch->d.func.finfo = (FmgrInfo*)palloc0(sizeof(FmgrInfo)); + scratch->d.func.fcinfo_data =(FunctionCallInfo) palloc0(sizeof(FunctionCallInfoData)); + scratch->d.func.flag = 0; + flinfo = scratch->d.func.finfo; + fcinfo = scratch->d.func.fcinfo_data; + + /* Set up the primary fmgr lookup information */ + fmgr_info(funcid, flinfo); + fmgr_info_set_expr((Node *) node, flinfo); + + /* Initialize function call parameter structure too */ + InitFunctionCallInfoData(*fcinfo, flinfo, + nargs, inputcollid, NULL, NULL); + + /* Keep extra copies of this info to save an indirection at runtime */ + scratch->d.func.fn_addr = flinfo->fn_addr; + scratch->d.func.nargs = nargs; + scratch->d.func.args = args; + + /* We only support non-set functions here */ + if (flinfo->fn_retset) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + + if (func_has_refcursor_args(funcid, fcinfo)) + scratch->d.func.flag |= FUNC_EXPR_FLAG_HAS_REFCURSOR; + + if (fcinfo->refcursor_data.return_number) + scratch->d.func.flag |= FUNC_EXPR_FLAG_HAS_CURSOR_RETURN; + + if (supportTranaction) { + fcinfo->context = (Node *)fssnode; + } + + if (scratch->d.func.flag & FUNC_EXPR_FLAG_HAS_CURSOR_RETURN) { + /* init returnCursor to store out-args cursor info on ExprContext*/ + fcinfo->refcursor_data.returnCursor = + (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number); + } else { + fcinfo->refcursor_data.returnCursor = NULL; + } + + scratch->d.func.var_dno = NULL; + if (scratch->d.func.flag & FUNC_EXPR_FLAG_HAS_REFCURSOR) { + /* init argCursor to store in-args cursor info on ExprContext */ + fcinfo->refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->nargs); + scratch->d.func.var_dno = (int*)palloc0(sizeof(int) * fcinfo->nargs); + for (i = 0; i < fcinfo->nargs; i++) { + scratch->d.func.var_dno[i] = -1; + } + } + + /* Build code to evaluate arguments directly into the fcinfo struct */ + argno = 0; + foreach(lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + ExecInitExprRec(arg, state, &fcinfo->arg[argno], &fcinfo->argnull[argno], node); + fcinfo->argTypes[argno] = exprType((Node*)arg); + argno++; + } + + scratch->opcode = EEOP_FUNCEXPR; + + /* Insert appropriate opcode depending on strictness and stats level */ + if (u_sess->attr.attr_common.pgstat_track_functions <= flinfo->fn_stats) + { + if (flinfo->fn_strict && nargs > 0) + scratch->d.func.flag |= FUNC_EXPR_FLAG_STRICT; + } + else + { + if (flinfo->fn_strict && nargs > 0) + scratch->d.func.flag |= (FUNC_EXPR_FLAG_STRICT | FUNC_EXPR_FLAG_FUSAGE); + else + scratch->d.func.flag |= FUNC_EXPR_FLAG_FUSAGE; + } + + scratch->d.func.is_plpgsql_func_with_outparam = is_function_with_plpgsql_language_and_outparam(funcid); +} + +/* + * Add expression steps deforming the ExprState's inner/outer/scan slots + * as much as required by the expression. + */ +static void +ExecInitExprSlots(ExprState *state, Node *node) +{ + LastAttnumInfo info = {0, 0, 0}; + + /* + * Figure out which attributes we're going to need. + */ + get_last_attnums_walker(node, &info); + + ExecPushExprSlots(state, &info); +} + +/* + * Add steps deforming the ExprState's inner/out/scan slots as much as + * indicated by info. This is useful when building an ExprState covering more + * than one expression. + */ +static void +ExecPushExprSlots(ExprState *state, LastAttnumInfo *info) +{ + ExprEvalStep scratch; + + /* Emit steps as needed */ + if (info->last_inner > 0) + { + scratch.opcode = ExprEvalOp::EEOP_INNER_FETCHSOME; + scratch.d.fetch.last_var = info->last_inner; + ExprEvalPushStep(state, &scratch); + } + if (info->last_outer > 0) + { + scratch.opcode = EEOP_OUTER_FETCHSOME; + scratch.d.fetch.last_var = info->last_outer; + ExprEvalPushStep(state, &scratch); + } + if (info->last_scan > 0) + { + scratch.opcode = EEOP_SCAN_FETCHSOME; + scratch.d.fetch.last_var = info->last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +/* + * get_last_attnums_walker: expression walker for ExecInitExprSlots + */ +static bool +get_last_attnums_walker(Node *node, LastAttnumInfo *info) +{ +if (node == NULL) + return false; + if (IsA(node, Var)) + { + Var *variable = (Var *) node; + AttrNumber attnum = variable->varattno; + + switch (variable->varno) + { + case INNER_VAR: + info->last_inner = Max(info->last_inner, attnum); + break; + + case OUTER_VAR: + info->last_outer = Max(info->last_outer, attnum); + break; + + /* INDEX_VAR is handled by default case */ + + default: + info->last_scan = Max(info->last_scan, attnum); + break; + } + return false; + } + + /* + * Don't examine the arguments or filters of Aggrefs or WindowFuncs, + * because those do not represent expressions to be evaluated within the + * calling expression's econtext. GroupingFunc arguments are never + * evaluated at all. + */ + if (IsA(node, Aggref)) + return false; + if (IsA(node, WindowFunc)) + return false; + if (IsA(node, GroupingFunc)) + return false; + return expression_tree_walker(node, (bool (*)())get_last_attnums_walker, + (void *) info); +} + +/* + * Prepare step for the evaluation of a whole-row variable. + * The caller still has to push the step. + */ +static void +ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, ExprState *state) +{ + /* fill in all but the target */ + scratch->opcode = EEOP_WHOLEROW; + scratch->d.wholerow.var = variable; + scratch->d.wholerow.first = true; + scratch->d.wholerow.slow = false; + scratch->d.wholerow.tupdesc = NULL; /* filled at runtime */ + scratch->d.wholerow.junkFilter = NULL; + + /* + * If the input tuple came from a subquery, it might contain "resjunk" + * columns (such as GROUP BY or ORDER BY columns), which we don't want to + * keep in the whole-row result. We can get rid of such columns by + * passing the tuple through a JunkFilter --- but to make one, we have to + * lay our hands on the subquery's targetlist. Fortunately, there are not + * very many cases where this can happen, and we can identify all of them + * by examining our parent PlanState. We assume this is not an issue in + * standalone expressions that don't have parent plans. (Whole-row Vars + * can occur in such expressions, but they will always be referencing + * table rows.) + */ + if (state->parent) + { + PlanState *subplan = NULL; + + switch (nodeTag(state->parent)) + { + case T_SubqueryScanState: + subplan = ((SubqueryScanState *) state->parent)->subplan; + break; + case T_CteScanState: + subplan = ((CteScanState *) state->parent)->cteplanstate; + break; + default: + break; + } + + if (subplan) + { + bool junk_filter_needed = false; + ListCell *tlist; + + /* Detect whether subplan tlist actually has any junk columns */ + foreach(tlist, subplan->plan->targetlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(tlist); + + if (tle->resjunk) + { + junk_filter_needed = true; + break; + } + } + + /* If so, build the junkfilter now */ + if (junk_filter_needed) + { + scratch->d.wholerow.junkFilter = + ExecInitJunkFilter(subplan->plan->targetlist, + ExecGetResultType(subplan)->tdhasoid, + ExecInitExtraTupleSlot(state->parent->state)); + } + } + } +} + +/* + * Prepare evaluation of an ArrayRef expression. + */ +static void +ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, + ExprState *state, Datum *resv, bool *resnull, Expr *node) +{ + bool isAssignment = (aref->refassgnexpr != NULL); + ArrayRefState *arefstate = (ArrayRefState*)palloc0(sizeof(ArrayRefState)); + List *adjust_jumps = NIL; + ListCell *lc; + int i; + + /* Fill constant fields of ArrayRefState */ + arefstate->isassignment = isAssignment; + arefstate->refelemtype = aref->refelemtype; + arefstate->refattrlength = get_typlen(aref->refarraytype); + get_typlenbyvalalign(aref->refelemtype, + &arefstate->refelemlength, + &arefstate->refelembyval, + &arefstate->refelemalign); + + /* + * Evaluate array input. It's safe to do so into resv/resnull, because we + * won't use that as target for any of the other subexpressions, and it'll + * be overwritten by the final EEOP_ARRAYREF_FETCH/ASSIGN step, which is + * pushed last. + */ + ExecInitExprRec(aref->refexpr, state, resv, resnull, node); + + arefstate->refexpr = aref->refexpr; + arefstate->refupperindexpr_count = list_length(aref->refupperindexpr); + arefstate->plpgsql_index = 0; + arefstate->typOid = exprType((Node*)aref); + + /* + * If refexpr yields NULL, and it's a fetch, then result is NULL. We can + * implement this with just JUMP_IF_NULL, since we evaluated the array + * into the desired target location. + */ + if (!isAssignment) + { + scratch->opcode = EEOP_JUMP_IF_NULL; + scratch->d.jump.jumpdone = -1; /* adjust later */ + ExprEvalPushStep(state, scratch); + adjust_jumps = lappend_int(adjust_jumps, + state->steps_len - 1); + } + + /* Verify subscript list lengths are within limit */ + if (list_length(aref->refupperindexpr) > MAXDIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", + list_length(aref->refupperindexpr), MAXDIM))); + + if (list_length(aref->reflowerindexpr) > MAXDIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", + list_length(aref->reflowerindexpr), MAXDIM))); + + /* Evaluate upper subscripts */ + i = 0; + foreach(lc, aref->refupperindexpr) + { + Expr *e = (Expr *) lfirst(lc); + + /* When slicing, individual subscript bounds can be omitted */ + if (!e) + { + arefstate->upperprovided[i] = false; + i++; + continue; + } + + arefstate->upperprovided[i] = true; + + /* Each subscript is evaluated into subscriptvalue/subscriptnull */ + ExecInitExprRec(e, state, + &arefstate->subscriptvalue, &arefstate->subscriptnull, node); + + /* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */ + scratch->opcode = EEOP_ARRAYREF_SUBSCRIPT; + scratch->d.arrayref_subscript.state = arefstate; + scratch->d.arrayref_subscript.off = i; + scratch->d.arrayref_subscript.isupper = true; + scratch->d.arrayref_subscript.jumpdone = -1; /* adjust later */ + ExprEvalPushStep(state, scratch); + adjust_jumps = lappend_int(adjust_jumps, + state->steps_len - 1); + i++; + } + arefstate->numupper = i; + + /* Evaluate lower subscripts similarly */ + i = 0; + foreach(lc, aref->reflowerindexpr) + { + Expr *e = (Expr *) lfirst(lc); + + /* When slicing, individual subscript bounds can be omitted */ + if (!e) + { + arefstate->lowerprovided[i] = false; + i++; + continue; + } + + arefstate->lowerprovided[i] = true; + + /* Each subscript is evaluated into subscriptvalue/subscriptnull */ + ExecInitExprRec(e, state, + &arefstate->subscriptvalue, &arefstate->subscriptnull, node); + + /* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */ + scratch->opcode = EEOP_ARRAYREF_SUBSCRIPT; + scratch->d.arrayref_subscript.state = arefstate; + scratch->d.arrayref_subscript.off = i; + scratch->d.arrayref_subscript.isupper = false; + scratch->d.arrayref_subscript.jumpdone = -1; /* adjust later */ + ExprEvalPushStep(state, scratch); + adjust_jumps = lappend_int(adjust_jumps, + state->steps_len - 1); + i++; + } + arefstate->numlower = i; + + /* Should be impossible if parser is sane, but check anyway: */ + if (arefstate->numlower != 0 && + arefstate->numupper != arefstate->numlower) + elog(ERROR, "upper and lower index lists are not same length"); + + if (isAssignment) + { + Datum *save_innermost_caseval; + bool *save_innermost_casenull; + + /* + * We might have a nested-assignment situation, in which the + * refassgnexpr is itself a FieldStore or ArrayRef that needs to + * obtain and modify the previous value of the array element or slice + * being replaced. If so, we have to extract that value from the + * array and pass it down via the CaseTestExpr mechanism. It's safe + * to reuse the CASE mechanism because there cannot be a CASE between + * here and where the value would be needed, and an array assignment + * can't be within a CASE either. (So saving and restoring + * innermost_caseval is just paranoia, but let's do it anyway.) + * + * Since fetching the old element might be a nontrivial expense, do it + * only if the argument actually needs it. + */ + if (isAssignmentIndirectionExpr(aref->refassgnexpr)) + { + scratch->opcode = EEOP_ARRAYREF_OLD; + scratch->d.arrayref.state = arefstate; + ExprEvalPushStep(state, scratch); + } + + /* ARRAYREF_OLD puts extracted value into prevvalue/prevnull */ + save_innermost_caseval = state->innermost_caseval; + save_innermost_casenull = state->innermost_casenull; + state->innermost_caseval = &arefstate->prevvalue; + state->innermost_casenull = &arefstate->prevnull; + + /* evaluate replacement value into replacevalue/replacenull */ + ExecInitExprRec(aref->refassgnexpr, state, + &arefstate->replacevalue, &arefstate->replacenull, node); + + state->innermost_caseval = save_innermost_caseval; + state->innermost_casenull = save_innermost_casenull; + + /* and perform the assignment */ + scratch->opcode = EEOP_ARRAYREF_ASSIGN; + scratch->d.arrayref.state = arefstate; + ExprEvalPushStep(state, scratch); + } + else + { + /* array fetch is much simpler */ + scratch->opcode = EEOP_ARRAYREF_FETCH; + scratch->d.arrayref.state = arefstate; + ExprEvalPushStep(state, scratch); + } + + /* adjust jump targets */ + foreach(lc, adjust_jumps) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + + if (as->opcode == EEOP_ARRAYREF_SUBSCRIPT) + { + Assert(as->d.arrayref_subscript.jumpdone == -1); + as->d.arrayref_subscript.jumpdone = state->steps_len; + } + else + { + Assert(as->opcode == EEOP_JUMP_IF_NULL); + Assert(as->d.jump.jumpdone == -1); + as->d.jump.jumpdone = state->steps_len; + } + } +} + +/* + * Helper for preparing ArrayRef expressions for evaluation: is expr a nested + * FieldStore or ArrayRef that needs the old element value passed down? + * + * (We could use this in FieldStore too, but in that case passing the old + * value is so cheap there's no need.) + * + * Note: it might seem that this needs to recurse, but it does not; the + * CaseTestExpr, if any, will be directly the arg or refexpr of the top-level + * node. Nested-assignment situations give rise to expression trees in which + * each level of assignment has its own CaseTestExpr, and the recursive + * structure appears within the newvals or refassgnexpr field. + */ +static bool +isAssignmentIndirectionExpr(Expr *expr) +{ + if (expr == NULL) + return false; /* just paranoia */ + if (IsA(expr, FieldStore)) + { + FieldStore *fstore = (FieldStore *) expr; + + if (fstore->arg && IsA(fstore->arg, CaseTestExpr)) + return true; + } + else if (IsA(expr, ArrayRef)) + { + ArrayRef *arrayRef = (ArrayRef *) expr; + + if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr)) + return true; + } + return false; +} + +/* + * Prepare evaluation of a CoerceToDomain expression. + */ +static void +ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest, + ExprState *state, Datum *resv, bool *resnull, Expr *node) +{ + Datum *domainval = NULL; + bool *domainnull = NULL; + ListCell *l; + + scratch->d.domaincheck.resulttype = ctest->resulttype; + /* we'll allocate workspace only if needed */ + scratch->d.domaincheck.checkvalue = NULL; + scratch->d.domaincheck.checknull = NULL; + + /* + * Evaluate argument - it's fine to directly store it into resv/resnull, + * if there's constraint failures there'll be errors, otherwise it's what + * needs to be returned. + */ + ExecInitExprRec(ctest->arg, state, resv, resnull, node); + + List* constraints = GetDomainConstraints(ctest->resulttype); + /* + * Compile code to check each domain constraint. NOTNULL constraints can + * just be applied on the resv/resnull value, but for CHECK constraints we + * need more pushups. + */ + foreach(l, constraints) + { + DomainConstraintState *con = (DomainConstraintState *) lfirst(l); + Datum *save_innermost_domainval; + bool *save_innermost_domainnull; + + scratch->d.domaincheck.constraintname = con->name; + + switch (con->constrainttype) + { + case DOM_CONSTRAINT_NOTNULL: + scratch->opcode = EEOP_DOMAIN_NOTNULL; + ExprEvalPushStep(state, scratch); + break; + case DOM_CONSTRAINT_CHECK: + /* Allocate workspace for CHECK output if we didn't yet */ + if (scratch->d.domaincheck.checkvalue == NULL) + { + scratch->d.domaincheck.checkvalue = + (Datum *) palloc(sizeof(Datum)); + scratch->d.domaincheck.checknull = + (bool *) palloc(sizeof(bool)); + } + + /* + * If first time through, determine where CoerceToDomainValue + * nodes should read from. + */ + if (domainval == NULL) + { + /* + * Since value might be read multiple times, force to R/O + * - but only if it could be an expanded datum. + */ + if (get_typlen(ctest->resulttype) == -1) + { + ExprEvalStep scratch2 = {0}; + + /* Yes, so make output workspace for MAKE_READONLY */ + domainval = (Datum *) palloc(sizeof(Datum)); + domainnull = (bool *) palloc(sizeof(bool)); + + /* Emit MAKE_READONLY */ + scratch2.opcode = EEOP_MAKE_READONLY; + scratch2.resvalue = domainval; + scratch2.resnull = domainnull; + scratch2.d.make_readonly.value = resv; + scratch2.d.make_readonly.isnull = resnull; + ExprEvalPushStep(state, &scratch2); + } + else + { + /* No, so it's fine to read from resv/resnull */ + domainval = resv; + domainnull = resnull; + } + } + + /* + * Set up value to be returned by CoerceToDomainValue nodes. + * We must save and restore innermost_domainval/null fields, + * in case this node is itself within a check expression for + * another domain. + */ + save_innermost_domainval = state->innermost_domainval; + save_innermost_domainnull = state->innermost_domainnull; + state->innermost_domainval = domainval; + state->innermost_domainnull = domainnull; + + /* evaluate check expression value */ + ExecInitExprRec(con->check_node, state, + scratch->d.domaincheck.checkvalue, + scratch->d.domaincheck.checknull, node); + + state->innermost_domainval = save_innermost_domainval; + state->innermost_domainnull = save_innermost_domainnull; + + /* now test result */ + scratch->opcode = EEOP_DOMAIN_CHECK; + ExprEvalPushStep(state, scratch); + + break; + default: + elog(ERROR, "unrecognized constraint type: %d", + (int) con->constrainttype); + break; + } + } +} diff --git a/src/gausskernel/runtime/executor/execExprInterp.cpp b/src/gausskernel/runtime/executor/execExprInterp.cpp new file mode 100644 index 000000000..37f1b275b --- /dev/null +++ b/src/gausskernel/runtime/executor/execExprInterp.cpp @@ -0,0 +1,3872 @@ +/*------------------------------------------------------------------------- + * + * execExprInterp.c + * Interpreted evaluation of an expression step list. + * + * This file provides either a "direct threaded" (for gcc, clang and + * compatible) or a "switch threaded" (for all compilers) implementation of + * expression evaluation. The former is amongst the fastest known methods + * of interpreting programs without resorting to assembly level work, or + * just-in-time compilation, but it requires support for computed gotos. + * The latter is amongst the fastest approaches doable in standard C. + * + * In either case we use ExprEvalStep->opcode to dispatch to the code block + * within ExecInterpExpr() that implements the specific opcode type. + * + * Switch-threading uses a plain switch() statement to perform the + * dispatch. This has the advantages of being plain C and allowing the + * compiler to warn if implementation of a specific opcode has been forgotten. + * The disadvantage is that dispatches will, as commonly implemented by + * compilers, happen from a single location, requiring more jumps and causing + * bad branch prediction. + * + * In direct threading, we use gcc's label-as-values extension - also adopted + * by some other compilers - to replace ExprEvalStep->opcode with the address + * of the block implementing the instruction. Dispatch to the next instruction + * is done by a "computed goto". This allows for better branch prediction + * (as the jumps are happening from different locations) and fewer jumps + * (as no preparatory jump to a common dispatch location is needed). + * + * When using direct threading, ExecReadyInterpretedExpr will replace + * each step's opcode field with the address of the relevant code block and + * ExprState->flags will contain EEO_FLAG_DIRECT_THREADED to remember that + * that's been done. + * + * For very simple instructions the overhead of the full interpreter + * "startup", as minimal as it is, is noticeable. Therefore + * ExecReadyInterpretedExpr will choose to implement certain simple + * opcode patterns using special fast-path routines (ExecJust*). + * + * Complex or uncommon instructions are not implemented in-line in + * ExecInterpExpr(), rather we call out to a helper function appearing later + * in this file. For one reason, there'd not be a noticeable performance + * benefit, but more importantly those complex routines are intended to be + * shared between different expression evaluation approaches. For instance + * a JIT compiler would generate calls to them. (This is why they are + * exported rather than being "static" in this file.) + * + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/gausskernel/runtime/executor/execExprInterp.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/pg_type.h" +#include "commands/sequence.h" +#include "nodes/execExpr.h" +#include "executor/node/nodeSubplan.h" +#include "funcapi.h" +#include "utils/memutils.h" +#include "miscadmin.h" +#include "nodes/nodeFuncs.h" +#include "nodes/execExpr.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/timestamp.h" +#include "utils/typcache.h" +#include "utils/xml.h" +#include "utils/elog.h" +#include "utils/expandeddatum.h" +#include "access/tableam.h" +#include "access/tupconvert.h" +#include "executor/node/nodeCtescan.h" +#include "rewrite/rewriteHandler.h" + +/* + * Use computed-goto-based opcode dispatch when computed gotos are available. + * But use a separate symbol so that it's easy to adjust locally in this file + * for development and testing. + */ +#ifdef HAVE_COMPUTED_GOTO +#define EEO_USE_COMPUTED_GOTO +#endif + +/* + * Macros for opcode dispatch. + * + * EEO_SWITCH - just hides the switch if not in use. + * EEO_CASE - labels the implementation of named expression step type. + * EEO_DISPATCH - jump to the implementation of the step type for 'op'. + * EEO_OPCODE - compute opcode required by used expression evaluation method. + * EEO_NEXT - increment 'op' and jump to correct next step type. + * EEO_JUMP - jump to the specified step number within the current expression. + */ +#if defined(EEO_USE_COMPUTED_GOTO) + +/* struct for jump target -> opcode lookup table */ +typedef struct ExprEvalOpLookup +{ + const void *opcode; + ExprEvalOp op; +} ExprEvalOpLookup; + +/* to make dispatch_table accessible outside ExecInterpExpr() */ +static const void **dispatch_table = NULL; + +/* jump target -> opcode lookup table */ +static ExprEvalOpLookup reverse_dispatch_table[EEOP_LAST]; + +#define EEO_SWITCH() +#define EEO_CASE(name) CASE_##name: +#define EEO_DISPATCH() goto *((void *) op->opcode) +#define EEO_OPCODE(opcode) ((intptr_t) dispatch_table[opcode]) + +#else /* !EEO_USE_COMPUTED_GOTO */ + +#define EEO_SWITCH() starteval: switch ((ExprEvalOp) op->opcode) +#define EEO_CASE(name) case name: +#define EEO_DISPATCH() goto starteval +#define EEO_OPCODE(opcode) (opcode) + +#endif /* EEO_USE_COMPUTED_GOTO */ + +#define EEO_NEXT() \ + do { \ + op++; \ + EEO_DISPATCH(); \ + } while (0) + +#define EEO_JUMP(stepno) \ + do { \ + op = &state->steps[stepno]; \ + EEO_DISPATCH(); \ + } while (0) + +static Datum ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone); +static void ExecInitInterpreter(void); + +/* support functions */ +static void CheckVarSlotCompatibility(TupleTableSlot *slot, int attnum, Oid vartype); +static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod, + TupleDesc *cache_field, ExprContext *econtext); +static void ShutdownTupleDescRef(Datum arg); +static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, + ExprContext *econtext, bool checkisnull); + +/* fast-path evaluation functions */ +static Datum ExecJustInnerVarFirst(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone); +static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone); +static Datum ExecJustOuterVarFirst(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone); +static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone); +static Datum ExecJustScanVarFirst(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone); +static Datum ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone); +static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone); +static Datum ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone); +static Datum ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone); +static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone); + +extern bool func_has_refcursor_args(Oid Funcid, FunctionCallInfoData* fcinfo); +extern void check_huge_clob_paramter(FunctionCallInfoData* fcinfo, bool is_have_huge_clob); +extern Datum fetch_lob_value_from_tuple(varatt_lob_pointer* lob_pointer, Oid update_oid, bool* is_null); + +/* + * Prepare ExprState for interpreted execution. + */ +void +ExecReadyInterpretedExpr(ExprState *state) +{ + /* Ensure one-time interpreter setup has been done */ + ExecInitInterpreter(); + + + /* Simple validity checks on expression */ + Assert(state->steps_len >= 1); + Assert(state->steps[state->steps_len - 1].opcode == EEOP_DONE); + + /* + * Don't perform redundant initialization. This is unreachable in current + * cases, but might be hit if there's additional expression evaluation + * methods that rely on interpreted execution to work. + */ + if (state->flags & EEO_FLAG_INTERPRETER_INITIALIZED) + return; + + /* DIRECT_THREADED should not already be set */ + Assert((state->flags & EEO_FLAG_DIRECT_THREADED) == 0); + + /* + * There shouldn't be any errors before the expression is fully + * initialized, and even if so, it'd lead to the expression being + * abandoned. So we can set the flag now and save some code. + */ + state->flags |= EEO_FLAG_INTERPRETER_INITIALIZED; + + /* + * Select fast-path evalfuncs for very simple expressions. "Starting up" + * the full interpreter is a measurable overhead for these, and these + * patterns occur often enough to be worth optimizing. + */ + if (state->steps_len == 3) + { + ExprEvalOp step0 = (ExprEvalOp)state->steps[0].opcode; + ExprEvalOp step1 = (ExprEvalOp) state->steps[1].opcode; + + if (step0 == EEOP_INNER_FETCHSOME && + step1 == EEOP_INNER_VAR) + { + state->evalfunc = ExecJustInnerVar; + return; + } + else if (step0 == EEOP_OUTER_FETCHSOME && + step1 == EEOP_OUTER_VAR) + { + state->evalfunc = ExecJustOuterVar; + return; + } + else if (step0 == EEOP_SCAN_FETCHSOME && + step1 == EEOP_SCAN_VAR) + { + state->evalfunc = ExecJustScanVar; + return; + } + else if (step0 == EEOP_INNER_FETCHSOME && + step1 == EEOP_ASSIGN_INNER_VAR) + { + state->evalfunc = ExecJustAssignInnerVar; + return; + } + else if (step0 == EEOP_OUTER_FETCHSOME && + step1 == EEOP_ASSIGN_OUTER_VAR) + { + state->evalfunc = ExecJustAssignOuterVar; + return; + } + else if (step0 == EEOP_SCAN_FETCHSOME && + step1 == EEOP_ASSIGN_SCAN_VAR) + { + state->evalfunc = ExecJustAssignScanVar; + return; + } + } + else if (state->steps_len == 2 && + state->steps[0].opcode == EEOP_CONST) + { + state->evalfunc = ExecJustConst; + return; + } + +#if defined(EEO_USE_COMPUTED_GOTO) + + /* + * In the direct-threaded implementation, replace each opcode with the + * address to jump to. (Use ExecEvalStepOp() to get back the opcode.) + */ + { + int off; + + for (off = 0; off < state->steps_len; off++) + { + ExprEvalStep *op = &state->steps[off]; + + op->opcode = EEO_OPCODE(op->opcode); + } + + state->flags |= EEO_FLAG_DIRECT_THREADED; + } +#endif /* EEO_USE_COMPUTED_GOTO */ + + state->evalfunc = ExecInterpExpr; +} + +static inline void +UpdateElogFieldName(ExprState *state) +{ + if (state->expr) { + ListCell *lc = lnext(state->current_targetentry); + if (lc == NULL) + return; + + TargetEntry *te = (TargetEntry*)lfirst(lc); + state->current_targetentry = lc; + ELOG_FIELD_NAME_UPDATE(te->resname); + } +} + +static bool IsTableOfFunc(Oid funcOid) +{ + const Oid array_function_start_oid = 7881; + const Oid array_function_end_oid = 7892; + const Oid array_indexby_delete_oid = 7896; + return (funcOid >= array_function_start_oid && funcOid <= array_function_end_oid) || + funcOid == array_indexby_delete_oid; +} + +static Datum +ExecMakeFunctionResultNoSets(ExprState *state, ExprEvalStep *op,ExprContext *econtext) +{ + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + PgStat_FunctionCallUsage fcusage; + int i; + ListCell* lc = NULL; + + bool savedIsSTP = u_sess->SPI_cxt.is_stp; + bool savedProConfigIsSet = u_sess->SPI_cxt.is_proconfig_set; + bool supportTranaction = false; + bool is_have_huge_clob = false; + bool needResetErrMsg = op->d.func.needResetErrMsg; + + if(!u_sess->SPI_cxt.is_allow_commit_rollback){ + if(fcinfo->context){ + ((FunctionScanState *)(fcinfo->context))->atomic =true; + } + } + + econtext->plpgsql_estate = plpgsql_estate; + plpgsql_estate = NULL; + + if (econtext) { + fcinfo->can_ignore = econtext->can_ignore; + } + + /* + * Incause of connet_by_root() and sys_connect_by_path() we need get the + * current scan tuple slot so attach the econtext here + * + * NOTE: Have to revisit!! so I don't have better solution to handle the case + * where scantuple is available in built in funct + */ + if (fcinfo->flinfo->fn_oid == CONNECT_BY_ROOT_FUNCOID || + fcinfo->flinfo->fn_oid == SYS_CONNECT_BY_PATH_FUNCOID) { + fcinfo->swinfo.sw_econtext = (Node *)econtext; + fcinfo->swinfo.sw_exprstate = (Node *)linitial(op->d.func.args); + fcinfo->swinfo.sw_is_flt_frame = true; + } + + i = 0; + econtext->is_cursor = false; + u_sess->plsql_cxt.func_tableof_index = NIL; + + foreach (lc, op->d.func.args) { + Expr *arg = (Expr *)lfirst(lc); + + if ((op->d.func.flag & FUNC_EXPR_FLAG_HAS_REFCURSOR) && + fcinfo->argTypes[i] == REFCURSOROID) { + econtext->is_cursor = true; + } + + if (is_huge_clob(fcinfo->argTypes[i], fcinfo->argnull[i], fcinfo->arg[i])) { + is_have_huge_clob = true; + } + + ExecTableOfIndexInfo execTableOfIndexInfo; + initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext); + ExecEvalParamExternTableOfIndex((Node*)arg, &execTableOfIndexInfo); + if (execTableOfIndexInfo.tableOfIndex != NULL) { + if (!IsTableOfFunc(fcinfo->flinfo->fn_oid)) { + MemoryContext oldCxt = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); + PLpgSQL_func_tableof_index* func_tableof = + (PLpgSQL_func_tableof_index*)palloc0(sizeof(PLpgSQL_func_tableof_index)); + func_tableof->varno = i; + func_tableof->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; + func_tableof->tableOfIndex = copyTableOfIndex(execTableOfIndexInfo.tableOfIndex); + u_sess->plsql_cxt.func_tableof_index = lappend(u_sess->plsql_cxt.func_tableof_index, func_tableof); + MemoryContextSwitchTo(oldCxt); + } + + u_sess->SPI_cxt.cur_tableof_index->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; + u_sess->SPI_cxt.cur_tableof_index->tableOfIndex = execTableOfIndexInfo.tableOfIndex; + u_sess->SPI_cxt.cur_tableof_index->tableOfNestLayer = execTableOfIndexInfo.tableOfLayers; + /* for nest table of output, save layer of this var tableOfGetNestLayer in ExecEvalArrayRef, + or set to zero for get whole nest table. */ + u_sess->SPI_cxt.cur_tableof_index->tableOfGetNestLayer = -1; + } + + if ((op->d.func.flag & FUNC_EXPR_FLAG_HAS_REFCURSOR) && + econtext->is_cursor) { + op->d.func.var_dno[i] = econtext->dno; + CopyCursorInfoData(&fcinfo->refcursor_data.argCursor[i], &econtext->cursor_data); + } + econtext->is_cursor = false; + i++; + } + + /* + * If function is strict, and there are any NULL arguments, skip calling + * the function and return NULL. + */ + if (op->d.func.flag & FUNC_EXPR_FLAG_STRICT) { + while (--i >= 0) { + if (fcinfo->argnull[i]) { + *op->resnull = true; + u_sess->SPI_cxt.is_stp = savedIsSTP; + u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet; + if (needResetErrMsg) { + stp_reset_commit_rolback_err_msg(); + } + *op->resvalue = 0; + return *op->resvalue; + } + } + } + + if (op->d.func.flag & FUNC_EXPR_FLAG_FUSAGE) + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + check_huge_clob_paramter(fcinfo, is_have_huge_clob); + if (u_sess->instr_cxt.global_instr != NULL && fcinfo->flinfo->fn_addr == plpgsql_call_handler) { + StreamInstrumentation* save_global_instr = u_sess->instr_cxt.global_instr; + u_sess->instr_cxt.global_instr = NULL; + *op->resvalue = op->d.func.fn_addr(fcinfo); + u_sess->instr_cxt.global_instr = save_global_instr; + } else { + if (fcinfo->argTypes[0] == CLOBOID && fcinfo->argTypes[1] == CLOBOID && fcinfo->flinfo->fn_addr == textcat) { + bool is_null = false; + if (fcinfo->arg[0] != 0 && VARATT_IS_EXTERNAL_LOB(fcinfo->arg[0])) { + struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(fcinfo->arg[0])); + fcinfo->arg[0] = fetch_lob_value_from_tuple(lob_pointer, InvalidOid, &is_null); + } + if (fcinfo->arg[1] != 0 && VARATT_IS_EXTERNAL_LOB(fcinfo->arg[1])) { + struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(fcinfo->arg[1])); + fcinfo->arg[1] = fetch_lob_value_from_tuple(lob_pointer, InvalidOid, &is_null); + } + } + *op->resvalue = op->d.func.fn_addr(fcinfo); + } + *op->resnull = fcinfo->isnull; + + if ((op->d.func.flag & FUNC_EXPR_FLAG_HAS_REFCURSOR) && + econtext->plpgsql_estate != NULL) { + PLpgSQL_execstate* estate = econtext->plpgsql_estate; + for (i = 0; i < fcinfo->nargs; i++) { + /* copy in-args cursor option info */ + if (op->d.func.var_dno[i] >= 0) { + int dno = op->d.func.var_dno[i]; + Cursor_Data* cursor_data = &fcinfo->refcursor_data.argCursor[i]; +#ifdef USE_ASSERT_CHECKING + PLpgSQL_datum* datum = estate->datums[dno]; +#endif + Assert(datum->dtype == PLPGSQL_DTYPE_VAR); + Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID); + + ExecCopyDataToDatum(estate->datums, dno, cursor_data); + } + } + + if (fcinfo->flinfo->fn_rettype == REFCURSOROID) { + /* copy function returns cursor option info. + * for simple expr in exec_eval_expr, we can not get the result type, + * so cursor_return_data mallocs here. + */ + if (estate->cursor_return_data == NULL) { + estate->cursor_return_data = (Cursor_Data*)palloc0(sizeof(Cursor_Data)); + estate->cursor_return_numbers = 1; + } + int rc = memcpy_s(estate->cursor_return_data, + sizeof(Cursor_Data), + fcinfo->refcursor_data.returnCursor, + sizeof(Cursor_Data)); + securec_check(rc, "\0", "\0"); + } + } + + if (op->d.func.flag & FUNC_EXPR_FLAG_FUSAGE) + pgstat_end_function_usage(&fcusage, true); + + if (op->d.func.flag & FUNC_EXPR_FLAG_HAS_REFCURSOR) { + if (fcinfo->refcursor_data.argCursor != NULL) + pfree_ext(fcinfo->refcursor_data.argCursor); + if (op->d.func.var_dno != NULL) + pfree_ext(op->d.func.var_dno); + } + + u_sess->SPI_cxt.is_stp = savedIsSTP; + u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet; + if (needResetErrMsg) { + stp_reset_commit_rolback_err_msg(); + } + + if (op->d.func.is_plpgsql_func_with_outparam) { + set_result_for_plpgsql_language_function_with_outparam_by_flatten(op->resvalue, op->resnull); + } + + return *op->resvalue; +} + +/* + * Evaluate expression identified by "state" in the execution context + * given by "econtext". *isnull is set to the is-null flag for the result, + * and the Datum value is the function result. + * + * As a special case, return the dispatch table's address if state is NULL. + * This is used by ExecInitInterpreter to set up the dispatch_table global. + * (Only applies when EEO_USE_COMPUTED_GOTO is defined.) + */ +static Datum +ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone) +{ + ExprEvalStep *op; + TupleTableSlot *resultslot; + TupleTableSlot *innerslot; + TupleTableSlot *outerslot; + TupleTableSlot *scanslot; + + /* + * This array has to be in the same order as enum ExprEvalOp. + */ +#if defined(EEO_USE_COMPUTED_GOTO) + static const void *const dispatch_table[] = { + &&CASE_EEOP_DONE, + &&CASE_EEOP_INNER_FETCHSOME, + &&CASE_EEOP_OUTER_FETCHSOME, + &&CASE_EEOP_SCAN_FETCHSOME, + &&CASE_EEOP_INNER_VAR, + &&CASE_EEOP_OUTER_VAR, + &&CASE_EEOP_SCAN_VAR, + &&CASE_EEOP_INNER_SYSVAR, + &&CASE_EEOP_OUTER_SYSVAR, + &&CASE_EEOP_SCAN_SYSVAR, + &&CASE_EEOP_WHOLEROW, + &&CASE_EEOP_ASSIGN_INNER_VAR, + &&CASE_EEOP_ASSIGN_OUTER_VAR, + &&CASE_EEOP_ASSIGN_SCAN_VAR, + &&CASE_EEOP_ASSIGN_TMP, + &&CASE_EEOP_ASSIGN_TMP_MAKE_RO, + &&CASE_EEOP_CONST, + &&CASE_EEOP_FUNCEXPR, + &&CASE_EEOP_BOOL_AND_STEP_FIRST, + &&CASE_EEOP_BOOL_AND_STEP, + &&CASE_EEOP_BOOL_AND_STEP_LAST, + &&CASE_EEOP_BOOL_OR_STEP_FIRST, + &&CASE_EEOP_BOOL_OR_STEP, + &&CASE_EEOP_BOOL_OR_STEP_LAST, + &&CASE_EEOP_BOOL_NOT_STEP, + &&CASE_EEOP_QUAL, + &&CASE_EEOP_JUMP, + &&CASE_EEOP_JUMP_IF_NULL, + &&CASE_EEOP_JUMP_IF_NOT_NULL, + &&CASE_EEOP_JUMP_IF_NOT_TRUE, + &&CASE_EEOP_NULLTEST_ISNULL, + &&CASE_EEOP_NULLTEST_ISNOTNULL, + &&CASE_EEOP_NULLTEST_ROWISNULL, + &&CASE_EEOP_NULLTEST_ROWISNOTNULL, + &&CASE_EEOP_BOOLTEST_IS_TRUE, + &&CASE_EEOP_BOOLTEST_IS_NOT_TRUE, + &&CASE_EEOP_BOOLTEST_IS_FALSE, + &&CASE_EEOP_BOOLTEST_IS_NOT_FALSE, + &&CASE_EEOP_PARAM_EXEC, + &&CASE_EEOP_PARAM_EXTERN, + &&CASE_EEOP_CASE_TESTVAL, + &&CASE_EEOP_MAKE_READONLY, + &&CASE_EEOP_IOCOERCE, + &&CASE_EEOP_DISTINCT, + &&CASE_EEOP_NULLIF, + &&CASE_EEOP_CURRENTOFEXPR, + &&CASE_EEOP_ARRAYEXPR, + &&CASE_EEOP_ARRAYCOERCE, + &&CASE_EEOP_ROW, + &&CASE_EEOP_ROWCOMPARE_STEP, + &&CASE_EEOP_ROWCOMPARE_FINAL, + &&CASE_EEOP_MINMAX, + &&CASE_EEOP_FIELDSELECT, + &&CASE_EEOP_FIELDSTORE_DEFORM, + &&CASE_EEOP_FIELDSTORE_FORM, + &&CASE_EEOP_ARRAYREF_SUBSCRIPT, + &&CASE_EEOP_ARRAYREF_OLD, + &&CASE_EEOP_ARRAYREF_ASSIGN, + &&CASE_EEOP_ARRAYREF_FETCH, + &&CASE_EEOP_DOMAIN_TESTVAL, + &&CASE_EEOP_DOMAIN_NOTNULL, + &&CASE_EEOP_DOMAIN_CHECK, + &&CASE_EEOP_CONVERT_ROWTYPE, + &&CASE_EEOP_SCALARARRAYOP, + &&CASE_EEOP_XMLEXPR, + &&CASE_EEOP_AGGREF, + &&CASE_EEOP_GROUPING_FUNC, + &&CASE_EEOP_WINDOW_FUNC, + &&CASE_EEOP_SUBPLAN, + &&CASE_EEOP_ALTERNATIVE_SUBPLAN, + &&CASE_EEOP_ROWNUM, + &&CASE_EEOP_GROUPING_ID, + &&CASE_EEOP_HASH_FILTER, + &&CASE_EEOP_USERVAR_OR_SETVARIABLE, + &&CASE_EEOP_USERSET_ELEM, + &&CASE_EEOP_PREFIX_BTYEA, + &&CASE_EEOP_PREFIX_TEXT, + &&CASE_EEOP_LAST + }; + + StaticAssertStmt(EEOP_LAST + 1 == lengthof(dispatch_table), + "dispatch_table out of whack with ExprEvalOp"); + + if (unlikely(state == NULL)) + return PointerGetDatum(dispatch_table); +#else + Assert(state != NULL); +#endif /* EEO_USE_COMPUTED_GOTO */ + + /* setup state */ + op = state->steps; + resultslot = state->resultslot; + innerslot = econtext->ecxt_innertuple; + outerslot = econtext->ecxt_outertuple; + scanslot = econtext->ecxt_scantuple; + + if (isDone != NULL) + *isDone = ExprSingleResult; + +#if defined(EEO_USE_COMPUTED_GOTO) + EEO_DISPATCH(); +#endif + + EEO_SWITCH() + { + EEO_CASE(EEOP_DONE) + { + goto out; + } + + EEO_CASE(EEOP_INNER_FETCHSOME) + { + tableam_tslot_getsomeattrs(innerslot, op->d.fetch.last_var); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_OUTER_FETCHSOME) + { + tableam_tslot_getsomeattrs(outerslot, op->d.fetch.last_var); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_SCAN_FETCHSOME) + { + tableam_tslot_getsomeattrs(scanslot, op->d.fetch.last_var); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_INNER_VAR) + { + int attnum = op->d.var.attnum; + + /* + * Since we already extracted all referenced columns from the + * tuple with a FETCHSOME step, we can just grab the value + * directly out of the slot's decomposed-data arrays. But let's + * have an Assert to check that that did happen. + */ +// Assert((innerslot->tts_tupslotTableAm == TAM_HEAP) ? (attnum >= 0 && attnum < innerslot->tts_nvalid) : (attnum >= 0)); + *op->resvalue = innerslot->tts_values[attnum]; + *op->resnull = innerslot->tts_isnull[attnum]; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_OUTER_VAR) + { + int attnum = op->d.var.attnum; + + /* See EEOP_INNER_VAR comments */ + +// Assert((outerslot->tts_tupslotTableAm == TAM_HEAP) ? (attnum >= 0 && attnum < outerslot->tts_nvalid) : (attnum >= 0)); + *op->resvalue = outerslot->tts_values[attnum]; + *op->resnull = outerslot->tts_isnull[attnum]; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_SCAN_VAR) + { + int attnum = op->d.var.attnum; + + /* See EEOP_INNER_VAR comments */ + +// Assert((scanslot->tts_tupslotTableAm == TAM_HEAP) ? (attnum >= 0 && attnum < scanslot->tts_nvalid) : (attnum >= 0)); + *op->resvalue = scanslot->tts_values[attnum]; + *op->resnull = scanslot->tts_isnull[attnum]; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_INNER_SYSVAR) + { + int attnum = op->d.var.attnum; + Datum d; + + /* these asserts must match defenses in slot_getattr */ + Assert(innerslot->tts_tuple != NULL); + Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr)); + + /* heap_getsysattr has sufficient defenses against bad attnums */ + d = tableam_tslot_getattr(innerslot, attnum, op->resnull); + *op->resvalue = d; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_OUTER_SYSVAR) + { + int attnum = op->d.var.attnum; + Datum d; + + /* these asserts must match defenses in slot_getattr */ + Assert(outerslot->tts_tuple != NULL); + Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr)); + + /* heap_getsysattr has sufficient defenses against bad attnums */ + d = tableam_tslot_getattr(outerslot, attnum, op->resnull); + *op->resvalue = d; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_SCAN_SYSVAR) + { + int attnum = op->d.var.attnum; + Datum d; + + /* these asserts must match defenses in slot_getattr */ + Assert(scanslot->tts_tuple != NULL); + Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr)); + + /* heap_getsysattr has sufficient defenses against bad attnums */ + d = tableam_tslot_getattr(scanslot, attnum, op->resnull); + *op->resvalue = d; + + EEO_NEXT(); + + } + + EEO_CASE(EEOP_WHOLEROW) + { + /* too complex for an inline implementation */ + ExecEvalWholeRowVar(state, op, econtext); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ASSIGN_INNER_VAR) + { + int resultnum = op->d.assign_var.resultnum; + int attnum = op->d.assign_var.attnum; + + /* + * We do not need CheckVarSlotCompatibility here; that was taken + * care of at compilation time. But see EEOP_INNER_VAR comments. + */ +// Assert((innerslot->tts_tupslotTableAm == TAM_HEAP) ? (attnum >= 0 && attnum < innerslot->tts_nvalid) : (attnum >= 0)); + Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); + resultslot->tts_values[resultnum] = innerslot->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum]; + + UpdateElogFieldName(state); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ASSIGN_OUTER_VAR) + { + int resultnum = op->d.assign_var.resultnum; + int attnum = op->d.assign_var.attnum; + + /* + * We do not need CheckVarSlotCompatibility here; that was taken + * care of at compilation time. But see EEOP_INNER_VAR comments. + */ +// Assert((outerslot->tts_tupslotTableAm == TAM_HEAP) ? (attnum >= 0 && attnum < outerslot->tts_nvalid) : (attnum >= 0)); + Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); + resultslot->tts_values[resultnum] = outerslot->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum]; + + UpdateElogFieldName(state); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ASSIGN_SCAN_VAR) + { + int resultnum = op->d.assign_var.resultnum; + int attnum = op->d.assign_var.attnum; + + /* + * We do not need CheckVarSlotCompatibility here; that was taken + * care of at compilation time. But see EEOP_INNER_VAR comments. + */ +// Assert((scanslot->tts_tupslotTableAm == TAM_HEAP) ? (attnum >= 0 && attnum < scanslot->tts_nvalid) : (attnum >= 0)); + Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); + resultslot->tts_values[resultnum] = scanslot->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum]; + + UpdateElogFieldName(state); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ASSIGN_TMP) + { + int resultnum = op->d.assign_tmp.resultnum; + + Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + + UpdateElogFieldName(state); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ASSIGN_TMP_MAKE_RO) + { + int resultnum = op->d.assign_tmp.resultnum; + + Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); + resultslot->tts_isnull[resultnum] = state->resnull; + if (!resultslot->tts_isnull[resultnum]) + resultslot->tts_values[resultnum] = + MakeExpandedObjectReadOnlyInternal(state->resvalue); + else + resultslot->tts_values[resultnum] = state->resvalue; + + UpdateElogFieldName(state); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_CONST) + { + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + + /* if a const cursor, copy cursor option data to econtext */ + if ((econtext->is_cursor || op->d.constval.is_cursor) && op->d.constval.con && + op->d.constval.con->consttype == REFCURSOROID) { + CopyCursorInfoData(&econtext->cursor_data, &op->d.constval.con->cursor_data); + econtext->dno = op->d.constval.con->cursor_data.cur_dno; + } + + EEO_NEXT(); + } + + /* + * Function-call implementations. Arguments have previously been + * evaluated directly into fcinfo->args. + * + * As both STRICT checks and function-usage are noticeable performance + * wise, and function calls are a very hot-path (they also back + * operators!), it's worth having so many separate opcodes. + * + * Note: the reason for using a temporary variable "d", here and in + * other places, is that some compilers think "*op->resvalue = f();" + * requires them to evaluate op->resvalue into a register before + * calling f(), just in case f() is able to modify op->resvalue + * somehow. The extra line of code can save a useless register spill + * and reload across the function call. + */ + EEO_CASE(EEOP_FUNCEXPR) + { + ExecMakeFunctionResultNoSets(state, op, econtext); + + EEO_NEXT(); + } + + /* + * If any of its clauses is FALSE, an AND's result is FALSE regardless + * of the states of the rest of the clauses, so we can stop evaluating + * and return FALSE immediately. If none are FALSE and one or more is + * NULL, we return NULL; otherwise we return TRUE. This makes sense + * when you interpret NULL as "don't know": perhaps one of the "don't + * knows" would have been FALSE if we'd known its value. Only when + * all the inputs are known to be TRUE can we state confidently that + * the AND's result is TRUE. + */ + EEO_CASE(EEOP_BOOL_AND_STEP_FIRST) + { + *op->d.boolexpr.anynull = false; + + /* + * EEOP_BOOL_AND_STEP_FIRST resets anynull, otherwise it's the + * same as EEOP_BOOL_AND_STEP - so fall through to that. + */ + + /* FALL THROUGH */ + } + + EEO_CASE(EEOP_BOOL_AND_STEP) + { + if (*op->resnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->resvalue)) + { + /* result is already set to FALSE, need not change it */ + /* bail out early */ + EEO_JUMP(op->d.boolexpr.jumpdone); + } + + EEO_NEXT(); + } + + EEO_CASE(EEOP_BOOL_AND_STEP_LAST) + { + if (*op->resnull) + { + /* result is already set to NULL, need not change it */ + } + else if (!DatumGetBool(*op->resvalue)) + { + /* result is already set to FALSE, need not change it */ + + /* + * No point jumping early to jumpdone - would be same target + * (as this is the last argument to the AND expression), + * except more expensive. + */ + } + else if (*op->d.boolexpr.anynull) + { + *op->resvalue = (Datum) 0; + *op->resnull = true; + } + else + { + /* result is already set to TRUE, need not change it */ + } + + EEO_NEXT(); + } + + /* + * If any of its clauses is TRUE, an OR's result is TRUE regardless of + * the states of the rest of the clauses, so we can stop evaluating + * and return TRUE immediately. If none are TRUE and one or more is + * NULL, we return NULL; otherwise we return FALSE. This makes sense + * when you interpret NULL as "don't know": perhaps one of the "don't + * knows" would have been TRUE if we'd known its value. Only when all + * the inputs are known to be FALSE can we state confidently that the + * OR's result is FALSE. + */ + EEO_CASE(EEOP_BOOL_OR_STEP_FIRST) + { + *op->d.boolexpr.anynull = false; + + /* + * EEOP_BOOL_OR_STEP_FIRST resets anynull, otherwise it's the same + * as EEOP_BOOL_OR_STEP - so fall through to that. + */ + + /* FALL THROUGH */ + } + + EEO_CASE(EEOP_BOOL_OR_STEP) + { + if (*op->resnull) + { + *op->d.boolexpr.anynull = true; + } + else if (DatumGetBool(*op->resvalue)) + { + /* result is already set to TRUE, need not change it */ + /* bail out early */ + EEO_JUMP(op->d.boolexpr.jumpdone); + } + + EEO_NEXT(); + } + + EEO_CASE(EEOP_BOOL_OR_STEP_LAST) + { + if (*op->resnull) + { + /* result is already set to NULL, need not change it */ + } + else if (DatumGetBool(*op->resvalue)) + { + /* result is already set to TRUE, need not change it */ + + /* + * No point jumping to jumpdone - would be same target (as + * this is the last argument to the AND expression), except + * more expensive. + */ + } + else if (*op->d.boolexpr.anynull) + { + *op->resvalue = (Datum) 0; + *op->resnull = true; + } + else + { + /* result is already set to FALSE, need not change it */ + } + + EEO_NEXT(); + } + + EEO_CASE(EEOP_BOOL_NOT_STEP) + { + /* + * Evaluation of 'not' is simple... if expr is false, then return + * 'true' and vice versa. It's safe to do this even on a + * nominally null value, so we ignore resnull; that means that + * NULL in produces NULL out, which is what we want. + */ + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_QUAL) + { + /* simplified version of BOOL_AND_STEP for use by ExecQual() */ + + /* If argument (also result) is false or null ... */ + if (*op->resnull || + !DatumGetBool(*op->resvalue)) + { + /* ... bail out early, returning FALSE */ + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + EEO_JUMP(op->d.qualexpr.jumpdone); + } + + /* + * Otherwise, leave the TRUE value in place, in case this is the + * last qual. Then, TRUE is the correct answer. + */ + + EEO_NEXT(); + } + + EEO_CASE(EEOP_JUMP) + { + /* Unconditionally jump to target step */ + EEO_JUMP(op->d.jump.jumpdone); + } + + EEO_CASE(EEOP_JUMP_IF_NULL) + { + /* Transfer control if current result is null */ + if (*op->resnull) + EEO_JUMP(op->d.jump.jumpdone); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_JUMP_IF_NOT_NULL) + { + /* Transfer control if current result is non-null */ + if (!*op->resnull) + EEO_JUMP(op->d.jump.jumpdone); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_JUMP_IF_NOT_TRUE) + { + /* Transfer control if current result is null or false */ + if (*op->resnull || !DatumGetBool(*op->resvalue)) + EEO_JUMP(op->d.jump.jumpdone); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_NULLTEST_ISNULL) + { + *op->resvalue = BoolGetDatum(*op->resnull); + *op->resnull = false; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_NULLTEST_ISNOTNULL) + { + *op->resvalue = BoolGetDatum(!*op->resnull); + *op->resnull = false; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_NULLTEST_ROWISNULL) + { + /* out of line implementation: too large */ + ExecEvalRowNull(state, op, econtext); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_NULLTEST_ROWISNOTNULL) + { + /* out of line implementation: too large */ + ExecEvalRowNotNull(state, op, econtext); + + EEO_NEXT(); + } + + /* BooleanTest implementations for all booltesttypes */ + + EEO_CASE(EEOP_BOOLTEST_IS_TRUE) + { + if (*op->resnull) + *op->resvalue = BoolGetDatum(false); + else + *op->resvalue = *op->resvalue; + *op->resnull = false; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_BOOLTEST_IS_NOT_TRUE) + { + if (*op->resnull) + *op->resvalue = BoolGetDatum(true); + else + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); + *op->resnull = false; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_BOOLTEST_IS_FALSE) + { + if (*op->resnull) + *op->resvalue = BoolGetDatum(false); + else + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); + *op->resnull = false; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_BOOLTEST_IS_NOT_FALSE) + { + if (*op->resnull) + *op->resvalue = BoolGetDatum(true); + else + *op->resvalue = *op->resvalue; + *op->resnull = false; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_PARAM_EXEC) + { + /* out of line implementation: too large */ + ExecEvalParamExec(state, op, econtext); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_PARAM_EXTERN) + { + /* out of line implementation: too large */ + ExecEvalParamExtern(state, op, econtext); + EEO_NEXT(); + } + + EEO_CASE(EEOP_CASE_TESTVAL) + { + /* + * Normally upper parts of the expression tree have setup the + * values to be returned here, but some parts of the system + * currently misuse {caseValue,domainValue}_{datum,isNull} to set + * run-time data. So if no values have been set-up, use + * ExprContext's. This isn't pretty, but also not *that* ugly, + * and this is unlikely to be performance sensitive enough to + * worry about an extra branch. + */ + if (op->d.casetest.value) + { + *op->resvalue = *op->d.casetest.value; + *op->resnull = *op->d.casetest.isnull; + } + else + { + *op->resvalue = econtext->caseValue_datum; + *op->resnull = econtext->caseValue_isNull; + } + + EEO_NEXT(); + } + + EEO_CASE(EEOP_DOMAIN_TESTVAL) + { + /* + * See EEOP_CASE_TESTVAL comment. + */ + if (op->d.casetest.value) + { + *op->resvalue = *op->d.casetest.value; + *op->resnull = *op->d.casetest.isnull; + } + else + { + *op->resvalue = econtext->domainValue_datum; + *op->resnull = econtext->domainValue_isNull; + } + + EEO_NEXT(); + } + + EEO_CASE(EEOP_MAKE_READONLY) + { + /* + * Force a varlena value that might be read multiple times to R/O + */ + if (!*op->d.make_readonly.isnull) + *op->resvalue = + MakeExpandedObjectReadOnlyInternal(*op->d.make_readonly.value); + *op->resnull = *op->d.make_readonly.isnull; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_IOCOERCE) + { + /* + * Evaluate a CoerceViaIO node. This can be quite a hot path, so + * inline as much work as possible. The source value is in our + * result variable. + */ + char *str; + + /* call output function (similar to OutputFunctionCall) */ + if (*op->resnull) + { + /* output functions are not called on nulls */ + str = NULL; + } + else + { + FunctionCallInfo fcinfo_out; + + fcinfo_out = op->d.iocoerce.fcinfo_data_out; + fcinfo_out->arg[0] = *op->resvalue; + fcinfo_out->argnull[0] = false; + + fcinfo_out->isnull = false; + str = DatumGetCString(FunctionCallInvoke(fcinfo_out)); + + /* OutputFunctionCall assumes result isn't null */ + Assert(!fcinfo_out->isnull); + } + + /* call input function (similar to InputFunctionCall) */ + if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL) + { + FunctionCallInfo fcinfo_in; + + fcinfo_in = op->d.iocoerce.fcinfo_data_in; + fcinfo_in->arg[0] = PointerGetDatum(str); + fcinfo_in->argnull[0] = *op->resnull; + /* second and third arguments are already set up */ + + fcinfo_in->isnull = false; + *op->resvalue = FunctionCallInvoke(fcinfo_in); + + /* Should get null result if and only if str is NULL */ + if (str == NULL) + { + Assert(*op->resnull); + Assert(fcinfo_in->isnull); + } + else + { + Assert(!*op->resnull); + Assert(!fcinfo_in->isnull); + } + } + + EEO_NEXT(); + } + + EEO_CASE(EEOP_DISTINCT) + { + /* + * IS DISTINCT FROM must evaluate arguments (already done into + * fcinfo->args) to determine whether they are NULL; if either is + * NULL then the result is determined. If neither is NULL, then + * proceed to evaluate the comparison function, which is just the + * type's standard equality operator. We need not care whether + * that function is strict. Because the handling of nulls is + * different, we can't just reuse EEOP_FUNCEXPR. + */ + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + /* check function arguments for NULLness */ + if (fcinfo->argnull[0] && fcinfo->argnull[1]) + { + /* Both NULL? Then is not distinct... */ + *op->resvalue = BoolGetDatum(false); + *op->resnull = false; + } + else if (fcinfo->argnull[0] || fcinfo->argnull[1]) + { + /* Only one is NULL? Then is distinct... */ + *op->resvalue = BoolGetDatum(true); + *op->resnull = false; + } + else + { + /* Neither null, so apply the equality function */ + Datum eqresult; + + fcinfo->isnull = false; + eqresult = op->d.func.fn_addr(fcinfo); + /* Must invert result of "="; safe to do even if null */ + *op->resvalue = BoolGetDatum(!DatumGetBool(eqresult)); + *op->resnull = fcinfo->isnull; + } + + EEO_NEXT(); + } + + EEO_CASE(EEOP_NULLIF) + { + /* + * The arguments are already evaluated into fcinfo->args. + */ + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + /* if either argument is NULL they can't be equal */ + if (!fcinfo->argnull[0] && !fcinfo->argnull[1]) + { + Datum result; + + fcinfo->isnull = false; + result = op->d.func.fn_addr(fcinfo); + + /* if the arguments are equal return null */ + if (!fcinfo->isnull && DatumGetBool(result)) + { + *op->resvalue = (Datum) 0; + *op->resnull = true; + + EEO_NEXT(); + } + } + + /* Arguments aren't equal, so return the first one */ + *op->resvalue = fcinfo->arg[0]; + *op->resnull = fcinfo->argnull[0]; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_CURRENTOFEXPR) + { + /* error invocation uses space, and shouldn't ever occur */ + ExecEvalCurrentOfExpr(state, op); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ARRAYEXPR) + { + /* too complex for an inline implementation */ + ExecEvalArrayExpr(state, op); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ARRAYCOERCE) + { + /* too complex for an inline implementation */ + ExecEvalArrayCoerce(state, op); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ROW) + { + /* too complex for an inline implementation */ + ExecEvalRow(state, op); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ROWCOMPARE_STEP) + { + FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data; + + /* force NULL result if strict fn and NULL input */ + if (op->d.rowcompare_step.finfo->fn_strict && + (fcinfo->argnull[0] || fcinfo->argnull[1])) + { + *op->resnull = true; + EEO_JUMP(op->d.rowcompare_step.jumpnull); + } + + /* Apply comparison function */ + fcinfo->isnull = false; + *op->resvalue = (op->d.rowcompare_step.fn_addr) (fcinfo); + + /* force NULL result if NULL function result */ + if (fcinfo->isnull) + { + *op->resnull = true; + EEO_JUMP(op->d.rowcompare_step.jumpnull); + } + *op->resnull = false; + + /* If unequal, no need to compare remaining columns */ + if (DatumGetInt32(*op->resvalue) != 0) + { + EEO_JUMP(op->d.rowcompare_step.jumpdone); + } + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ROWCOMPARE_FINAL) + { + int32 cmpresult = DatumGetInt32(*op->resvalue); + RowCompareType rctype = op->d.rowcompare_final.rctype; + + *op->resnull = false; + switch (rctype) + { + /* EQ and NE cases aren't allowed here */ + case ROWCOMPARE_LT: + *op->resvalue = BoolGetDatum(cmpresult < 0); + break; + case ROWCOMPARE_LE: + *op->resvalue = BoolGetDatum(cmpresult <= 0); + break; + case ROWCOMPARE_GE: + *op->resvalue = BoolGetDatum(cmpresult >= 0); + break; + case ROWCOMPARE_GT: + *op->resvalue = BoolGetDatum(cmpresult > 0); + break; + default: + Assert(false); + break; + } + + EEO_NEXT(); + } + + EEO_CASE(EEOP_MINMAX) + { + /* too complex for an inline implementation */ + ExecEvalMinMax(state, op); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_FIELDSELECT) + { + /* too complex for an inline implementation */ + ExecEvalFieldSelect(state, op, econtext); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_FIELDSTORE_DEFORM) + { + /* too complex for an inline implementation */ + ExecEvalFieldStoreDeForm(state, op, econtext); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_FIELDSTORE_FORM) + { + /* too complex for an inline implementation */ + ExecEvalFieldStoreForm(state, op, econtext); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ARRAYREF_SUBSCRIPT) + { + /* Process an array subscript */ + + /* too complex for an inline implementation */ + if (ExecEvalArrayRefSubscript(state, op, econtext)) + { + EEO_NEXT(); + } + else + { + /* Subscript is null, short-circuit ArrayRef to NULL */ + EEO_JUMP(op->d.arrayref_subscript.jumpdone); + } + } + + EEO_CASE(EEOP_ARRAYREF_OLD) + { + /* + * Fetch the old value in an arrayref assignment, in case it's + * referenced (via a CaseTestExpr) inside the assignment + * expression. + */ + + /* too complex for an inline implementation */ + ExecEvalArrayRefOld(state, op); + + EEO_NEXT(); + } + + /* + * Perform ArrayRef assignment + */ + EEO_CASE(EEOP_ARRAYREF_ASSIGN) + { + /* too complex for an inline implementation */ + ExecEvalArrayRefAssign(state, op); + + EEO_NEXT(); + } + + /* + * Fetch subset of an array. + */ + EEO_CASE(EEOP_ARRAYREF_FETCH) + { + /* too complex for an inline implementation */ + ExecEvalArrayRefFetch(state, op); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_CONVERT_ROWTYPE) + { + /* too complex for an inline implementation */ + ExecEvalConvertRowtype(state, op, econtext); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_SCALARARRAYOP) + { + /* too complex for an inline implementation */ + ExecEvalScalarArrayOp(state, op); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_DOMAIN_NOTNULL) + { + /* too complex for an inline implementation */ + ExecEvalConstraintNotNull(state, op); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_DOMAIN_CHECK) + { + /* too complex for an inline implementation */ + ExecEvalConstraintCheck(state, op); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_XMLEXPR) + { + /* too complex for an inline implementation */ + ExecEvalXmlExpr(state, op); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_AGGREF) + { + /* + * Returns a Datum whose value is the precomputed aggregate value + * found in the given expression context. + */ + AggrefExprState *aggref = op->d.aggref.astate; + + Assert(econtext->ecxt_aggvalues != NULL); + + *op->resvalue = econtext->ecxt_aggvalues[aggref->aggno]; + *op->resnull = econtext->ecxt_aggnulls[aggref->aggno]; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_GROUPING_FUNC) + { + /* too complex/uncommon for an inline implementation */ + ExecEvalGroupingFunc(state, op); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_WINDOW_FUNC) + { + /* + * Like Aggref, just return a precomputed value from the econtext. + */ + WindowFuncExprState *wfunc = op->d.window_func.wfstate; + + Assert(econtext->ecxt_aggvalues != NULL); + + *op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno]; + *op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno]; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_SUBPLAN) + { + /* too complex for an inline implementation */ + ExecEvalSubPlan(state, op, econtext); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_ALTERNATIVE_SUBPLAN) + { + /* too complex for an inline implementation */ + ExecEvalAlternativeSubPlan(state, op, econtext); + + EEO_NEXT(); + } + + /* rownum */ + EEO_CASE(EEOP_ROWNUM) + { + if (op->d.rownum.typeCompat) { + *op->resvalue = DirectFunctionCall1(int8_numeric, Int64GetDatum(op->d.rownum.RownumState->ps_rownum + 1)); + } else { + *op->resvalue = Int64GetDatum(op->d.rownum.RownumState->ps_rownum + 1); + } + *op->resnull = false; + + EEO_NEXT(); + } + + /* grouping_id */ + EEO_CASE(EEOP_GROUPING_ID) + { + int groupingId = 0; + + for (int i = 0; i < op->d.grouping_id.GroupingIdState->current_phase; i++) { + groupingId += op->d.grouping_id.GroupingIdState->phases[i].numsets; + } + groupingId += op->d.grouping_id.GroupingIdState->projected_set + 1; + + *op->resvalue = (Datum)groupingId; + *op->resnull = false; + + EEO_NEXT(); + } + + /* hash_filter */ + EEO_CASE(EEOP_HASH_FILTER) + { + ExecEvalHashFilter(state, op, econtext); + + EEO_NEXT(); + } + + EEO_CASE(EEOP_USERVAR_OR_SETVARIABLE) + { + Const* con = op->d.uservar.con; + *op->resnull = con->constisnull; + + if (econtext->is_cursor && con->consttype == REFCURSOROID) { + CopyCursorInfoData(&econtext->cursor_data, &con->cursor_data); + econtext->dno = con->cursor_data.cur_dno; + } + *op->resvalue = con->constvalue; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_USERSET_ELEM) + { + UserSetElem *elem = op->d.userset.useexpr; + Node *node = NULL; + UserSetElem elemcopy; + elemcopy.xpr = elem->xpr; + elemcopy.name = elem->name; + + node = eval_const_expression_value(NULL, (Node *)elem->val, NULL); + if (nodeTag(node) == T_Const) { + elemcopy.val = (Expr *)const_expression_to_const(node); + } else { + elemcopy.val = (Expr *)const_expression_to_const(QueryRewriteNonConstant(node)); + } + + check_set_user_message(&elemcopy); + + *op->resvalue = ((Const *)elemcopy.val)->constvalue; + + *op->resnull = false; + + EEO_NEXT(); + } + + EEO_CASE(EEOP_PREFIX_BTYEA) + { + if (*op->resnull) { + *op->resvalue = (Datum)0; + *op->resnull = true; + } else { + *op->resvalue = PointerGetDatum(bytea_substring(*op->resvalue, 1, op->d.prefix_key.pkey->length, false)); + *op->resnull = false; + } + + EEO_NEXT(); + } + + EEO_CASE(EEOP_PREFIX_TEXT) + { + if (*op->resnull) { + *op->resvalue = (Datum)0; + *op->resnull = true; + } else { + *op->resvalue = PointerGetDatum(text_substring(*op->resvalue, 1, op->d.prefix_key.pkey->length, false)); + *op->resnull = false; + } + + EEO_NEXT(); + } + + EEO_CASE(EEOP_LAST) + { + /* unreachable */ + Assert(false); + goto out; + } + } + +out: + *isnull = state->resnull; + return state->resvalue; +} + +/* + * Check whether a user attribute in a slot can be referenced by a Var + * expression. This should succeed unless there have been schema changes + * since the expression tree has been created. + */ +static void +CheckVarSlotCompatibility(TupleTableSlot *slot, int attnum, Oid vartype) +{ + /* + * What we have to check for here is the possibility of an attribute + * having been dropped or changed in type since the plan tree was created. + * Ideally the plan will get invalidated and not re-used, but just in + * case, we keep these defenses. Fortunately it's sufficient to check + * once on the first time through. + * + * Note: ideally we'd check typmod as well as typid, but that seems + * impractical at the moment: in many cases the tupdesc will have been + * generated by ExecTypeFromTL(), and that can't guarantee to generate an + * accurate typmod in all cases, because some expression node types don't + * carry typmod. Fortunately, for precisely that reason, there should be + * no places with a critical dependency on the typmod of a value. + * + * System attributes don't require checking since their types never + * change. + */ + if (attnum > 0) + { + TupleDesc slot_tupdesc = slot->tts_tupleDescriptor; + Form_pg_attribute attr; + + if (attnum > slot_tupdesc->natts) /* should never happen */ + elog(ERROR, "attribute number %d exceeds number of columns %d", + attnum, slot_tupdesc->natts); + + attr = TupleDescAttr(slot_tupdesc, attnum - 1); + + if (attr->attisdropped) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("attribute %d of type %s has been dropped", + attnum, format_type_be(slot_tupdesc->tdtypeid)))); + + if (vartype != attr->atttypid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("attribute %d of type %s has wrong type", + attnum, format_type_be(slot_tupdesc->tdtypeid)), + errdetail("Table has type %s, but query expects %s.", + format_type_be(attr->atttypid), + format_type_be(vartype)))); + } +} + +/* + * get_cached_rowtype: utility function to lookup a rowtype tupdesc + * + * type_id, typmod: identity of the rowtype + * cache_field: where to cache the TupleDesc pointer in expression state node + * (field must be initialized to NULL) + * econtext: expression context we are executing in + * + * NOTE: because the shutdown callback will be called during plan rescan, + * must be prepared to re-do this during any node execution; cannot call + * just once during expression initialization. + */ +static TupleDesc +get_cached_rowtype(Oid type_id, int32 typmod, + TupleDesc *cache_field, ExprContext *econtext) +{ + TupleDesc tupDesc = *cache_field; + + /* Do lookup if no cached value or if requested type changed */ + if (tupDesc == NULL || + type_id != tupDesc->tdtypeid || + typmod != tupDesc->tdtypmod) + { + tupDesc = lookup_rowtype_tupdesc(type_id, typmod); + + if (*cache_field) + { + /* Release old tupdesc; but callback is already registered */ + ReleaseTupleDesc(*cache_field); + } + else + { + /* Need to register shutdown callback to release tupdesc */ + RegisterExprContextCallback(econtext, + ShutdownTupleDescRef, + PointerGetDatum(cache_field)); + } + *cache_field = tupDesc; + } + return tupDesc; +} + +/* + * Callback function to release a tupdesc refcount at econtext shutdown + */ +static void +ShutdownTupleDescRef(Datum arg) +{ + TupleDesc *cache_field = (TupleDesc *) DatumGetPointer(arg); + + if (*cache_field) + ReleaseTupleDesc(*cache_field); + *cache_field = NULL; +} + +/* + * Fast-path functions, for very simple expressions + */ + +/* Simple reference to inner Var */ +static Datum +ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone) +{ + ExprEvalStep *op = &state->steps[1]; + int attnum = op->d.var.attnum + 1; + TupleTableSlot *slot = econtext->ecxt_innertuple; + + if (isDone != NULL) + *isDone = ExprSingleResult; + + /* See comments in ExecJustInnerVar */ + return tableam_tslot_getattr(slot, attnum, isnull); +} + +/* Simple reference to outer Var */ +static Datum +ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone) +{ + ExprEvalStep *op = &state->steps[1]; + int attnum = op->d.var.attnum + 1; + TupleTableSlot *slot = econtext->ecxt_outertuple; + + if (isDone != NULL) + *isDone = ExprSingleResult; + + /* See comments in ExecJustOuterVar */ + return tableam_tslot_getattr(slot, attnum, isnull); +} + +/* Simple reference to scan Var */ +static Datum +ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone) +{ + ExprEvalStep *op = &state->steps[1]; + int attnum = op->d.var.attnum + 1; + TupleTableSlot *slot = econtext->ecxt_scantuple; + + if (isDone != NULL) + *isDone = ExprSingleResult; + /* See comments in ExecJustScanVar */ + return tableam_tslot_getattr(slot, attnum, isnull); +} + +/* implementation of ExecJustAssign(Inner|Outer|Scan)Var */ +static inline Datum +ExecJustAssignVarImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull, ExprDoneCond* isDone) +{ + ExprEvalStep *op = &state->steps[1]; + int attnum = op->d.assign_var.attnum + 1; + int resultnum = op->d.assign_var.resultnum; + TupleTableSlot *outslot = state->resultslot; + + if (isDone != NULL) + *isDone = ExprSingleResult; + + /* + * We do not need CheckVarSlotCompatibility here; that was taken care of + * at compilation time. + * + * Since we use slot_getattr(), we don't need to implement the FETCHSOME + * step explicitly, and we also needn't Assert that the attnum is in range + * --- slot_getattr() will take care of any problems. Nonetheless, check + * that resultnum is in range. + */ + Assert(resultnum >= 0 && resultnum < outslot->tts_tupleDescriptor->natts); + outslot->tts_values[resultnum] = + tableam_tslot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]); + return 0; +} + +/* Simple Const expression */ +static Datum +ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone) +{ + ExprEvalStep *op = &state->steps[0]; + + if (isDone != NULL) + *isDone = ExprSingleResult; + + *isnull = op->d.constval.isnull; + + /* if a const cursor, copy cursor option data to econtext */ + if ((econtext->is_cursor || op->d.constval.is_cursor) && op->d.constval.con && + op->d.constval.con->consttype == REFCURSOROID) { + CopyCursorInfoData(&econtext->cursor_data, &op->d.constval.con->cursor_data); + econtext->dno = op->d.constval.con->cursor_data.cur_dno; + } + + return op->d.constval.value; +} + +/* Evaluate inner Var and assign to appropriate column of result tuple */ +static Datum +ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone) +{ + return ExecJustAssignVarImpl(state, econtext->ecxt_innertuple, isnull, isDone); +} + +/* Evaluate outer Var and assign to appropriate column of result tuple */ +static Datum +ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone) +{ + return ExecJustAssignVarImpl(state, econtext->ecxt_outertuple, isnull, isDone); +} + +/* Evaluate scan Var and assign to appropriate column of result tuple */ +static Datum +ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull, ExprDoneCond* isDone) +{ + return ExecJustAssignVarImpl(state, econtext->ecxt_scantuple, isnull, isDone); +} + +#if defined(EEO_USE_COMPUTED_GOTO) +/* + * Comparator used when building address->opcode lookup table for + * ExecEvalStepOp() in the threaded dispatch case. + */ +static int +dispatch_compare_ptr(const void *a, const void *b) +{ + const ExprEvalOpLookup *la = (const ExprEvalOpLookup *) a; + const ExprEvalOpLookup *lb = (const ExprEvalOpLookup *) b; + + if (la->opcode < lb->opcode) + return -1; + else if (la->opcode > lb->opcode) + return 1; + return 0; +} +#endif + +/* + * Do one-time initialization of interpretation machinery. + */ +static void +ExecInitInterpreter(void) +{ +#if defined(EEO_USE_COMPUTED_GOTO) + /* Set up externally-visible pointer to dispatch table */ + if (dispatch_table == NULL) + { + int i; + + dispatch_table = (const void **) + DatumGetPointer(ExecInterpExpr(NULL, NULL, NULL, NULL)); + + /* build reverse lookup table */ + for (i = 0; i < EEOP_LAST; i++) + { + reverse_dispatch_table[i].opcode = dispatch_table[i]; + reverse_dispatch_table[i].op = (ExprEvalOp) i; + } + + /* make it bsearch()able */ + qsort(reverse_dispatch_table, + EEOP_LAST /* nmembers */ , + sizeof(ExprEvalOpLookup), + dispatch_compare_ptr); + } +#endif +} + +/* + * Function to return the opcode of an expression step. + * + * When direct-threading is in use, ExprState->opcode isn't easily + * decipherable. This function returns the appropriate enum member. + */ +ExprEvalOp +ExecEvalStepOp(ExprState *state, ExprEvalStep *op) +{ +#if defined(EEO_USE_COMPUTED_GOTO) + if (state->flags & EEO_FLAG_DIRECT_THREADED) + { + ExprEvalOpLookup key; + ExprEvalOpLookup *res; + + key.opcode = (void *) op->opcode; + res = (ExprEvalOpLookup*)bsearch(&key, + reverse_dispatch_table, + EEOP_LAST /* nmembers */ , + sizeof(ExprEvalOpLookup), + dispatch_compare_ptr); + Assert(res); /* unknown ops shouldn't get looked up */ + return res->op; + } +#endif + return (ExprEvalOp) op->opcode; +} + + +/* + * Out-of-line helper functions for complex instructions. + */ + +/* + * Evaluate a PARAM_EXEC parameter. + * + * PARAM_EXEC params (internal executor parameters) are stored in the + * ecxt_param_exec_vals array, and can be accessed by array index. + */ +void +ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + ParamExecData *prm; + + prm = &(econtext->ecxt_param_exec_vals[op->d.param.paramid]); + if (unlikely(prm->execPlan != NULL)) + { + /* Parameter not evaluated yet, so go do it */ + ExecSetParamPlan((SubPlanState*)prm->execPlan, econtext); + /* ExecSetParamPlan should have processed this param... */ + Assert(prm->execPlan == NULL); + } + *op->resvalue = prm->value; + *op->resnull = prm->isnull; +} + +/* + * Evaluate a PARAM_EXTERN parameter. + * + * PARAM_EXTERN parameters must be sought in ecxt_param_list_info. + */ +void +ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + ParamListInfo paramInfo = econtext->ecxt_param_list_info; + int paramId = op->d.param.paramid; + + if (likely(paramInfo && + paramId > 0 && paramId <= paramInfo->numParams)) + { + ParamExternData *prm = ¶mInfo->params[paramId - 1]; + + /* give hook a chance in case parameter is dynamic */ + if (paramInfo->paramFetch != NULL) + (*paramInfo->paramFetch) (paramInfo, paramId); + + if (likely(OidIsValid(prm->ptype))) + { + /* safety check in case hook did something unexpected */ + if (unlikely(prm->ptype != op->d.param.paramtype)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)", + paramId, + format_type_be(prm->ptype), + format_type_be(op->d.param.paramtype)))); + *op->resvalue = prm->value; + *op->resnull = prm->isnull; + + /* copy cursor option from param to econtext */ + if ((econtext->is_cursor || op->d.param.is_cursor) && prm->ptype == REFCURSOROID) { + CopyCursorInfoData(&econtext->cursor_data, &prm->cursor_data); + econtext->dno = paramId - 1; + } + + return; + } + } + + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("no value found for parameter %d", paramId))); +} + +/* + * Raise error if a CURRENT OF expression is evaluated. + * + * The planner should convert CURRENT OF into a TidScan qualification, or some + * other special handling in a ForeignScan node. So we have to be able to do + * ExecInitExpr on a CurrentOfExpr, but we shouldn't ever actually execute it. + * If we get here, we suppose we must be dealing with CURRENT OF on a foreign + * table whose FDW doesn't handle it, and complain accordingly. + */ +void +ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_EXECUTOR), errmsg("CURRENT OF cannot be executed"))); + return; /* keep compiler quiet */ +} + +/* + * Evaluate NullTest / IS NULL for rows. + */ +void +ExecEvalRowNull(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + ExecEvalRowNullInt(state, op, econtext, true); +} + +/* + * Evaluate NullTest / IS NOT NULL for rows. + */ +void +ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + ExecEvalRowNullInt(state, op, econtext, false); +} + +static Datum CheckRowTypeIsNull(TupleDesc tupDesc, HeapTupleData tmptup, bool checkisnull) +{ + int att; + + for (att = 1; att <= tupDesc->natts; att++) { + /* ignore dropped columns */ + if (tupDesc->attrs[att - 1].attisdropped) + continue; + if (tableam_tops_tuple_attisnull(&tmptup, att, tupDesc)) { + /* null field disproves IS NOT NULL */ + if (!checkisnull) + return BoolGetDatum(false); + } else { + /* non-null field disproves IS NULL */ + if (checkisnull) + return BoolGetDatum(false); + } + } + + return BoolGetDatum(true); +} + +static Datum CheckRowTypeIsNullForAFormat(TupleDesc tupDesc, HeapTupleData tmptup, bool checkisnull) +{ + int att; + + for (att = 1; att <= tupDesc->natts; att++) { + /* ignore dropped columns */ + if (tupDesc->attrs[att - 1].attisdropped) + continue; + if (!tableam_tops_tuple_attisnull(&tmptup, att, tupDesc)) { + /* non-null field disproves IS NULL */ + if (checkisnull) { + return BoolGetDatum(false); + } else { + return BoolGetDatum(true); + } + } + } + + /* non-null field disproves IS NULL */ + if (checkisnull) { + return BoolGetDatum(true); + } else { + return BoolGetDatum(false); + } +} + +/* Common code for IS [NOT] NULL on a row value */ +static void +ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, + ExprContext *econtext, bool checkisnull) +{ + Datum value = *op->resvalue; + bool isnull = *op->resnull; + HeapTupleHeader tuple; + Oid tupType; + int32 tupTypmod; + TupleDesc tupDesc; + HeapTupleData tmptup; + int att; + + *op->resnull = false; + + /* NULL row variables are treated just as NULL scalar columns */ + if (isnull) + { + *op->resvalue = BoolGetDatum(checkisnull); + return; + } + + /* + * The SQL standard defines IS [NOT] NULL for a non-null rowtype argument + * as: + * + * "R IS NULL" is true if every field is the null value. + * + * "R IS NOT NULL" is true if no field is the null value. + * + * This definition is (apparently intentionally) not recursive; so our + * tests on the fields are primitive attisnull tests, not recursive checks + * to see if they are all-nulls or no-nulls rowtypes. + * + * The standard does not consider the possibility of zero-field rows, but + * here we consider them to vacuously satisfy both predicates. + */ + + tuple = DatumGetHeapTupleHeader(value); + + tupType = HeapTupleHeaderGetTypeId(tuple); + tupTypmod = HeapTupleHeaderGetTypMod(tuple); + + /* Lookup tupdesc if first time through or if type changes */ + tupDesc = get_cached_rowtype(tupType, tupTypmod, + &op->d.nulltest_row.argdesc, + econtext); + + /* + * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader. + */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); + tmptup.t_data = tuple; + + if (AFORMAT_NULL_TEST_MODE) + *op->resvalue = CheckRowTypeIsNullForAFormat(tupDesc, tmptup, checkisnull); + else + *op->resvalue = CheckRowTypeIsNull(tupDesc, tmptup, checkisnull); +} + +/* + * Evaluate an ARRAY[] expression. + * + * The individual array elements (or subarrays) have already been evaluated + * into op->d.arrayexpr.elemvalues[]/elemnulls[]. + */ +void +ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op) +{ + ArrayType *result; + Oid element_type = op->d.arrayexpr.elemtype; + int nelems = op->d.arrayexpr.nelems; + int ndims = 0; + int dims[MAXDIM]; + int lbs[MAXDIM]; + + /* Set non-null as default */ + *op->resnull = false; + + if (!op->d.arrayexpr.multidims) + { + /* Elements are presumably of scalar type */ + Datum *dvalues = op->d.arrayexpr.elemvalues; + bool *dnulls = op->d.arrayexpr.elemnulls; + + /* Shouldn't happen here, but if length is 0, return empty array */ + if (nelems == 0) { + *op->resvalue = PointerGetDatum(construct_empty_array(element_type)); + return; + } + + /* setup for 1-D array of the given length */ + ndims = 1; + dims[0] = nelems; + lbs[0] = 1; + + result = construct_md_array(dvalues, dnulls, ndims, dims, lbs, + element_type, + op->d.arrayexpr.elemlength, + op->d.arrayexpr.elembyval, + op->d.arrayexpr.elemalign); + } + else + { + /* Must be nested array expressions */ + int nbytes = 0; + int nitems = 0; + int outer_nelems = 0; + int elem_ndims = 0; + int *elem_dims = NULL; + int *elem_lbs = NULL; + bool firstone = true; + bool havenulls = false; + bool haveempty = false; + char **subdata; + bits8 **subbitmaps; + int *subbytes; + int *subnitems; + int32 dataoffset; + char *dat; + int iitem; + int elemoff; + int i; + + subdata = (char **) palloc(nelems * sizeof(char *)); + subbitmaps = (bits8 **) palloc(nelems * sizeof(bits8 *)); + subbytes = (int *) palloc(nelems * sizeof(int)); + subnitems = (int *) palloc(nelems * sizeof(int)); + + /* loop through and get data area from each element */ + for (elemoff = 0; elemoff < nelems; elemoff++) + { + Datum arraydatum; + bool eisnull; + ArrayType *array; + int this_ndims; + + arraydatum = op->d.arrayexpr.elemvalues[elemoff]; + eisnull = op->d.arrayexpr.elemnulls[elemoff]; + + /* temporarily ignore null subarrays */ + if (eisnull) + { + haveempty = true; + continue; + } + + array = DatumGetArrayTypeP(arraydatum); + + /* run-time double-check on element type */ + if (element_type != ARR_ELEMTYPE(array)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot merge incompatible arrays"), + errdetail("Array with element type %s cannot be " + "included in ARRAY construct with element type %s.", + format_type_be(ARR_ELEMTYPE(array)), + format_type_be(element_type)))); + + this_ndims = ARR_NDIM(array); + /* temporarily ignore zero-dimensional subarrays */ + if (this_ndims <= 0) + { + haveempty = true; + continue; + } + + if (firstone) + { + /* Get sub-array details from first member */ + elem_ndims = this_ndims; + ndims = elem_ndims + 1; + if (ndims <= 0 || ndims > MAXDIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("number of array dimensions (%d) exceeds " \ + "the maximum allowed (%d)", ndims, MAXDIM))); + + elem_dims = (int *) palloc(elem_ndims * sizeof(int)); + memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int)); + elem_lbs = (int *) palloc(elem_ndims * sizeof(int)); + memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int)); + + firstone = false; + } + else + { + /* Check other sub-arrays are compatible */ + if (elem_ndims != this_ndims || + memcmp(elem_dims, ARR_DIMS(array), + elem_ndims * sizeof(int)) != 0 || + memcmp(elem_lbs, ARR_LBOUND(array), + elem_ndims * sizeof(int)) != 0) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("multidimensional arrays must have array " + "expressions with matching dimensions"))); + } + + subdata[outer_nelems] = ARR_DATA_PTR(array); + subbitmaps[outer_nelems] = ARR_NULLBITMAP(array); + subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array); + nbytes += subbytes[outer_nelems]; + subnitems[outer_nelems] = ArrayGetNItems(this_ndims, + ARR_DIMS(array)); + nitems += subnitems[outer_nelems]; + havenulls |= ARR_HASNULL(array); + outer_nelems++; + } + + /* + * If all items were null or empty arrays, return an empty array; + * otherwise, if some were and some weren't, raise error. (Note: we + * must special-case this somehow to avoid trying to generate a 1-D + * array formed from empty arrays. It's not ideal...) + */ + if (haveempty) + { + if (ndims == 0) /* didn't find any nonempty array */ + { + *op->resvalue = PointerGetDatum(construct_empty_array(element_type)); + return; + } + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("multidimensional arrays must have array " + "expressions with matching dimensions"))); + } + + /* setup for multi-D array */ + dims[0] = outer_nelems; + lbs[0] = 1; + for (i = 1; i < ndims; i++) + { + dims[i] = elem_dims[i - 1]; + lbs[i] = elem_lbs[i - 1]; + } + + if (havenulls) + { + dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems); + nbytes += dataoffset; + } + else + { + dataoffset = 0; /* marker for no null bitmap */ + nbytes += ARR_OVERHEAD_NONULLS(ndims); + } + + result = (ArrayType *) palloc(nbytes); + SET_VARSIZE(result, nbytes); + result->ndim = ndims; + result->dataoffset = dataoffset; + result->elemtype = element_type; + memcpy(ARR_DIMS(result), dims, ndims * sizeof(int)); + memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int)); + + dat = ARR_DATA_PTR(result); + iitem = 0; + for (i = 0; i < outer_nelems; i++) + { + memcpy(dat, subdata[i], subbytes[i]); + dat += subbytes[i]; + if (havenulls) + array_bitmap_copy(ARR_NULLBITMAP(result), iitem, + subbitmaps[i], 0, + subnitems[i]); + iitem += subnitems[i]; + } + } + + *op->resvalue = PointerGetDatum(result); +} + +/* + * Evaluate an ArrayCoerceExpr expression. + * + * Source array is in step's result variable. + */ +void +ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op) +{ + ArrayCoerceExpr *acoerce = op->d.arraycoerce.coerceexpr; + Datum result; + ArrayType* array = NULL; + FunctionCallInfoData locfcinfo; + + /* NULL array -> NULL result */ + if (*op->resnull) + return; + + result = *op->resvalue; + + /* + * If it's binary-compatible, modify the element type in the array header, + * but otherwise leave the array as we received it. + */ + if (!OidIsValid(acoerce->elemfuncid)) + { + /* Detoast input array if necessary, and copy in any case */ + array = DatumGetArrayTypePCopy(result); + ARR_ELEMTYPE(array) = op->d.arraycoerce.resultelemtype; + *op->resvalue = PointerGetDatum(array); + return; + } + + /* Detoast input array if necessary, but don't make a useless copy */ + array = DatumGetArrayTypeP(result); + + /* + * Use array_map to apply the function to each array element. + * + * We pass on the desttypmod and isExplicit flags whether or not the + * function wants them. + * + * Note: coercion functions are assumed to not use collation. + */ + InitFunctionCallInfoData(locfcinfo, op->d.arraycoerce.elemfunc, 3, + InvalidOid, NULL, NULL); + locfcinfo.arg[0] = PointerGetDatum(array);; + locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod); + locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit); + locfcinfo.argnull[0] = false; + locfcinfo.argnull[1] = false; + locfcinfo.argnull[2] = false; + + *op->resvalue = array_map(&locfcinfo, ARR_ELEMTYPE(array), op->d.arraycoerce.resultelemtype, + op->d.arraycoerce.amstate); +} + +/* + * Evaluate a ROW() expression. + * + * The individual columns have already been evaluated into + * op->d.row.elemvalues[]/elemnulls[]. + */ +void +ExecEvalRow(ExprState *state, ExprEvalStep *op) +{ + HeapTuple tuple; + + /* build tuple from evaluated field values */ + tuple = (HeapTuple)tableam_tops_form_tuple(op->d.row.tupdesc, + op->d.row.elemvalues, + op->d.row.elemnulls); + + *op->resvalue = HeapTupleGetDatum(tuple); + *op->resnull = false; +} + +/* + * Evaluate GREATEST() or LEAST() expression (note this is *not* MIN()/MAX()). + * + * All of the to-be-compared expressions have already been evaluated into + * op->d.minmax.values[]/nulls[]. + */ +void ExecEvalMinMax(ExprState *state, ExprEvalStep *op) +{ + Datum *values = op->d.minmax.values; + bool *nulls = op->d.minmax.nulls; + FunctionCallInfo fcinfo = op->d.minmax.fcinfo_data; + MinMaxOp op_operator = op->d.minmax.op; + int off; + + /* set at initialization */ + Assert(fcinfo->argnull[0] == false); + Assert(fcinfo->argnull[1] == false); + + /* default to null result */ + *op->resnull = true; + + for (off = 0; off < op->d.minmax.nelems; off++) { + /* ignore NULL inputs */ + if (nulls[off]) { + continue; + } + + if (*op->resnull) { + /* first nonnull input, adopt value */ + *op->resvalue = values[off]; + *op->resnull = false; + } else { + int cmpresult; + + /* apply comparison function */ + fcinfo->arg[0] = *op->resvalue; + fcinfo->arg[1] = values[off]; + + fcinfo->isnull = false; + cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo)); + if (fcinfo->isnull) /* probably should not happen */ + continue; + + if (cmpresult > 0 && op_operator == IS_LEAST) + *op->resvalue = values[off]; + else if (cmpresult < 0 && op_operator == IS_GREATEST) + *op->resvalue = values[off]; + } + } +} + +/* + * Evaluate a FieldSelect node. + * + * Source record is in step's result variable. + */ +void +ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + AttrNumber fieldnum = op->d.fieldselect.fieldnum; + Datum tupDatum; + HeapTupleHeader tuple; + Oid tupType; + int32 tupTypmod; + TupleDesc tupDesc; + Form_pg_attribute attr; + HeapTupleData tmptup; + + /* NULL record -> NULL result */ + if (*op->resnull) + return; + + /* Get the composite datum and extract its type fields */ + tupDatum = *op->resvalue; + tuple = DatumGetHeapTupleHeader(tupDatum); + + tupType = HeapTupleHeaderGetTypeId(tuple); + tupTypmod = HeapTupleHeaderGetTypMod(tuple); + + /* Lookup tupdesc if first time through or if type changes */ + tupDesc = get_cached_rowtype(tupType, tupTypmod, + &op->d.fieldselect.argdesc, + econtext); + + /* + * Find field's attr record. Note we don't support system columns here: a + * datum tuple doesn't have valid values for most of the interesting + * system columns anyway. + */ + if (fieldnum <= 0) /* should never happen */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmodule(MOD_EXECUTOR), + errmsg("unsupported reference to system column %d in FieldSelect", fieldnum))); + if (fieldnum > tupDesc->natts) /* should never happen */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_ATTRIBUTE), + errmodule(MOD_EXECUTOR), + errmsg("attribute number %d exceeds number of columns %d", fieldnum, tupDesc->natts))); + attr = &tupDesc->attrs[fieldnum - 1]; + + /* Check for dropped column, and force a NULL result if so */ + if (attr->attisdropped) + { + *op->resnull = true; + return; + } + + /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */ + /* As in CheckVarSlotCompatibility, we should but can't check typmod */ + if (op->d.fieldselect.resulttype != attr->atttypid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("attribute %d has wrong type", fieldnum), + errdetail("Table has type %s, but query expects %s.", + format_type_be(attr->atttypid), + format_type_be(op->d.fieldselect.resulttype)))); + + /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); + tmptup.t_data = tuple; + + /* extract the field */ + *op->resvalue = tableam_tops_tuple_getattr(&tmptup, fieldnum, tupDesc, op->resnull); +} + +/* + * Deform source tuple, filling in the step's values/nulls arrays, before + * evaluating individual new values as part of a FieldStore expression. + * Subsequent steps will overwrite individual elements of the values/nulls + * arrays with the new field values, and then FIELDSTORE_FORM will build the + * new tuple value. + * + * Source record is in step's result variable. + */ +void +ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + TupleDesc tupDesc; + errno_t rc = EOK; + + /* Lookup tupdesc if first time through or after rescan */ + tupDesc = get_cached_rowtype(op->d.fieldstore.fstore->resulttype, -1, + op->d.fieldstore.argdesc, econtext); + + /* Check that current tupdesc doesn't have more fields than we allocated */ + if (unlikely(tupDesc->natts > op->d.fieldstore.ncolumns)) + elog(ERROR, "too many columns in composite type %u", + op->d.fieldstore.fstore->resulttype); + + if (!*op->resnull) { + /* + * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We + * set all the fields in the struct just in case. + */ + Datum tupDatum = *op->resvalue; + HeapTupleHeader tuphdr; + HeapTupleData tmptup; + + tuphdr = DatumGetHeapTupleHeader(tupDatum); + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr); + ItemPointerSetInvalid(&(tmptup.t_self)); + tmptup.t_tableOid = InvalidOid; + tmptup.t_bucketId = InvalidBktId; +#ifdef PGXC + tmptup.t_xc_node_id = 0; +#endif + HeapTupleSetZeroBase(&tmptup); + tmptup.t_data = tuphdr; + + tableam_tops_deform_tuple(&tmptup, tupDesc, op->d.fieldstore.values, op->d.fieldstore.nulls); + } else { + /* Convert null input tuple into an all-nulls row */ + rc = memset_s(op->d.fieldstore.nulls, op->d.fieldstore.ncolumns * sizeof(bool), + true, op->d.fieldstore.ncolumns * sizeof(bool)); + securec_check(rc, "\0", "\0"); + } +} + +/* + * Compute the new composite datum after each individual field value of a + * FieldStore expression has been evaluated. + */ +void +ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + HeapTuple tuple; + + /* argdesc should already be valid from the DeForm step */ + tuple = (HeapTuple)tableam_tops_form_tuple(*op->d.fieldstore.argdesc, + op->d.fieldstore.values, op->d.fieldstore.nulls); + + *op->resvalue = HeapTupleGetDatum(tuple); + *op->resnull = false; +} + +/* + * Process a subscript in an ArrayRef expression. + * + * If subscript is NULL, throw error in assignment case, or in fetch case + * set result to NULL and return false (instructing caller to skip the rest + * of the ArrayRef sequence). + * + * Subscript expression result is in subscriptvalue/subscriptnull. + * On success, integer subscript value has been saved in upperindex[] or + * lowerindex[] for use later. + */ +bool +ExecEvalArrayRefSubscript(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + ArrayRefState *arefstate = op->d.arrayref_subscript.state; + int *indexes; + int off = op->d.arrayref_subscript.off; + + ExecTableOfIndexInfo execTableOfIndexInfo; + initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext); + ExecEvalParamExternTableOfIndex((Node*)arefstate->refexpr, &execTableOfIndexInfo); + if (u_sess->SPI_cxt.cur_tableof_index != NULL) { + u_sess->SPI_cxt.cur_tableof_index->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; + u_sess->SPI_cxt.cur_tableof_index->tableOfIndex = execTableOfIndexInfo.tableOfIndex; + u_sess->SPI_cxt.cur_tableof_index->tableOfGetNestLayer = arefstate->refupperindexpr_count; + } + + /* Convert datum to int, save in appropriate place */ + if (op->d.arrayref_subscript.isupper) { + if (OidIsValid(execTableOfIndexInfo.tableOfIndexType) || execTableOfIndexInfo.isnestedtable) { + bool isTran = false; + PLpgSQL_execstate* old_estate = plpgsql_estate; + Datum exprValue = arefstate->subscriptvalue; + + plpgsql_estate = old_estate; + if (execTableOfIndexInfo.tableOfIndexType == VARCHAROID && !arefstate->subscriptnull && VARATT_IS_1B(exprValue)) { + exprValue = transVaratt1BTo4B(exprValue); + isTran = true; + } + TableOfIndexKey key; + PLpgSQL_var* node = NULL; + key.exprtypeid = execTableOfIndexInfo.tableOfIndexType; + key.exprdatum = exprValue; + int index = getTableOfIndexByDatumValue(key, execTableOfIndexInfo.tableOfIndex, &node); + if (isTran) { + pfree(DatumGetPointer(exprValue)); + } + if (execTableOfIndexInfo.isnestedtable) { + /* for nested table, we should take inner table's array and skip current indx */ + if (node == NULL || index == -1) { + arefstate->subscriptnull = true; + } else { + PLpgSQL_var* var = node; + execTableOfIndexInfo.isnestedtable = (var->nest_table != NULL); + *op->resvalue = var->value; + execTableOfIndexInfo.tableOfIndexType = var->datatype->tableOfIndexType; + execTableOfIndexInfo.tableOfIndex = var->tableOfIndex; + arefstate->subscriptnull = var->isnull; + if (plpgsql_estate) + plpgsql_estate->curr_nested_table_type = var->datatype->typoid; + return true; + } + } + if (index == -1) { + arefstate->subscriptnull = true; + } else { + arefstate->upperindex[off] = index; + } + } + else { + arefstate->upperindex[off] = DatumGetInt32(arefstate->subscriptvalue); + } + + arefstate->plpgsql_index++; + + /* If any index expr yields NULL, result is NULL or error */ + if (arefstate->subscriptnull) { + if (arefstate->isassignment) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("array subscript in assignment must not be null"))); + *op->resnull = true; + return (Datum)NULL; + } + } + else { + if (execTableOfIndexInfo.tableOfIndexType == VARCHAROID || execTableOfIndexInfo.isnestedtable) { + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("index by varchar or nested table don't support two subscripts"))); + } else { + arefstate->lowerindex[off] = DatumGetInt32(arefstate->subscriptvalue); + } + + /* If any index expr yields NULL, result is NULL or error */ + if (arefstate->subscriptnull) + { + if (arefstate->isassignment) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("array subscript in assignment must not be null"))); + *op->resnull = true; + return false; + } + } + + return true; +} + +/* + * Evaluate ArrayRef fetch. + * + * Source array is in step's result variable. + */ +void +ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op) +{ + ArrayRefState *arefstate = op->d.arrayref.state; + ArrayType* array_source = (ArrayType*)DatumGetPointer(*op->resvalue); + /* Should not get here if source array (or any subscript) is null */ + Assert(!(*op->resnull)); + + /* for nested table, if get inner table's elem, need cover elem type */ + if (arefstate->refupperindexpr_count > arefstate->plpgsql_index && + arefstate->plpgsql_index > 0 && plpgsql_estate) { + if (plpgsql_estate->curr_nested_table_type != arefstate->typOid) { + plpgsql_estate->curr_nested_table_type = ARR_ELEMTYPE(array_source); + get_typlenbyvalalign(plpgsql_estate->curr_nested_table_type, + &arefstate->refelemlength, + &arefstate->refelembyval, + &arefstate->refelemalign); + } + } + + if (arefstate->numlower == 0) + { + /* Scalar case */ + *op->resvalue = (Datum)array_ref(array_source, + arefstate->numupper, + arefstate->upperindex, + arefstate->refattrlength, + arefstate->refelemlength, + arefstate->refelembyval, + arefstate->refelemalign, + op->resnull); + } + else + { + /* Slice case */ + *op->resvalue = (Datum)array_get_slice(array_source, + arefstate->numupper, + arefstate->upperindex, + arefstate->lowerindex, + arefstate->refattrlength, + arefstate->refelemlength, + arefstate->refelembyval, + arefstate->refelemalign); + } +} + +/* + * Compute old array element/slice value for an ArrayRef assignment + * expression. Will only be generated if the new-value subexpression + * contains ArrayRef or FieldStore. The value is stored into the + * ArrayRefState's prevvalue/prevnull fields. + */ +void +ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op) +{ + ArrayRefState *arefstate = op->d.arrayref.state; + ArrayType* array_source = (ArrayType*)DatumGetPointer(*op->resvalue); + + if (*op->resnull) + { + /* whole array is null, so any element or slice is too */ + arefstate->prevvalue = (Datum) 0; + arefstate->prevnull = true; + } + else if (arefstate->numlower == 0) + { + /* Scalar case */ + arefstate->prevvalue = (Datum)array_ref(array_source, + arefstate->numupper, + arefstate->upperindex, + arefstate->refattrlength, + arefstate->refelemlength, + arefstate->refelembyval, + arefstate->refelemalign, + &arefstate->prevnull); + } + else + { + /* Slice case */ + /* this is currently unreachable */ + arefstate->prevvalue = (Datum)array_get_slice(array_source, + arefstate->numupper, + arefstate->upperindex, + arefstate->lowerindex, + arefstate->refattrlength, + arefstate->refelemlength, + arefstate->refelembyval, + arefstate->refelemalign); + arefstate->prevnull = false; + } +} + +/* + * Evaluate ArrayRef assignment. + * + * Input array (possibly null) is in result area, replacement value is in + * ArrayRefState's replacevalue/replacenull. + */ +void +ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op) +{ + ArrayRefState *arefstate = op->d.arrayref.state; + ArrayType* array_source = (ArrayType*)DatumGetPointer(*op->resvalue); + + /* + * For an assignment to a fixed-length array type, both the original array + * and the value to be assigned into it must be non-NULL, else we punt and + * return the original array. + */ + if (arefstate->refattrlength > 0) /* fixed-length array? */ + { + if (*op->resnull || arefstate->replacenull) + return; + } + + /* + * For assignment to varlena arrays, we handle a NULL original array by + * substituting an empty (zero-dimensional) array; insertion of the new + * element will result in a singleton array value. It does not matter + * whether the new element is NULL. + */ + if (*op->resnull) + { + array_source = construct_empty_array(arefstate->refelemtype); + *op->resnull = false; + } + + if (arefstate->numlower == 0) + { + /* Scalar case */ + *op->resvalue = (Datum)array_set(array_source, + arefstate->numupper, + arefstate->upperindex, + arefstate->replacevalue, + arefstate->replacenull, + arefstate->refattrlength, + arefstate->refelemlength, + arefstate->refelembyval, + arefstate->refelemalign); + } + else + { + /* Slice case */ + *op->resvalue = (Datum)array_set_slice(array_source, + arefstate->numupper, + arefstate->upperindex, + arefstate->lowerindex, + (ArrayType*)DatumGetPointer(arefstate->replacevalue), + arefstate->replacenull, + arefstate->refattrlength, + arefstate->refelemlength, + arefstate->refelembyval, + arefstate->refelemalign); + } +} + +/* + * Evaluate a rowtype coercion operation. + * This may require rearranging field positions. + * + * Source record is in step's result variable. + */ +void +ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + ConvertRowtypeExpr *convert = op->d.convert_rowtype.convert; + HeapTuple result; + Datum tupDatum; + HeapTupleHeader tuple; + HeapTupleData tmptup; + TupleDesc indesc, + outdesc; + + /* NULL in -> NULL out */ + if (*op->resnull) + return; + + tupDatum = *op->resvalue; + tuple = DatumGetHeapTupleHeader(tupDatum); + + /* Lookup tupdescs if first time through or after rescan */ + if (op->d.convert_rowtype.indesc == NULL) + { + get_cached_rowtype(exprType((Node *) convert->arg), -1, + &op->d.convert_rowtype.indesc, + econtext); + op->d.convert_rowtype.initialized = false; + } + if (op->d.convert_rowtype.outdesc == NULL) + { + get_cached_rowtype(convert->resulttype, -1, + &op->d.convert_rowtype.outdesc, + econtext); + op->d.convert_rowtype.initialized = false; + } + + indesc = op->d.convert_rowtype.indesc; + outdesc = op->d.convert_rowtype.outdesc; + + /* + * We used to be able to assert that incoming tuples are marked with + * exactly the rowtype of indesc. However, now that ExecEvalWholeRowVar + * might change the tuples' marking to plain RECORD due to inserting + * aliases, we can only make this weak test: + */ + Assert(HeapTupleHeaderGetTypeId(tuple) == indesc->tdtypeid || + HeapTupleHeaderGetTypeId(tuple) == RECORDOID); + + /* if first time through, initialize conversion map */ + if (!op->d.convert_rowtype.initialized) + { + MemoryContext old_cxt; + + /* allocate map in long-lived memory context */ + old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + + /* prepare map from old to new attribute numbers */ + op->d.convert_rowtype.map = + convert_tuples_by_name(indesc, outdesc, + gettext_noop("could not convert row type")); + op->d.convert_rowtype.initialized = true; + + MemoryContextSwitchTo(old_cxt); + } + + /* + * No-op if no conversion needed (not clear this can happen here). + */ + if (op->d.convert_rowtype.map == NULL) + return; + + /* + * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader. + */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); + tmptup.t_data = tuple; + + result = do_convert_tuple(&tmptup, op->d.convert_rowtype.map); + + *op->resvalue = HeapTupleGetDatum(result); +} + +/* + * Evaluate "scalar op ANY/ALL (array)". + * + * Source array is in our result area, scalar arg is already evaluated into + * fcinfo->arg[0]/argnull[0]. + * + * The operator always yields boolean, and we combine the results across all + * array elements using OR and AND (for ANY and ALL respectively). Of course + * we short-circuit as soon as the result is known. + */ +void +ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op) +{ + FunctionCallInfo fcinfo = op->d.scalararrayop.fcinfo_data; + bool useOr = op->d.scalararrayop.useOr; + bool strictfunc = op->d.scalararrayop.finfo->fn_strict; + ArrayType *arr; + int nitems; + Datum result; + bool resultnull; + int16 typlen; + bool typbyval; + char typalign; + char *s; + bits8 *bitmap; + int bitmask; + + /* + * If the array is NULL then we return NULL --- it's not very meaningful + * to do anything else, even if the operator isn't strict. + */ + if (*op->resnull) + return; + + /* Else okay to fetch and detoast the array */ + arr = DatumGetArrayTypeP(*op->resvalue); + + /* + * If the array is empty, we return either FALSE or TRUE per the useOr + * flag. This is correct even if the scalar is NULL; since we would + * evaluate the operator zero times, it matters not whether it would want + * to return NULL. + */ + nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); + if (nitems <= 0) + { + *op->resvalue = BoolGetDatum(!useOr); + *op->resnull = false; + return; + } + + /* + * If the scalar is NULL, and the function is strict, return NULL; no + * point in iterating the loop. + */ + if (fcinfo->argnull[0] && strictfunc) + { + *op->resnull = true; + return; + } + + /* + * We arrange to look up info about the element type only once per series + * of calls, assuming the element type doesn't change underneath us. + */ + if (op->d.scalararrayop.element_type != ARR_ELEMTYPE(arr)) + { + get_typlenbyvalalign(ARR_ELEMTYPE(arr), + &op->d.scalararrayop.typlen, + &op->d.scalararrayop.typbyval, + &op->d.scalararrayop.typalign); + op->d.scalararrayop.element_type = ARR_ELEMTYPE(arr); + } + + typlen = op->d.scalararrayop.typlen; + typbyval = op->d.scalararrayop.typbyval; + typalign = op->d.scalararrayop.typalign; + + /* Initialize result appropriately depending on useOr */ + result = BoolGetDatum(!useOr); + resultnull = false; + + /* Loop over the array elements */ + s = (char *) ARR_DATA_PTR(arr); + bitmap = ARR_NULLBITMAP(arr); + bitmask = 1; + + for (int i = 0; i < nitems; i++) + { + Datum elt; + Datum thisresult; + + /* Get array element, checking for NULL */ + if (bitmap && (*bitmap & bitmask) == 0) + { + fcinfo->arg[1] = (Datum) 0; + fcinfo->argnull[1] = true; + } + else + { + elt = fetch_att(s, typbyval, typlen); + s = att_addlength_pointer(s, typlen, s); + s = (char *) att_align_nominal(s, typalign); + fcinfo->arg[1] = elt; + fcinfo->argnull[1] = false; + } + + /* Call comparison function */ + if (fcinfo->argnull[1] && strictfunc) + { + fcinfo->isnull = true; + thisresult = (Datum) 0; + } + else + { + fcinfo->isnull = false; + thisresult = op->d.scalararrayop.fn_addr(fcinfo); + } + + /* Combine results per OR or AND semantics */ + if (fcinfo->isnull) + resultnull = true; + else if (useOr) + { + if (DatumGetBool(thisresult)) + { + result = BoolGetDatum(true); + resultnull = false; + break; /* needn't look at any more elements */ + } + } + else + { + if (!DatumGetBool(thisresult)) + { + result = BoolGetDatum(false); + resultnull = false; + break; /* needn't look at any more elements */ + } + } + + /* advance bitmap pointer if any */ + if (bitmap) + { + bitmask <<= 1; + if (bitmask == 0x100) + { + bitmap++; + bitmask = 1; + } + } + } + + *op->resvalue = result; + *op->resnull = resultnull; +} + +/* + * Evaluate a NOT NULL domain constraint. + */ +void +ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op) +{ + if (*op->resnull) + ereport(ERROR, + (errcode(ERRCODE_NOT_NULL_VIOLATION), + errmsg("domain %s does not allow null values", format_type_be(op->d.domaincheck.resulttype)))); +} + +/* + * Evaluate a CHECK domain constraint. + */ +void +ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op) +{ + if (!*op->d.domaincheck.checknull && + !DatumGetBool(*op->d.domaincheck.checkvalue)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("value for domain %s violates check constraint \"%s\"", + format_type_be(op->d.domaincheck.resulttype), + op->d.domaincheck.constraintname))); +} + +/* + * Evaluate the various forms of XmlExpr. + * + * Arguments have been evaluated into named_argvalue/named_argnull + * and/or argvalue/argnull arrays. + */ +void +ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op) +{ + XmlExpr *xexpr = op->d.xmlexpr.xexpr; + Datum value; + int i; + + *op->resnull = true; /* until we get a result */ + *op->resvalue = (Datum) 0; + + switch (xexpr->op) + { + case IS_XMLCONCAT: + { + Datum *argvalue = op->d.xmlexpr.argvalue; + bool *argnull = op->d.xmlexpr.argnull; + List *values = NIL; + + for (i = 0; i < list_length(xexpr->args); i++) + { + if (!argnull[i]) + values = lappend(values, DatumGetPointer(argvalue[i])); + } + + if (values != NIL) + { + *op->resvalue = PointerGetDatum(xmlconcat(values)); + *op->resnull = false; + } + } + break; + + case IS_XMLFOREST: + { + Datum *argvalue = op->d.xmlexpr.named_argvalue; + bool *argnull = op->d.xmlexpr.named_argnull; + StringInfoData buf; + ListCell *lc; + ListCell *lc2; + + initStringInfo(&buf); + + i = 0; + forboth(lc, xexpr->named_args, lc2, xexpr->arg_names) + { + Expr *e = (Expr *) lfirst(lc); + char *argname = strVal(lfirst(lc2)); + + if (!argnull[i]) + { + value = argvalue[i]; + appendStringInfo(&buf, "<%s>%s", + argname, + map_sql_value_to_xml_value(value, + exprType((Node *) e), true), + argname); + *op->resnull = false; + } + i++; + } + + if (!*op->resnull) + { + text *result; + + result = cstring_to_text_with_len(buf.data, buf.len); + *op->resvalue = PointerGetDatum(result); + } + + pfree(buf.data); + } + break; + + case IS_XMLELEMENT: + *op->resvalue = PointerGetDatum(xmlelementByFlatten(xexpr, + op->d.xmlexpr.named_argvalue, + op->d.xmlexpr.named_argnull, + op->d.xmlexpr.argvalue, + op->d.xmlexpr.argnull)); + *op->resnull = false; + break; + + case IS_XMLPARSE: + { + Datum *argvalue = op->d.xmlexpr.argvalue; + bool *argnull = op->d.xmlexpr.argnull; + text *data; + bool preserve_whitespace; + + /* arguments are known to be text, bool */ + Assert(list_length(xexpr->args) == 2); + + if (argnull[0]) + return; + value = argvalue[0]; + data = DatumGetTextPP(value); + + if (argnull[1]) /* probably can't happen */ + return; + value = argvalue[1]; + preserve_whitespace = DatumGetBool(value); + + *op->resvalue = PointerGetDatum(xmlparse(data, + xexpr->xmloption, + preserve_whitespace)); + *op->resnull = false; + } + break; + + case IS_XMLPI: + { + text *arg; + bool isnull; + + /* optional argument is known to be text */ + Assert(list_length(xexpr->args) <= 1); + + if (xexpr->args) + { + isnull = op->d.xmlexpr.argnull[0]; + if (isnull) + arg = NULL; + else + arg = DatumGetTextPP(op->d.xmlexpr.argvalue[0]); + } + else + { + arg = NULL; + isnull = false; + } + + *op->resvalue = PointerGetDatum(xmlpi(xexpr->name, + arg, + isnull, + op->resnull)); + } + break; + + case IS_XMLROOT: + { + Datum *argvalue = op->d.xmlexpr.argvalue; + bool *argnull = op->d.xmlexpr.argnull; + xmltype *data; + text *version; + int standalone; + + /* arguments are known to be xml, text, int */ + Assert(list_length(xexpr->args) == 3); + + if (argnull[0]) + return; + data = DatumGetXmlP(argvalue[0]); + + if (argnull[1]) + version = NULL; + else + version = DatumGetTextPP(argvalue[1]); + + Assert(!argnull[2]); /* always present */ + standalone = DatumGetInt32(argvalue[2]); + + *op->resvalue = PointerGetDatum(xmlroot(data, + version, + standalone)); + *op->resnull = false; + } + break; + + case IS_XMLSERIALIZE: + { + Datum *argvalue = op->d.xmlexpr.argvalue; + bool *argnull = op->d.xmlexpr.argnull; + + /* argument type is known to be xml */ + Assert(list_length(xexpr->args) == 1); + + if (argnull[0]) + return; + value = argvalue[0]; + + *op->resvalue = PointerGetDatum( + xmltotext_with_xmloption(DatumGetXmlP(value), + xexpr->xmloption)); + *op->resnull = false; + } + break; + + case IS_DOCUMENT: + { + Datum *argvalue = op->d.xmlexpr.argvalue; + bool *argnull = op->d.xmlexpr.argnull; + + /* optional argument is known to be xml */ + Assert(list_length(xexpr->args) == 1); + + if (argnull[0]) + return; + value = argvalue[0]; + + *op->resvalue = + BoolGetDatum(xml_is_document(DatumGetXmlP(value))); + *op->resnull = false; + } + break; + + default: + elog(ERROR, "unrecognized XML operation"); + break; + } +} + +/* + * ExecEvalGroupingFunc + * + * Computes a bitmask with a bit for each (unevaluated) argument expression + * (rightmost arg is least significant bit). + * + * A bit is set if the corresponding expression is NOT part of the set of + * grouping expressions in the current grouping set. + */ +void +ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op) +{ + AggState *aggstate = castNode(AggState, state->parent); + int result = 0; + Bitmapset *grouped_cols = aggstate->grouped_cols; + ListCell *lc; + + foreach(lc, op->d.grouping_func.clauses) + { + int attnum = lfirst_int(lc); + + result <<= 1; + + if (!bms_is_member(attnum, grouped_cols)) + result |= 1; + } + + *op->resvalue = Int32GetDatum(result); + *op->resnull = false; +} + +/* + * Hand off evaluation of a subplan to nodeSubplan.c + */ +void +ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + SubPlanState *sstate = op->d.subplan.sstate; + + /* could potentially be nested, so make sure there's enough stack */ + check_stack_depth(); + + *op->resvalue = ExecSubPlan(sstate, econtext, op->resnull); +} + +/* + * Hand off evaluation of an alternative subplan to nodeSubplan.c + */ +void +ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + AlternativeSubPlanState *asstate = op->d.alternative_subplan.asstate; + + /* could potentially be nested, so make sure there's enough stack */ + check_stack_depth(); + + *op->resvalue = ExecAlternativeSubPlan(asstate, econtext, op->resnull); +} + +void +ExecEvalHashFilter(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + Datum result = 0; + Datum value = 0; + uint64 hashValue = 0; + int modulo = 0; + int nodeIndex = 0; + ListCell *vartypes = NULL; + bool isFirst = true; + bool hasNonNullValue = false; + int offset = 0; + + *op->resnull = true; + + /* Get every distribute key in arg and compute hash value */ + foreach(vartypes, op->d.hash_filter.typeOids) + { + Oid vartype = (Oid)lfirst_oid(vartypes); + + value = op->d.hash_filter.argvalue[offset]; + + int null_value_dn_index = (op->d.hash_filter.nodelist != NULL) ? op->d.hash_filter.nodelist[0] + : /* fetch first dn in group's dn list */ + 0; /* fetch first dn index */ + + if (op->d.hash_filter.argnull[offset]) { + if (null_value_dn_index == u_sess->pgxc_cxt.PGXCNodeId) { + *op->resnull = false; + result = BoolGetDatum(true); + } else + result = BoolGetDatum(false); + } else { + if (isFirst) { + hashValue = compute_hash(vartype, value, LOCATOR_TYPE_HASH); + isFirst = false; + } else { + hashValue = (hashValue << 1) | ((hashValue & 0x80000000) ? 1 : 0); + hashValue ^= compute_hash(vartype, value, LOCATOR_TYPE_HASH); + } + + hasNonNullValue = true; + } + + offset++; + } + + /* If has non null value, it should get nodeId and deside if need filter the value or not. */ + if (hasNonNullValue) { + modulo = op->d.hash_filter.bucketMap[abs((int)hashValue) & (op->d.hash_filter.bucketCnt - 1)]; + nodeIndex = op->d.hash_filter.nodelist[modulo]; + + /* If there are null value and non null value, and the last value in distkey is null, + we should set isNull is false. */ + *op->resnull = false; + /* Look into the handles and return correct position in array */ + if (nodeIndex == u_sess->pgxc_cxt.PGXCNodeId) + *op->resvalue = BoolGetDatum(true); + else + *op->resvalue = BoolGetDatum(false); + } else /* If all the value is null, return result. */ + *op->resvalue = result; +} + +/* + * Evaluate a wholerow Var expression. + * + * Returns a Datum whose value is the value of a whole-row range variable + * with respect to given expression context. + */ +void +ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext) +{ + Var *variable = op->d.wholerow.var; + TupleTableSlot *slot; + TupleDesc output_tupdesc; + MemoryContext oldcontext; + HeapTupleHeader dtuple; + HeapTuple tuple; + errno_t rc = EOK; + + /* This was checked by ExecInitExpr */ + Assert(variable->varattno == InvalidAttrNumber); + + /* Get the input slot we want */ + switch (variable->varno) + { + case INNER_VAR: + /* get the tuple from the inner node */ + slot = econtext->ecxt_innertuple; + break; + + case OUTER_VAR: + /* get the tuple from the outer node */ + slot = econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: + /* get the tuple from the relation being scanned */ + slot = econtext->ecxt_scantuple; + break; + } + + /* Apply the junkfilter if any */ + if (op->d.wholerow.junkFilter != NULL) + slot = ExecFilterJunk(op->d.wholerow.junkFilter, slot); + + /* + * If first time through, obtain tuple descriptor and check compatibility. + * + * XXX: It'd be great if this could be moved to the expression + * initialization phase, but due to using slots that's currently not + * feasible. + */ + if (op->d.wholerow.first) + { + /* optimistically assume we don't need slow path */ + op->d.wholerow.slow = false; + + /* + * If it's a RECORD Var, we'll use the slot's type ID info. It's likely + * that the slot's type is also RECORD; if so, make sure it's been + * "blessed", so that the Datum can be interpreted later. + * + * If the Var identifies a named composite type, we must check that the + * actual tuple type is compatible with it. + */ + if (variable->vartype != RECORDOID) { + TupleDesc var_tupdesc; + TupleDesc slot_tupdesc; + int i; + + /* + * We really only care about numbers of attributes and data types. + * Also, we can ignore type mismatch on columns that are dropped in + * the destination type, so long as (1) the physical storage matches + * or (2) the actual column value is NULL. Case (1) is helpful in + * some cases involving out-of-date cached plans, while case (2) is + * expected behavior in situations such as an INSERT into a table with + * dropped columns (the planner typically generates an INT4 NULL + * regardless of the dropped column type). If we find a dropped + * column and cannot verify that case (1) holds, we have to use + * ExecEvalWholeRowSlow to check (2) for each row. + */ + var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); + + slot_tupdesc = slot->tts_tupleDescriptor; + + if (var_tupdesc->natts != slot_tupdesc->natts) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("table row type and query-specified row type do not match"), + errdetail_plural("Table row contains %d attribute, but query expects %d.", + "Table row contains %d attributes, but query expects %d.", + slot_tupdesc->natts, + slot_tupdesc->natts, + var_tupdesc->natts))); + + for (i = 0; i < var_tupdesc->natts; i++) { + Form_pg_attribute vattr = &var_tupdesc->attrs[i]; + Form_pg_attribute sattr = &slot_tupdesc->attrs[i]; + + if (vattr->atttypid == sattr->atttypid) + continue; /* no worries */ + if (!vattr->attisdropped) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("table row type and query-specified row type do not match"), + errdetail("Table has type %s at ordinal position %d, but query expects %s.", + format_type_be(sattr->atttypid), + i + 1, + format_type_be(vattr->atttypid)))); + + if (vattr->attlen != sattr->attlen || vattr->attalign != sattr->attalign) + op->d.wholerow.slow = true; /* need to check for nulls */ + } + + /* + * Use the variable's declared rowtype as the descriptor for the + * output values, modulo possibly assigning new column names + * below. In particular, we *must* absorb any attisdropped + * markings. + */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + output_tupdesc = CreateTupleDescCopy(var_tupdesc); + MemoryContextSwitchTo(oldcontext); + + ReleaseTupleDesc(var_tupdesc); + } + else { + /* + * In the RECORD case, we use the input slot's rowtype as the + * descriptor for the output values, modulo possibly assigning new + * column names below. + */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + output_tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor); + MemoryContextSwitchTo(oldcontext); + } + + /* + * Construct a tuple descriptor for the composite values we'll + * produce, and make sure its record type is "blessed". The main + * reason to do this is to be sure that operations such as + * row_to_json() will see the desired column names when they look up + * the descriptor from the type information embedded in the composite + * values. + * + * We already got the correct physical datatype info above, but now we + * should try to find the source RTE and adopt its column aliases, in + * case they are different from the original rowtype's names. For + * example, in "SELECT foo(t) FROM tab t(x,y)", the first two columns + * in the composite output should be named "x" and "y" regardless of + * tab's column names. + * + * If we can't locate the RTE, assume the column names we've got are + * OK. (As of this writing, the only cases where we can't locate the + * RTE are in execution of trigger WHEN clauses, and then the Var will + * have the trigger's relation's rowtype, so its names are fine.) + * Also, if the creator of the RTE didn't bother to fill in an eref + * field, assume our column names are OK. (This happens in COPY, and + * perhaps other places.) + */ + if (econtext->ecxt_estate && + variable->varno <= list_length(econtext->ecxt_estate->es_range_table)) + { + RangeTblEntry *rte = rt_fetch(variable->varno, + econtext->ecxt_estate->es_range_table); + } + + /* Bless the tupdesc if needed, and save it in the execution state */ + op->d.wholerow.tupdesc = BlessTupleDesc(output_tupdesc); + + op->d.wholerow.first = false; + } + + /* + * Make sure all columns of the slot are accessible in the slot's + * Datum/isnull arrays. + */ + tableam_tslot_getallattrs(slot); + + if (op->d.wholerow.slow) + { + /* Check to see if any dropped attributes are non-null */ + TupleDesc tupleDesc = slot->tts_tupleDescriptor; + TupleDesc var_tupdesc = op->d.wholerow.tupdesc; + int i; + + Assert(var_tupdesc->natts == tupleDesc->natts); + + for (i = 0; i < var_tupdesc->natts; i++) + { + Form_pg_attribute vattr = &var_tupdesc->attrs[i]; + Form_pg_attribute sattr = &tupleDesc->attrs[i]; + + if (!vattr->attisdropped) + continue; /* already checked non-dropped cols */ + if (slot->tts_isnull[i]) + continue; /* null is always okay */ + if (vattr->attlen != sattr->attlen || + vattr->attalign != sattr->attalign) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("table row type and query-specified row type do not match"), + errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.", + i + 1))); + } + } + + /* + * Copy the slot tuple and make sure any toasted fields get detoasted. + * + * (The intermediate copy is a tad annoying here, but we currently have no + * primitive that will do the right thing. Note it is critical that we + * not change the slot's state, so we can't use ExecFetchSlotTupleDatum.) + */ + tuple = ExecCopySlotTuple(slot); + + /* + * We have to make a copy of the tuple so we can safely insert the Datum + * overhead fields, which are not set in on-disk tuples. + */ + dtuple = (HeapTupleHeader)palloc(tuple->t_len); + rc = memcpy_s((char*)dtuple, tuple->t_len, (char*)tuple->t_data, tuple->t_len); + securec_check(rc, "\0", "\0"); + + HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len); + HeapTupleHeaderSetTypeId(dtuple, slot->tts_tupleDescriptor->tdtypeid); + HeapTupleHeaderSetTypMod(dtuple, slot->tts_tupleDescriptor->tdtypmod); + + heap_freetuple(tuple); + + /* + * Label the datum with the composite type info we identified before. + */ + HeapTupleHeaderSetTypeId(dtuple, op->d.wholerow.tupdesc->tdtypeid); + HeapTupleHeaderSetTypMod(dtuple, op->d.wholerow.tupdesc->tdtypmod); + + *op->resnull = false; + *op->resvalue = PointerGetDatum(dtuple); +} diff --git a/src/gausskernel/runtime/executor/execMain.cpp b/src/gausskernel/runtime/executor/execMain.cpp index 0f4713eeb..1cbc5c8a8 100755 --- a/src/gausskernel/runtime/executor/execMain.cpp +++ b/src/gausskernel/runtime/executor/execMain.cpp @@ -1247,6 +1247,7 @@ void InitPlan(QueryDesc *queryDesc, int eflags) */ estate->es_range_table = rangeTable; estate->es_plannedstmt = plannedstmt; + estate->es_is_flt_frame = plannedstmt->is_flt_frame; #ifdef ENABLE_MOT estate->mot_jit_context = queryDesc->mot_jit_context; #endif @@ -2493,9 +2494,13 @@ static const char *ExecRelCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *sl oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = (List **)palloc(ncheck * sizeof(List *)); for (i = 0; i < ncheck; i++) { - /* ExecQual wants implicit-AND form */ - qual = make_ands_implicit((Expr *)stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *)ExecPrepareExpr((Expr *)qual, estate); + + if (estate->es_is_flt_frame){ + resultRelInfo->ri_ConstraintExprs[i] = (List*)ExecPrepareExpr((Expr*)stringToNode(check[i].ccbin), estate); + } else { + qual = make_ands_implicit((Expr*)stringToNode(check[i].ccbin)); + resultRelInfo->ri_ConstraintExprs[i] = (List*)ExecPrepareExpr((Expr *)qual,estate); + } } (void)MemoryContextSwitchTo(oldContext); } @@ -2518,8 +2523,14 @@ static const char *ExecRelCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *sl * expression is not to be treated as a failure. Therefore, tell * ExecQual to return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) { - return check[i].ccname; + if (estate->es_is_flt_frame){ + if (!ExecCheckByFlatten((ExprState*)qual, econtext)){ + return check[i].ccname; + } + } else { + if (!ExecQual(qual, econtext, true)){ + return check[i].ccname; + } } } diff --git a/src/gausskernel/runtime/executor/execMerge.cpp b/src/gausskernel/runtime/executor/execMerge.cpp index 9aa8f0064..564194add 100644 --- a/src/gausskernel/runtime/executor/execMerge.cpp +++ b/src/gausskernel/runtime/executor/execMerge.cpp @@ -313,14 +313,14 @@ TupleTableSlot* ExecMergeProjQual(ModifyTableState* mtstate, List* mergeMatchedA * (no need to check separately since ExecQual() will return true if * there are no conditions to evaluate). */ - if (ExecQual((List*)action->whenqual, econtext, false)) { + if (ExecQual((List*)action->whenqual, econtext)) { if (estate->es_result_update_remoterel == NULL) { /* * We set up the projection earlier, so all we do here is * Project, no need for any other tasks prior to the * ExecUpdate. */ - result_slot = ExecProject(action->proj, NULL); + result_slot = ExecProject(action->proj); } else { /* we don't do projection in remote query */ } @@ -483,14 +483,14 @@ static void ExecMergeNotMatched(ModifyTableState* mtstate, EState* estate, Tuple * (no need to check separately since ExecQual() will return true if * there are no conditions to evaluate). */ - if (ExecQual((List*)action->whenqual, econtext, false)) { + if (ExecQual((List *)action->whenqual, econtext)) { /* * We set up the projection earlier, so all we do here is * Project, no need for any other tasks prior to the * ExecInsert. */ if (estate->es_result_insert_remoterel == NULL) { - ExecProject(action->proj, NULL); + ExecProject(action->proj); /* * ExecPrepareTupleRouting may modify the passed-in slot. Hence * pass a local reference so that action->slot is not modified. @@ -556,11 +556,14 @@ void ExecInitMerge(ModifyTableState* mtstate, EState* estate, ResultRelInfo* res MergeAction* action = (MergeAction*)lfirst(l); MergeActionState* action_state = makeNode(MergeActionState); TupleDesc tupDesc; - List* targetList = NULL; action_state->matched = action->matched; action_state->commandType = action->commandType; - action_state->whenqual = ExecInitExpr((Expr*)action->qual, &mtstate->ps); + if (estate->es_is_flt_frame) { + action_state->whenqual = ExecInitQualByFlatten((List*)action->qual, &mtstate->ps); + } else { + action_state->whenqual = ExecInitExprByRecursion((Expr*)action->qual, &mtstate->ps); + } /* create target slot for this action's projection */ tupDesc = ExecTypeFromTL((List*)action->targetList, false, true, relationDesc->td_tam_ops); @@ -579,8 +582,7 @@ void ExecInitMerge(ModifyTableState* mtstate, EState* estate, ResultRelInfo* res } /* build action projection state */ - targetList = (List*)ExecInitExpr((Expr*)action->targetList, &mtstate->ps); - action_state->proj = ExecBuildProjectionInfo(targetList, econtext, mtstate->mt_mergeproj, relationDesc); + action_state->proj = ExecBuildProjectionInfo(action->targetList, econtext, mtstate->mt_mergeproj, &mtstate->ps, relationDesc); /* * We create two lists - one for WHEN MATCHED actions and one diff --git a/src/gausskernel/runtime/executor/execQual.cpp b/src/gausskernel/runtime/executor/execQual.cpp index 741682014..7b3ecb409 100644 --- a/src/gausskernel/runtime/executor/execQual.cpp +++ b/src/gausskernel/runtime/executor/execQual.cpp @@ -1,39 +1,39 @@ /* ------------------------------------------------------------------------- - * - * execQual.cpp - * Routines to evaluate qualification and targetlist expressions - * - * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/gausskernel/runtime/executor/execQual.cpp - * - * ------------------------------------------------------------------------- - */ +* +* execQual.cpp +* Routines to evaluate qualification and targetlist expressions +* +* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. +* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group +* Portions Copyright (c) 1994, Regents of the University of California +* +* +* IDENTIFICATION +* src/gausskernel/runtime/executor/execQual.cpp +* +* ------------------------------------------------------------------------- +*/ /* - * INTERFACE ROUTINES - * ExecEvalExpr - (now a macro) evaluate an expression, return a datum - * ExecEvalExprSwitchContext - same, but switch into eval memory context - * ExecQual - return true/false if qualification is satisfied - * ExecProject - form a new tuple by projecting the given tuple - * - * NOTES - * The more heavily used ExecEvalExpr routines, such as ExecEvalScalarVar, - * are hotspots. Making these faster will speed up the entire system. - * - * ExecProject() is used to make tuple projections. Rather then - * trying to speed it up, the execution plan should be pre-processed - * to facilitate attribute sharing between nodes wherever possible, - * instead of doing needless copying. -cim 5/31/91 - * - * During expression evaluation, we check_stack_depth only in - * ExecMakeFunctionResult (and substitute routines) rather than at every - * single node. This is a compromise that trades off precision of the - * stack limit setting to gain speed. - */ +* INTERFACE ROUTINES +* ExecEvalExpr - (now a macro) evaluate an expression, return a datum +* ExecEvalExprSwitchContext - same, but switch into eval memory context +* ExecQual - return true/false if qualification is satisfied +* ExecProject - form a new tuple by projecting the given tuple +* +* NOTES +* The more heavily used ExecEvalExpr routines, such as ExecEvalScalarVar, +* are hotspots. Making these faster will speed up the entire system. +* +* ExecProject() is used to make tuple projections. Rather then +* trying to speed it up, the execution plan should be pre-processed +* to facilitate attribute sharing between nodes wherever possible, +* instead of doing needless copying. -cim 5/31/91 +* +* During expression evaluation, we check_stack_depth only in +* ExecMakeFunctionResult (and substitute routines) rather than at every +* single node. This is a compromise that trades off precision of the +* stack limit setting to gain speed. +*/ #include "postgres.h" #include "knl/knl_variable.h" @@ -47,6 +47,7 @@ #include "executor/node/nodeSubplan.h" #include "executor/node/nodeAgg.h" #include "executor/node/nodeCtescan.h" +#include "executor/executor.h" #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" @@ -84,27 +85,27 @@ static Datum ExecEvalWindowFunc(WindowFuncExprState* wfunc, ExprContext* econtex static Datum ExecEvalScalarVar(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalScalarVarFast(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalWholeRowVar( - WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalWholeRowFast( - WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalWholeRowSlow( - WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalConst(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalParamExec(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalParamExtern(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static bool isVectorEngineSupportSetFunc(Oid funcid); template -static void init_fcache( - Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt, bool allowSRF, bool needDescForSets); +void init_fcache( + Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt, bool allowSRF, bool needDescForSets); static void ShutdownFuncExpr(Datum arg); static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod, TupleDesc* cache_field, ExprContext* econtext); static void ShutdownTupleDescRef(Datum arg); template static ExprDoneCond ExecEvalFuncArgs( - FunctionCallInfo fcinfo, List* argList, ExprContext* econtext, int* plpgsql_var_dno = NULL); + FunctionCallInfo fcinfo, List* argList, ExprContext* econtext, int* plpgsql_var_dno = NULL); static void ExecPrepareTuplestoreResult( - FuncExprState* fcache, ExprContext* econtext, Tuplestorestate* resultStore, TupleDesc resultDesc); + FuncExprState* fcache, ExprContext* econtext, Tuplestorestate* resultStore, TupleDesc resultDesc); static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc); template @@ -112,24 +113,24 @@ static Datum ExecMakeFunctionResult(FuncExprState* fcache, ExprContext* econtext template static Datum ExecMakeFunctionResultNoSets( - FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalFunc(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalOper(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalDistinct(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalScalarArrayOp( - ScalarArrayOpExprState* sstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + ScalarArrayOpExprState* sstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalNot(BoolExprState* notclause, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalOr(BoolExprState* orExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalAnd(BoolExprState* andExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalConvertRowtype( - ConvertRowtypeExprState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + ConvertRowtypeExprState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCase(CaseExprState* caseExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCaseTestExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalArray(ArrayExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalRow(RowExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalRowCompare(RowCompareExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCoalesce( - CoalesceExprState* coalesceExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + CoalesceExprState* coalesceExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalMinMax(MinMaxExprState* minmaxExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalXml(XmlExprState* xmlExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalNullIf(FuncExprState* nullIfExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); @@ -137,127 +138,127 @@ static Datum ExecEvalNullTest(NullTestState* nstate, ExprContext* econtext, bool static Datum ExecEvalHashFilter(HashFilterState* hstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalBooleanTest(GenericExprState* bstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCoerceToDomain( - CoerceToDomainState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + CoerceToDomainState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCoerceToDomainValue( - ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalFieldSelect(FieldSelectState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalFieldStore(FieldStoreState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalRelabelType( - GenericExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + GenericExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCoerceViaIO(CoerceViaIOState* iostate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalArrayCoerceExpr( - ArrayCoerceExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + ArrayCoerceExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCurrentOfExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalGroupingFuncExpr( - GroupingFuncExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); + GroupingFuncExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalGroupingIdExpr( - GroupingIdExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); -static bool func_has_refcursor_args(Oid Funcid, FunctionCallInfoData* fcinfo); + GroupingIdExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); +bool func_has_refcursor_args(Oid Funcid, FunctionCallInfoData* fcinfo); extern struct varlena *heap_tuple_fetch_and_copy(Relation rel, struct varlena *attr, bool needcheck); static void check_huge_clob_paramter(FunctionCallInfoData* fcinfo, bool is_have_huge_clob); THR_LOCAL PLpgSQL_execstate* plpgsql_estate = NULL; /* ---------------------------------------------------------------- - * ExecEvalExpr routines - * - * Recursively evaluate a targetlist or qualification expression. - * - * Each of the following routines having the signature - * Datum ExecEvalFoo(ExprState *expression, - * ExprContext *econtext, - * bool *isNull, - * ExprDoneCond *isDone); - * is responsible for evaluating one type or subtype of ExprState node. - * They are normally called via the ExecEvalExpr macro, which makes use of - * the function pointer set up when the ExprState node was built by - * ExecInitExpr. (In some cases, we change this pointer later to avoid - * re-executing one-time overhead.) - * - * Note: for notational simplicity we declare these functions as taking the - * specific type of ExprState that they work on. This requires casting when - * assigning the function pointer in ExecInitExpr. Be careful that the - * function signature is declared correctly, because the cast suppresses - * automatic checking! - * - * - * All these functions share this calling convention: - * - * Inputs: - * expression: the expression state tree to evaluate - * econtext: evaluation context information - * - * Outputs: - * return value: Datum value of result - * *isNull: set to TRUE if result is NULL (actual return value is - * meaningless if so); set to FALSE if non-null result - * *isDone: set to indicator of set-result status - * - * A caller that can only accept a singleton (non-set) result should pass - * NULL for isDone; if the expression computes a set result then an error - * will be reported via ereport. If the caller does pass an isDone pointer - * then *isDone is set to one of these three states: - * ExprSingleResult singleton result (not a set) - * ExprMultipleResult return value is one element of a set - * ExprEndResult there are no more elements in the set - * When ExprMultipleResult is returned, the caller should invoke - * ExecEvalExpr() repeatedly until ExprEndResult is returned. ExprEndResult - * is returned after the last real set element. For convenience isNull will - * always be set TRUE when ExprEndResult is returned, but this should not be - * taken as indicating a NULL element of the set. Note that these return - * conventions allow us to distinguish among a singleton NULL, a NULL element - * of a set, and an empty set. - * - * The caller should already have switched into the temporary memory - * context econtext->ecxt_per_tuple_memory. The convenience entry point - * ExecEvalExprSwitchContext() is provided for callers who don't prefer to - * do the switch in an outer loop. We do not do the switch in these routines - * because it'd be a waste of cycles during nested expression evaluation. - * ---------------------------------------------------------------- - */ +* ExecEvalExpr routines +* +* Recursively evaluate a targetlist or qualification expression. +* +* Each of the following routines having the signature +* Datum ExecEvalFoo(ExprState *expression, +* ExprContext *econtext, +* bool *isNull, +* ExprDoneCond *isDone); +* is responsible for evaluating one type or subtype of ExprState node. +* They are normally called via the ExecEvalExpr macro, which makes use of +* the function pointer set up when the ExprState node was built by +* ExecInitExpr. (In some cases, we change this pointer later to avoid +* re-executing one-time overhead.) +* +* Note: for notational simplicity we declare these functions as taking the +* specific type of ExprState that they work on. This requires casting when +* assigning the function pointer in ExecInitExpr. Be careful that the +* function signature is declared correctly, because the cast suppresses +* automatic checking! +* +* +* All these functions share this calling convention: +* +* Inputs: +* expression: the expression state tree to evaluate +* econtext: evaluation context information +* +* Outputs: +* return value: Datum value of result +* *isNull: set to TRUE if result is NULL (actual return value is +* meaningless if so); set to FALSE if non-null result +* *isDone: set to indicator of set-result status +* +* A caller that can only accept a singleton (non-set) result should pass +* NULL for isDone; if the expression computes a set result then an error +* will be reported via ereport. If the caller does pass an isDone pointer +* then *isDone is set to one of these three states: +* ExprSingleResult singleton result (not a set) +* ExprMultipleResult return value is one element of a set +* ExprEndResult there are no more elements in the set +* When ExprMultipleResult is returned, the caller should invoke +* ExecEvalExpr() repeatedly until ExprEndResult is returned. ExprEndResult +* is returned after the last real set element. For convenience isNull will +* always be set TRUE when ExprEndResult is returned, but this should not be +* taken as indicating a NULL element of the set. Note that these return +* conventions allow us to distinguish among a singleton NULL, a NULL element +* of a set, and an empty set. +* +* The caller should already have switched into the temporary memory +* context econtext->ecxt_per_tuple_memory. The convenience entry point +* ExecEvalExprSwitchContext() is provided for callers who don't prefer to +* do the switch in an outer loop. We do not do the switch in these routines +* because it'd be a waste of cycles during nested expression evaluation. +* ---------------------------------------------------------------- +*/ /* ---------- - * ExecEvalArrayRef - * - * This function takes an ArrayRef and returns the extracted Datum - * if it's a simple reference, or the modified array value if it's - * an array assignment (i.e., array element or slice insertion). - * - * NOTE: if we get a NULL result from a subscript expression, we return NULL - * when it's an array reference, or raise an error when it's an assignment. - * - * NOTE: we deliberately refrain from applying DatumGetArrayTypeP() here, - * even though that might seem natural, because this code needs to support - * both varlena arrays and fixed-length array types. DatumGetArrayTypeP() - * only works for the varlena kind. The routines we call in arrayfuncs.c - * have to know the difference (that's what they need refattrlength for). - * ---------- - */ +* ExecEvalArrayRef +* +* This function takes an ArrayRef and returns the extracted Datum +* if it's a simple reference, or the modified array value if it's +* an array assignment (i.e., array element or slice insertion). +* +* NOTE: if we get a NULL result from a subscript expression, we return NULL +* when it's an array reference, or raise an error when it's an assignment. +* +* NOTE: we deliberately refrain from applying DatumGetArrayTypeP() here, +* even though that might seem natural, because this code needs to support +* both varlena arrays and fixed-length array types. DatumGetArrayTypeP() +* only works for the varlena kind. The routines we call in arrayfuncs.c +* have to know the difference (that's what they need refattrlength for). +* ---------- +*/ Datum ExecEvalArrayRef(ArrayRefExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - ArrayRef* arrayRef = (ArrayRef*)astate->xprstate.expr; - ArrayType* array_source = NULL; - ArrayType* resultArray = NULL; - bool isAssignment = (arrayRef->refassgnexpr != NULL); - bool eisnull = false; - ListCell* l = NULL; - int i = 0; - int j = 0; - IntArray upper, lower; - int* lIndex = NULL; - Oid typOid = astate->xprstate.resultType; + ArrayRef* arrayRef = (ArrayRef*)astate->xprstate.expr; + ArrayType* array_source = NULL; + ArrayType* resultArray = NULL; + bool isAssignment = (arrayRef->refassgnexpr != NULL); + bool eisnull = false; + ListCell* l = NULL; + int i = 0; + int j = 0; + IntArray upper, lower; + int* lIndex = NULL; + Oid typOid = astate->xprstate.resultType; - array_source = (ArrayType*)DatumGetPointer(ExecEvalExpr(astate->refexpr, econtext, isNull, isDone)); - /* - * If refexpr yields NULL, and it's a fetch, then result is NULL. In the - * assignment case, we'll cons up something below. - */ - if (*isNull) { - if (isDone && *isDone == ExprEndResult) - return (Datum)NULL; /* end of set result */ - if (!isAssignment) - return (Datum)NULL; - } - int returnNestTableLayer = 0; + array_source = (ArrayType*)DatumGetPointer(ExecEvalExpr(astate->refexpr, econtext, isNull, isDone)); + /* + * If refexpr yields NULL, and it's a fetch, then result is NULL. In the + * assignment case, we'll cons up something below. + */ + if (*isNull) { + if (isDone && *isDone == ExprEndResult) + return (Datum)NULL; /* end of set result */ + if (!isAssignment) + return (Datum)NULL; + } + int returnNestTableLayer = 0; ExecTableOfIndexInfo execTableOfIndexInfo; initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext); ExecEvalParamExternTableOfIndex((Node*)astate->refexpr->expr, &execTableOfIndexInfo); @@ -267,368 +268,368 @@ Datum ExecEvalArrayRef(ArrayRefExprState* astate, ExprContext* econtext, bool* i u_sess->SPI_cxt.cur_tableof_index->tableOfGetNestLayer = list_length(astate->refupperindexpr); } - foreach (l, astate->refupperindexpr) { - ExprState* eltstate = (ExprState*)lfirst(l); + foreach (l, astate->refupperindexpr) { + ExprState* eltstate = (ExprState*)lfirst(l); - if (i >= MAXDIM) - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + if (i >= MAXDIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", i + 1, MAXDIM))); - if (OidIsValid(execTableOfIndexInfo.tableOfIndexType) || execTableOfIndexInfo.isnestedtable) { - bool isTran = false; - PLpgSQL_execstate* old_estate = plpgsql_estate; - Datum exprValue = (Datum)ExecEvalExpr(eltstate, econtext, &eisnull, NULL); - plpgsql_estate = old_estate; + if (OidIsValid(execTableOfIndexInfo.tableOfIndexType) || execTableOfIndexInfo.isnestedtable) { + bool isTran = false; + PLpgSQL_execstate* old_estate = plpgsql_estate; + Datum exprValue = (Datum)ExecEvalExpr(eltstate, econtext, &eisnull, NULL); + plpgsql_estate = old_estate; if (unlikely(execTableOfIndexInfo.tableOfIndexType)) - if (execTableOfIndexInfo.tableOfIndexType == VARCHAROID && !eisnull && VARATT_IS_1B(exprValue)) { - exprValue = transVaratt1BTo4B(exprValue); - isTran = true; - } - TableOfIndexKey key; - PLpgSQL_var* node = NULL; - key.exprtypeid = execTableOfIndexInfo.tableOfIndexType; - key.exprdatum = exprValue; - int index = getTableOfIndexByDatumValue(key, execTableOfIndexInfo.tableOfIndex, &node); - if (isTran) { - pfree(DatumGetPointer(exprValue)); - } - if (execTableOfIndexInfo.isnestedtable) { - /* for nested table, we should take inner table's array and skip current indx */ - if (node == NULL || index == -1) { - eisnull = true; - } else { - PLpgSQL_var* var = node; - execTableOfIndexInfo.isnestedtable = (var->nest_table != NULL); - array_source = (ArrayType*)DatumGetPointer(var->value); - execTableOfIndexInfo.tableOfIndexType = var->datatype->tableOfIndexType; - execTableOfIndexInfo.tableOfIndex = var->tableOfIndex; - eisnull = var->isnull; + if (execTableOfIndexInfo.tableOfIndexType == VARCHAROID && !eisnull && VARATT_IS_1B(exprValue)) { + exprValue = transVaratt1BTo4B(exprValue); + isTran = true; + } + TableOfIndexKey key; + PLpgSQL_var* node = NULL; + key.exprtypeid = execTableOfIndexInfo.tableOfIndexType; + key.exprdatum = exprValue; + int index = getTableOfIndexByDatumValue(key, execTableOfIndexInfo.tableOfIndex, &node); + if (isTran) { + pfree(DatumGetPointer(exprValue)); + } + if (execTableOfIndexInfo.isnestedtable) { + /* for nested table, we should take inner table's array and skip current indx */ + if (node == NULL || index == -1) { + eisnull = true; + } else { + PLpgSQL_var* var = node; + execTableOfIndexInfo.isnestedtable = (var->nest_table != NULL); + array_source = (ArrayType*)DatumGetPointer(var->value); + execTableOfIndexInfo.tableOfIndexType = var->datatype->tableOfIndexType; + execTableOfIndexInfo.tableOfIndex = var->tableOfIndex; + eisnull = var->isnull; returnNestTableLayer = var->nest_layers; - if (plpgsql_estate) - plpgsql_estate->curr_nested_table_type = var->datatype->typoid; - continue; - } + if (plpgsql_estate) + plpgsql_estate->curr_nested_table_type = var->datatype->typoid; + continue; + } } else { returnNestTableLayer = 0; - } - if (index == -1) { - eisnull = true; - } else { - upper.indx[i++] = index; - } - } else { + } + if (index == -1) { + eisnull = true; + } else { + upper.indx[i++] = index; + } + } else { PLpgSQL_execstate* old_estate = plpgsql_estate; - upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL)); + upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL)); plpgsql_estate = old_estate; returnNestTableLayer = 0; - } - - /* If any index expr yields NULL, result is NULL or error */ - if (eisnull) { - if (isAssignment) - ereport(ERROR, - (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + } + + /* If any index expr yields NULL, result is NULL or error */ + if (eisnull) { + if (isAssignment) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("array subscript in assignment must not be null"))); - *isNull = true; - return (Datum)NULL; - } - } + *isNull = true; + return (Datum)NULL; + } + } - if (astate->reflowerindexpr != NIL) { - foreach (l, astate->reflowerindexpr) { - ExprState* eltstate = (ExprState*)lfirst(l); + if (astate->reflowerindexpr != NIL) { + foreach (l, astate->reflowerindexpr) { + ExprState* eltstate = (ExprState*)lfirst(l); - if (j >= MAXDIM) - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + if (j >= MAXDIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", j + 1, MAXDIM))); - if (execTableOfIndexInfo.tableOfIndexType == VARCHAROID || execTableOfIndexInfo.isnestedtable) { - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("index by varchar or nested table don't support two subscripts"))); - } else { - PLpgSQL_execstate* old_estate = plpgsql_estate; + if (execTableOfIndexInfo.tableOfIndexType == VARCHAROID || execTableOfIndexInfo.isnestedtable) { + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("index by varchar or nested table don't support two subscripts"))); + } else { + PLpgSQL_execstate* old_estate = plpgsql_estate; lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL)); plpgsql_estate = old_estate; - } - /* If any index expr yields NULL, result is NULL or error */ - if (eisnull) { - if (isAssignment) - ereport(ERROR, - (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + } + /* If any index expr yields NULL, result is NULL or error */ + if (eisnull) { + if (isAssignment) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("array subscript in assignment must not be null"))); - *isNull = true; - return (Datum)NULL; - } - } - /* this can't happen unless parser messed up */ - if (i != j) - ereport(ERROR, - (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + *isNull = true; + return (Datum)NULL; + } + } + /* this can't happen unless parser messed up */ + if (i != j) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmodule(MOD_EXECUTOR), (errmsg("upper and lower index lists are not same length (%d, %d)", i, j)))); - lIndex = lower.indx; - } else - lIndex = NULL; + lIndex = lower.indx; + } else + lIndex = NULL; - if (isAssignment) { - Datum sourceData; - Datum save_datum; - bool save_isNull = false; + if (isAssignment) { + Datum sourceData; + Datum save_datum; + bool save_isNull = false; - /* - * We might have a nested-assignment situation, in which the - * refassgnexpr is itself a FieldStore or ArrayRef that needs to - * obtain and modify the previous value of the array element or slice - * being replaced. If so, we have to extract that value from the - * array and pass it down via the econtext's caseValue. It's safe to - * reuse the CASE mechanism because there cannot be a CASE between - * here and where the value would be needed, and an array assignment - * can't be within a CASE either. (So saving and restoring the - * caseValue is just paranoia, but let's do it anyway.) - * - * Since fetching the old element might be a nontrivial expense, do it - * only if the argument appears to actually need it. - */ - save_datum = econtext->caseValue_datum; - save_isNull = econtext->caseValue_isNull; + /* + * We might have a nested-assignment situation, in which the + * refassgnexpr is itself a FieldStore or ArrayRef that needs to + * obtain and modify the previous value of the array element or slice + * being replaced. If so, we have to extract that value from the + * array and pass it down via the econtext's caseValue. It's safe to + * reuse the CASE mechanism because there cannot be a CASE between + * here and where the value would be needed, and an array assignment + * can't be within a CASE either. (So saving and restoring the + * caseValue is just paranoia, but let's do it anyway.) + * + * Since fetching the old element might be a nontrivial expense, do it + * only if the argument appears to actually need it. + */ + save_datum = econtext->caseValue_datum; + save_isNull = econtext->caseValue_isNull; - if (isAssignmentIndirectionExpr(astate->refassgnexpr)) { - if (*isNull) { - /* whole array is null, so any element or slice is too */ - econtext->caseValue_datum = (Datum)0; - econtext->caseValue_isNull = true; - } else if (lIndex == NULL) { - econtext->caseValue_datum = array_ref(array_source, - i, - upper.indx, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign, - &econtext->caseValue_isNull); - } else { - resultArray = array_get_slice(array_source, - i, - upper.indx, - lower.indx, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign); - econtext->caseValue_datum = PointerGetDatum(resultArray); - econtext->caseValue_isNull = false; - } - } else { - /* argument shouldn't need caseValue, but for safety set it null */ - econtext->caseValue_datum = (Datum)0; - econtext->caseValue_isNull = true; - } + if (isAssignmentIndirectionExpr(astate->refassgnexpr)) { + if (*isNull) { + /* whole array is null, so any element or slice is too */ + econtext->caseValue_datum = (Datum)0; + econtext->caseValue_isNull = true; + } else if (lIndex == NULL) { + econtext->caseValue_datum = array_ref(array_source, + i, + upper.indx, + astate->refattrlength, + astate->refelemlength, + astate->refelembyval, + astate->refelemalign, + &econtext->caseValue_isNull); + } else { + resultArray = array_get_slice(array_source, + i, + upper.indx, + lower.indx, + astate->refattrlength, + astate->refelemlength, + astate->refelembyval, + astate->refelemalign); + econtext->caseValue_datum = PointerGetDatum(resultArray); + econtext->caseValue_isNull = false; + } + } else { + /* argument shouldn't need caseValue, but for safety set it null */ + econtext->caseValue_datum = (Datum)0; + econtext->caseValue_isNull = true; + } - /* - * Evaluate the value to be assigned into the array. - */ - sourceData = ExecEvalExpr(astate->refassgnexpr, econtext, &eisnull, NULL); + /* + * Evaluate the value to be assigned into the array. + */ + sourceData = ExecEvalExpr(astate->refassgnexpr, econtext, &eisnull, NULL); - econtext->caseValue_datum = save_datum; - econtext->caseValue_isNull = save_isNull; - - /* - * For an assignment to a fixed-length array type, both the original - * array and the value to be assigned into it must be non-NULL, else - * we punt and return the original array. - */ - if (astate->refattrlength > 0) /* fixed-length array? */ - if (eisnull || *isNull) - return PointerGetDatum(array_source); + econtext->caseValue_datum = save_datum; + econtext->caseValue_isNull = save_isNull; - /* - * For assignment to varlena arrays, we handle a NULL original array - * by substituting an empty (zero-dimensional) array; insertion of the - * new element will result in a singleton array value. It does not - * matter whether the new element is NULL. - */ - if (*isNull) { - array_source = construct_empty_array(arrayRef->refelemtype); - *isNull = false; - } + /* + * For an assignment to a fixed-length array type, both the original + * array and the value to be assigned into it must be non-NULL, else + * we punt and return the original array. + */ + if (astate->refattrlength > 0) /* fixed-length array? */ + if (eisnull || *isNull) + return PointerGetDatum(array_source); - if (lIndex == NULL) - resultArray = array_set(array_source, - i, - upper.indx, - sourceData, - eisnull, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign); - else - resultArray = array_set_slice(array_source, - i, - upper.indx, - lower.indx, - (ArrayType*)DatumGetPointer(sourceData), - eisnull, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign); - return PointerGetDatum(resultArray); - } - /* for nested table, if get inner table's elem, need cover elem type */ - if (list_length(astate->refupperindexpr) > i && i > 0 && plpgsql_estate) { - if (plpgsql_estate->curr_nested_table_type != typOid) { - plpgsql_estate->curr_nested_table_type = ARR_ELEMTYPE(array_source); - get_typlenbyvalalign(plpgsql_estate->curr_nested_table_type, + /* + * For assignment to varlena arrays, we handle a NULL original array + * by substituting an empty (zero-dimensional) array; insertion of the + * new element will result in a singleton array value. It does not + * matter whether the new element is NULL. + */ + if (*isNull) { + array_source = construct_empty_array(arrayRef->refelemtype); + *isNull = false; + } + + if (lIndex == NULL) + resultArray = array_set(array_source, + i, + upper.indx, + sourceData, + eisnull, + astate->refattrlength, + astate->refelemlength, + astate->refelembyval, + astate->refelemalign); + else + resultArray = array_set_slice(array_source, + i, + upper.indx, + lower.indx, + (ArrayType*)DatumGetPointer(sourceData), + eisnull, + astate->refattrlength, + astate->refelemlength, + astate->refelembyval, + astate->refelemalign); + return PointerGetDatum(resultArray); + } + /* for nested table, if get inner table's elem, need cover elem type */ + if (list_length(astate->refupperindexpr) > i && i > 0 && plpgsql_estate) { + if (plpgsql_estate->curr_nested_table_type != typOid) { + plpgsql_estate->curr_nested_table_type = ARR_ELEMTYPE(array_source); + get_typlenbyvalalign(plpgsql_estate->curr_nested_table_type, &astate->refelemlength, &astate->refelembyval, &astate->refelemalign); - } - } + } + } if (plpgsql_estate) { plpgsql_estate->curr_nested_table_layers = returnNestTableLayer; } - if (lIndex == NULL) { - if (unlikely(i == 0)) { - /* get nested table's inner table */ - *isNull = eisnull; - return (Datum)array_source; - } else { - return array_ref(array_source, - i, - upper.indx, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign, - isNull); - } - } else { - resultArray = array_get_slice(array_source, - i, - upper.indx, - lower.indx, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign); - return PointerGetDatum(resultArray); - } + if (lIndex == NULL) { + if (unlikely(i == 0)) { + /* get nested table's inner table */ + *isNull = eisnull; + return (Datum)array_source; + } else { + return array_ref(array_source, + i, + upper.indx, + astate->refattrlength, + astate->refelemlength, + astate->refelembyval, + astate->refelemalign, + isNull); + } + } else { + resultArray = array_get_slice(array_source, + i, + upper.indx, + lower.indx, + astate->refattrlength, + astate->refelemlength, + astate->refelembyval, + astate->refelemalign); + return PointerGetDatum(resultArray); + } } /* - * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef - * that might need the old element value passed down? - * - * (We could use this in ExecEvalFieldStore too, but in that case passing - * the old value is so cheap there's no need.) - */ +* Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef +* that might need the old element value passed down? +* +* (We could use this in ExecEvalFieldStore too, but in that case passing +* the old value is so cheap there's no need.) +*/ static bool isAssignmentIndirectionExpr(ExprState* exprstate) { - if (exprstate == NULL) - return false; /* just paranoia */ - if (IsA(exprstate, FieldStoreState)) { - FieldStore* fstore = (FieldStore*)exprstate->expr; + if (exprstate == NULL) + return false; /* just paranoia */ + if (IsA(exprstate, FieldStoreState)) { + FieldStore* fstore = (FieldStore*)exprstate->expr; - if (fstore->arg && IsA(fstore->arg, CaseTestExpr)) - return true; - } else if (IsA(exprstate, ArrayRefExprState)) { - ArrayRef* arrayRef = (ArrayRef*)exprstate->expr; + if (fstore->arg && IsA(fstore->arg, CaseTestExpr)) + return true; + } else if (IsA(exprstate, ArrayRefExprState)) { + ArrayRef* arrayRef = (ArrayRef*)exprstate->expr; - if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr)) - return true; - } - return false; + if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr)) + return true; + } + return false; } /* ---------------------------------------------------------------- - * ExecEvalAggref - * - * Returns a Datum whose value is the value of the precomputed - * aggregate found in the given expression context. - * ---------------------------------------------------------------- - */ +* ExecEvalAggref +* +* Returns a Datum whose value is the value of the precomputed +* aggregate found in the given expression context. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalAggref(AggrefExprState* aggref, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - if (econtext->ecxt_aggvalues == NULL) /* safety check */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_AGG), + if (econtext->ecxt_aggvalues == NULL) /* safety check */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_AGG), errmodule(MOD_EXECUTOR), errmsg("no aggregates in this expression context"))); - *isNull = econtext->ecxt_aggnulls[aggref->aggno]; - return econtext->ecxt_aggvalues[aggref->aggno]; + *isNull = econtext->ecxt_aggnulls[aggref->aggno]; + return econtext->ecxt_aggvalues[aggref->aggno]; } /* ---------------------------------------------------------------- - * ExecEvalWindowFunc - * - * Returns a Datum whose value is the value of the precomputed - * window function found in the given expression context. - * ---------------------------------------------------------------- - */ +* ExecEvalWindowFunc +* +* Returns a Datum whose value is the value of the precomputed +* window function found in the given expression context. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalWindowFunc(WindowFuncExprState* wfunc, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - if (econtext->ecxt_aggvalues == NULL) /* safety check */ - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), + if (econtext->ecxt_aggvalues == NULL) /* safety check */ + ereport(ERROR, + (errcode(ERRCODE_WINDOWING_ERROR), errmodule(MOD_EXECUTOR), errmsg("no window functions in this expression context"))); - *isNull = econtext->ecxt_aggnulls[wfunc->wfuncno]; - return econtext->ecxt_aggvalues[wfunc->wfuncno]; + *isNull = econtext->ecxt_aggnulls[wfunc->wfuncno]; + return econtext->ecxt_aggvalues[wfunc->wfuncno]; } /* ---------------------------------------------------------------- - * ExecEvalScalarVar - * - * Returns a Datum whose value is the value of a scalar (not whole-row) - * range variable with respect to given expression context. - * - * Note: ExecEvalScalarVar is executed only the first time through in a given - * plan; it changes the ExprState's function pointer to pass control directly - * to ExecEvalScalarVarFast after making one-time checks. - * ---------------------------------------------------------------- - */ +* ExecEvalScalarVar +* +* Returns a Datum whose value is the value of a scalar (not whole-row) +* range variable with respect to given expression context. +* +* Note: ExecEvalScalarVar is executed only the first time through in a given +* plan; it changes the ExprState's function pointer to pass control directly +* to ExecEvalScalarVarFast after making one-time checks. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalScalarVar(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Var* variable = (Var*)exprstate->expr; - TupleTableSlot* slot = NULL; - AttrNumber attnum; + Var* variable = (Var*)exprstate->expr; + TupleTableSlot* slot = NULL; + AttrNumber attnum; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - /* Get the input slot and attribute number we want */ - switch (variable->varno) { - case INNER_VAR: /* get the tuple from the inner node */ - slot = econtext->ecxt_innertuple; - break; + /* Get the input slot and attribute number we want */ + switch (variable->varno) { + case INNER_VAR: /* get the tuple from the inner node */ + slot = econtext->ecxt_innertuple; + break; - case OUTER_VAR: /* get the tuple from the outer node */ - slot = econtext->ecxt_outertuple; - break; + case OUTER_VAR: /* get the tuple from the outer node */ + slot = econtext->ecxt_outertuple; + break; - /* INDEX_VAR is handled by default case */ - default: /* get the tuple from the relation being scanned */ - slot = econtext->ecxt_scantuple; - break; - } + /* INDEX_VAR is handled by default case */ + default: /* get the tuple from the relation being scanned */ + slot = econtext->ecxt_scantuple; + break; + } attnum = variable->varattno; - /* This was checked by ExecInitExpr */ - Assert(attnum != InvalidAttrNumber); + /* This was checked by ExecInitExpr */ + Assert(attnum != InvalidAttrNumber); - RightRefState* refState = econtext->rightRefState; + RightRefState* refState = econtext->rightRefState; int index = attnum - 1; if (refState && refState->values && (IS_ENABLE_INSERT_RIGHT_REF(refState) || @@ -663,292 +664,292 @@ static Datum ExecEvalScalarVar(ExprState* exprstate, ExprContext* econtext, bool TupleDesc slot_tupdesc = slot->tts_tupleDescriptor; Form_pg_attribute attr; - if (attnum > slot_tupdesc->natts) /* should never happen */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_ATTRIBUTE), + if (attnum > slot_tupdesc->natts) /* should never happen */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_ATTRIBUTE), errmodule(MOD_EXECUTOR), errmsg("attribute number %d exceeds number of columns %d", attnum, slot_tupdesc->natts))); - attr = &slot_tupdesc->attrs[attnum - 1]; + attr = &slot_tupdesc->attrs[attnum - 1]; - /* can't check type if dropped, since atttypid is probably 0 */ - if (!attr->attisdropped) { - if (variable->vartype != attr->atttypid) - ereport(ERROR, - (errcode(ERRCODE_INVALID_ATTRIBUTE), + /* can't check type if dropped, since atttypid is probably 0 */ + if (!attr->attisdropped) { + if (variable->vartype != attr->atttypid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ATTRIBUTE), errmodule(MOD_EXECUTOR), errmsg("attribute %d has wrong type", attnum), errdetail("Table has type %s, but query expects %s.", - format_type_be(attr->atttypid), - format_type_be(variable->vartype)))); - } - } + format_type_be(attr->atttypid), + format_type_be(variable->vartype)))); + } + } - /* Skip the checking on future executions of node */ - exprstate->evalfunc = ExecEvalScalarVarFast; + /* Skip the checking on future executions of node */ + exprstate->evalfunc = ExecEvalScalarVarFast; - /* Fetch the value from the slot */ - return tableam_tslot_getattr(slot, attnum, isNull); + /* Fetch the value from the slot */ + return tableam_tslot_getattr(slot, attnum, isNull); } /* ---------------------------------------------------------------- - * ExecEvalScalarVarFast - * - * Returns a Datum for a scalar variable. - * ---------------------------------------------------------------- - */ +* ExecEvalScalarVarFast +* +* Returns a Datum for a scalar variable. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalScalarVarFast(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Var* variable = (Var*)exprstate->expr; - TupleTableSlot* slot = NULL; - AttrNumber attnum; + Var* variable = (Var*)exprstate->expr; + TupleTableSlot* slot = NULL; + AttrNumber attnum; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - /* Get the input slot and attribute number we want */ - switch (variable->varno) { - case INNER_VAR: /* get the tuple from the inner node */ - slot = econtext->ecxt_innertuple; - break; + /* Get the input slot and attribute number we want */ + switch (variable->varno) { + case INNER_VAR: /* get the tuple from the inner node */ + slot = econtext->ecxt_innertuple; + break; - case OUTER_VAR: /* get the tuple from the outer node */ - slot = econtext->ecxt_outertuple; - break; + case OUTER_VAR: /* get the tuple from the outer node */ + slot = econtext->ecxt_outertuple; + break; - /* INDEX_VAR is handled by default case */ - default: /* get the tuple from the relation being scanned */ - slot = econtext->ecxt_scantuple; - break; - } + /* INDEX_VAR is handled by default case */ + default: /* get the tuple from the relation being scanned */ + slot = econtext->ecxt_scantuple; + break; + } - attnum = variable->varattno; + attnum = variable->varattno; - Assert(slot != NULL); - /* Fetch the value from the slot */ - return tableam_tslot_getattr(slot, attnum, isNull); + Assert(slot != NULL); + /* Fetch the value from the slot */ + return tableam_tslot_getattr(slot, attnum, isNull); } /* ---------------------------------------------------------------- - * ExecEvalWholeRowVar - * - * Returns a Datum whose value is the value of a whole-row range - * variable with respect to given expression context. - * - * Note: ExecEvalWholeRowVar is executed only the first time through in a - * given plan; it changes the ExprState's function pointer to pass control - * directly to ExecEvalWholeRowFast or ExecEvalWholeRowSlow after making - * one-time checks. - * ---------------------------------------------------------------- - */ +* ExecEvalWholeRowVar +* +* Returns a Datum whose value is the value of a whole-row range +* variable with respect to given expression context. +* +* Note: ExecEvalWholeRowVar is executed only the first time through in a +* given plan; it changes the ExprState's function pointer to pass control +* directly to ExecEvalWholeRowFast or ExecEvalWholeRowSlow after making +* one-time checks. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalWholeRowVar( - WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Var* variable = (Var*)wrvstate->xprstate.expr; - TupleTableSlot* slot = NULL; - bool needslow = false; + Var* variable = (Var*)wrvstate->xprstate.expr; + TupleTableSlot* slot = NULL; + bool needslow = false; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - /* This was checked by ExecInitExpr */ - Assert(variable->varattno == InvalidAttrNumber); + /* This was checked by ExecInitExpr */ + Assert(variable->varattno == InvalidAttrNumber); - /* Get the input slot we want */ - switch (variable->varno) { - case INNER_VAR: /* get the tuple from the inner node */ - slot = econtext->ecxt_innertuple; - break; + /* Get the input slot we want */ + switch (variable->varno) { + case INNER_VAR: /* get the tuple from the inner node */ + slot = econtext->ecxt_innertuple; + break; - case OUTER_VAR: /* get the tuple from the outer node */ - slot = econtext->ecxt_outertuple; - break; + case OUTER_VAR: /* get the tuple from the outer node */ + slot = econtext->ecxt_outertuple; + break; - /* INDEX_VAR is handled by default case */ - default: /* get the tuple from the relation being scanned */ - slot = econtext->ecxt_scantuple; - break; - } + /* INDEX_VAR is handled by default case */ + default: /* get the tuple from the relation being scanned */ + slot = econtext->ecxt_scantuple; + break; + } - /* - * If the input tuple came from a subquery, it might contain "resjunk" - * columns (such as GROUP BY or ORDER BY columns), which we don't want to - * keep in the whole-row result. We can get rid of such columns by - * passing the tuple through a JunkFilter --- but to make one, we have to - * lay our hands on the subquery's targetlist. Fortunately, there are not - * very many cases where this can happen, and we can identify all of them - * by examining our parent PlanState. We assume this is not an issue in - * standalone expressions that don't have parent plans. (Whole-row Vars - * can occur in such expressions, but they will always be referencing - * table rows.) - */ - if (wrvstate->parent) { - PlanState* subplan = NULL; + /* + * If the input tuple came from a subquery, it might contain "resjunk" + * columns (such as GROUP BY or ORDER BY columns), which we don't want to + * keep in the whole-row result. We can get rid of such columns by + * passing the tuple through a JunkFilter --- but to make one, we have to + * lay our hands on the subquery's targetlist. Fortunately, there are not + * very many cases where this can happen, and we can identify all of them + * by examining our parent PlanState. We assume this is not an issue in + * standalone expressions that don't have parent plans. (Whole-row Vars + * can occur in such expressions, but they will always be referencing + * table rows.) + */ + if (wrvstate->parent) { + PlanState* subplan = NULL; - switch (nodeTag(wrvstate->parent)) { - case T_SubqueryScanState: - subplan = ((SubqueryScanState*)wrvstate->parent)->subplan; - break; - case T_CteScanState: - subplan = ((CteScanState*)wrvstate->parent)->cteplanstate; - break; - default: - break; - } + switch (nodeTag(wrvstate->parent)) { + case T_SubqueryScanState: + subplan = ((SubqueryScanState*)wrvstate->parent)->subplan; + break; + case T_CteScanState: + subplan = ((CteScanState*)wrvstate->parent)->cteplanstate; + break; + default: + break; + } - if (subplan != NULL) { - bool junk_filter_needed = false; - ListCell* tlist = NULL; + if (subplan != NULL) { + bool junk_filter_needed = false; + ListCell* tlist = NULL; - /* Detect whether subplan tlist actually has any junk columns */ - foreach (tlist, subplan->plan->targetlist) { - TargetEntry* tle = (TargetEntry*)lfirst(tlist); + /* Detect whether subplan tlist actually has any junk columns */ + foreach (tlist, subplan->plan->targetlist) { + TargetEntry* tle = (TargetEntry*)lfirst(tlist); - if (tle->resjunk) { - junk_filter_needed = true; - break; - } - } + if (tle->resjunk) { + junk_filter_needed = true; + break; + } + } - /* If so, build the junkfilter in the query memory context */ - if (junk_filter_needed) { - MemoryContext oldcontext; + /* If so, build the junkfilter in the query memory context */ + if (junk_filter_needed) { + MemoryContext oldcontext; - oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); - wrvstate->wrv_junkFilter = ExecInitJunkFilter(subplan->plan->targetlist, - ExecGetResultType(subplan)->tdhasoid, - ExecInitExtraTupleSlot(wrvstate->parent->state), + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + wrvstate->wrv_junkFilter = ExecInitJunkFilter(subplan->plan->targetlist, + ExecGetResultType(subplan)->tdhasoid, + ExecInitExtraTupleSlot(wrvstate->parent->state), TableAmHeap); MemoryContextSwitchTo(oldcontext); } } } - /* Apply the junkfilter if any */ - if (wrvstate->wrv_junkFilter != NULL) - slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot); + /* Apply the junkfilter if any */ + if (wrvstate->wrv_junkFilter != NULL) + slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot); - /* - * If the Var identifies a named composite type, we must check that the - * actual tuple type is compatible with it. - */ - if (variable->vartype != RECORDOID) { - TupleDesc var_tupdesc; - TupleDesc slot_tupdesc; - int i; + /* + * If the Var identifies a named composite type, we must check that the + * actual tuple type is compatible with it. + */ + if (variable->vartype != RECORDOID) { + TupleDesc var_tupdesc; + TupleDesc slot_tupdesc; + int i; - /* - * We really only care about numbers of attributes and data types. - * Also, we can ignore type mismatch on columns that are dropped in - * the destination type, so long as (1) the physical storage matches - * or (2) the actual column value is NULL. Case (1) is helpful in - * some cases involving out-of-date cached plans, while case (2) is - * expected behavior in situations such as an INSERT into a table with - * dropped columns (the planner typically generates an INT4 NULL - * regardless of the dropped column type). If we find a dropped - * column and cannot verify that case (1) holds, we have to use - * ExecEvalWholeRowSlow to check (2) for each row. - */ - var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); + /* + * We really only care about numbers of attributes and data types. + * Also, we can ignore type mismatch on columns that are dropped in + * the destination type, so long as (1) the physical storage matches + * or (2) the actual column value is NULL. Case (1) is helpful in + * some cases involving out-of-date cached plans, while case (2) is + * expected behavior in situations such as an INSERT into a table with + * dropped columns (the planner typically generates an INT4 NULL + * regardless of the dropped column type). If we find a dropped + * column and cannot verify that case (1) holds, we have to use + * ExecEvalWholeRowSlow to check (2) for each row. + */ + var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); - slot_tupdesc = slot->tts_tupleDescriptor; + slot_tupdesc = slot->tts_tupleDescriptor; - if (var_tupdesc->natts != slot_tupdesc->natts) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), + if (var_tupdesc->natts != slot_tupdesc->natts) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("table row type and query-specified row type do not match"), errdetail_plural("Table row contains %d attribute, but query expects %d.", - "Table row contains %d attributes, but query expects %d.", - slot_tupdesc->natts, - slot_tupdesc->natts, - var_tupdesc->natts))); + "Table row contains %d attributes, but query expects %d.", + slot_tupdesc->natts, + slot_tupdesc->natts, + var_tupdesc->natts))); - for (i = 0; i < var_tupdesc->natts; i++) { - Form_pg_attribute vattr = &var_tupdesc->attrs[i]; - Form_pg_attribute sattr = &slot_tupdesc->attrs[i]; + for (i = 0; i < var_tupdesc->natts; i++) { + Form_pg_attribute vattr = &var_tupdesc->attrs[i]; + Form_pg_attribute sattr = &slot_tupdesc->attrs[i]; - if (vattr->atttypid == sattr->atttypid) - continue; /* no worries */ - if (!vattr->attisdropped) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), + if (vattr->atttypid == sattr->atttypid) + continue; /* no worries */ + if (!vattr->attisdropped) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("table row type and query-specified row type do not match"), errdetail("Table has type %s at ordinal position %d, but query expects %s.", - format_type_be(sattr->atttypid), - i + 1, - format_type_be(vattr->atttypid)))); + format_type_be(sattr->atttypid), + i + 1, + format_type_be(vattr->atttypid)))); - if (vattr->attlen != sattr->attlen || vattr->attalign != sattr->attalign) - needslow = true; /* need runtime check for null */ - } + if (vattr->attlen != sattr->attlen || vattr->attalign != sattr->attalign) + needslow = true; /* need runtime check for null */ + } - ReleaseTupleDesc(var_tupdesc); - } + ReleaseTupleDesc(var_tupdesc); + } - /* Skip the checking on future executions of node */ - if (needslow) - wrvstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWholeRowSlow; - else - wrvstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWholeRowFast; + /* Skip the checking on future executions of node */ + if (needslow) + wrvstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWholeRowSlow; + else + wrvstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWholeRowFast; - /* Fetch the value */ - return (*wrvstate->xprstate.evalfunc)((ExprState*)wrvstate, econtext, isNull, isDone); + /* Fetch the value */ + return (*wrvstate->xprstate.evalfunc)((ExprState*)wrvstate, econtext, isNull, isDone); } /* ---------------------------------------------------------------- - * ExecEvalWholeRowFast - * - * Returns a Datum for a whole-row variable. - * ---------------------------------------------------------------- - */ +* ExecEvalWholeRowFast +* +* Returns a Datum for a whole-row variable. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalWholeRowFast( - WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Var* variable = (Var*)wrvstate->xprstate.expr; - TupleTableSlot* slot = NULL; - TupleDesc slot_tupdesc; - HeapTuple tuple; - TupleDesc tupleDesc; - HeapTupleHeader dtuple; - errno_t rc = EOK; + Var* variable = (Var*)wrvstate->xprstate.expr; + TupleTableSlot* slot = NULL; + TupleDesc slot_tupdesc; + HeapTuple tuple; + TupleDesc tupleDesc; + HeapTupleHeader dtuple; + errno_t rc = EOK; - if (isDone != NULL) - *isDone = ExprSingleResult; - *isNull = false; + if (isDone != NULL) + *isDone = ExprSingleResult; + *isNull = false; - /* Get the input slot we want */ - switch (variable->varno) { - case INNER_VAR: /* get the tuple from the inner node */ - slot = econtext->ecxt_innertuple; - break; + /* Get the input slot we want */ + switch (variable->varno) { + case INNER_VAR: /* get the tuple from the inner node */ + slot = econtext->ecxt_innertuple; + break; - case OUTER_VAR: /* get the tuple from the outer node */ - slot = econtext->ecxt_outertuple; - break; + case OUTER_VAR: /* get the tuple from the outer node */ + slot = econtext->ecxt_outertuple; + break; - /* INDEX_VAR is handled by default case */ - default: /* get the tuple from the relation being scanned */ - slot = econtext->ecxt_scantuple; - break; - } + /* INDEX_VAR is handled by default case */ + default: /* get the tuple from the relation being scanned */ + slot = econtext->ecxt_scantuple; + break; + } - /* Apply the junkfilter if any */ - if (wrvstate->wrv_junkFilter != NULL) - slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot); + /* Apply the junkfilter if any */ + if (wrvstate->wrv_junkFilter != NULL) + slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot); - /* - * If it's a RECORD Var, we'll use the slot's type ID info. It's likely - * that the slot's type is also RECORD; if so, make sure it's been - * "blessed", so that the Datum can be interpreted later. - */ - slot_tupdesc = slot->tts_tupleDescriptor; - if (variable->vartype == RECORDOID) { - if (slot_tupdesc->tdtypeid == RECORDOID && slot_tupdesc->tdtypmod < 0) - assign_record_type_typmod(slot_tupdesc); - } + /* + * If it's a RECORD Var, we'll use the slot's type ID info. It's likely + * that the slot's type is also RECORD; if so, make sure it's been + * "blessed", so that the Datum can be interpreted later. + */ + slot_tupdesc = slot->tts_tupleDescriptor; + if (variable->vartype == RECORDOID) { + if (slot_tupdesc->tdtypeid == RECORDOID && slot_tupdesc->tdtypmod < 0) + assign_record_type_typmod(slot_tupdesc); + } - tuple = ExecFetchSlotTuple(slot); - tupleDesc = slot->tts_tupleDescriptor; + tuple = ExecFetchSlotTuple(slot); + tupleDesc = slot->tts_tupleDescriptor; /* * If it's a RECORD Var, we'll use the slot's type ID info. It's likely * that the slot's type is also RECORD; if so, make sure it's been @@ -962,125 +963,125 @@ static Datum ExecEvalWholeRowFast( tupleDesc->tdtypmod < 0) assign_record_type_typmod(tupleDesc); - /* - * We have to make a copy of the tuple so we can safely insert the Datum - * overhead fields, which are not set in on-disk tuples. - */ - dtuple = (HeapTupleHeader)palloc(tuple->t_len); - rc = memcpy_s((char*)dtuple, tuple->t_len, (char*)tuple->t_data, tuple->t_len); - securec_check(rc, "\0", "\0"); + /* + * We have to make a copy of the tuple so we can safely insert the Datum + * overhead fields, which are not set in on-disk tuples. + */ + dtuple = (HeapTupleHeader)palloc(tuple->t_len); + rc = memcpy_s((char*)dtuple, tuple->t_len, (char*)tuple->t_data, tuple->t_len); + securec_check(rc, "\0", "\0"); - HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len); + HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len); - /* - * If the Var identifies a named composite type, label the tuple with that - * type; otherwise use what is in the tupleDesc. - */ - if (variable->vartype != RECORDOID) { - HeapTupleHeaderSetTypeId(dtuple, variable->vartype); - HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod); - } else { - HeapTupleHeaderSetTypeId(dtuple, tupleDesc->tdtypeid); - HeapTupleHeaderSetTypMod(dtuple, tupleDesc->tdtypmod); - } + /* + * If the Var identifies a named composite type, label the tuple with that + * type; otherwise use what is in the tupleDesc. + */ + if (variable->vartype != RECORDOID) { + HeapTupleHeaderSetTypeId(dtuple, variable->vartype); + HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod); + } else { + HeapTupleHeaderSetTypeId(dtuple, tupleDesc->tdtypeid); + HeapTupleHeaderSetTypMod(dtuple, tupleDesc->tdtypmod); + } - return PointerGetDatum(dtuple); + return PointerGetDatum(dtuple); } /* ---------------------------------------------------------------- - * ExecEvalWholeRowSlow - * - * Returns a Datum for a whole-row variable, in the "slow" case where - * we can't just copy the subplan's output. - * ---------------------------------------------------------------- - */ +* ExecEvalWholeRowSlow +* +* Returns a Datum for a whole-row variable, in the "slow" case where +* we can't just copy the subplan's output. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalWholeRowSlow( - WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Var* variable = (Var*)wrvstate->xprstate.expr; - TupleTableSlot* slot = NULL; - HeapTuple tuple; - TupleDesc tupleDesc; - TupleDesc var_tupdesc; - HeapTupleHeader dtuple; - int i; - errno_t rc = EOK; + Var* variable = (Var*)wrvstate->xprstate.expr; + TupleTableSlot* slot = NULL; + HeapTuple tuple; + TupleDesc tupleDesc; + TupleDesc var_tupdesc; + HeapTupleHeader dtuple; + int i; + errno_t rc = EOK; - if (isDone != NULL) - *isDone = ExprSingleResult; - *isNull = false; + if (isDone != NULL) + *isDone = ExprSingleResult; + *isNull = false; - /* Get the input slot we want */ - switch (variable->varno) { - case INNER_VAR: /* get the tuple from the inner node */ - slot = econtext->ecxt_innertuple; - break; + /* Get the input slot we want */ + switch (variable->varno) { + case INNER_VAR: /* get the tuple from the inner node */ + slot = econtext->ecxt_innertuple; + break; - case OUTER_VAR: /* get the tuple from the outer node */ - slot = econtext->ecxt_outertuple; - break; + case OUTER_VAR: /* get the tuple from the outer node */ + slot = econtext->ecxt_outertuple; + break; - /* INDEX_VAR is handled by default case */ - default: /* get the tuple from the relation being scanned */ - slot = econtext->ecxt_scantuple; - break; - } + /* INDEX_VAR is handled by default case */ + default: /* get the tuple from the relation being scanned */ + slot = econtext->ecxt_scantuple; + break; + } - /* Apply the junkfilter if any */ - if (wrvstate->wrv_junkFilter != NULL) - slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot); + /* Apply the junkfilter if any */ + if (wrvstate->wrv_junkFilter != NULL) + slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot); - tuple = ExecFetchSlotTuple(slot); - tupleDesc = slot->tts_tupleDescriptor; + tuple = ExecFetchSlotTuple(slot); + tupleDesc = slot->tts_tupleDescriptor; - Assert(variable->vartype != RECORDOID); - var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); + Assert(variable->vartype != RECORDOID); + var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); - /* Check to see if any dropped attributes are non-null */ - for (i = 0; i < var_tupdesc->natts; i++) { - Form_pg_attribute vattr = &var_tupdesc->attrs[i]; - Form_pg_attribute sattr = &tupleDesc->attrs[i]; + /* Check to see if any dropped attributes are non-null */ + for (i = 0; i < var_tupdesc->natts; i++) { + Form_pg_attribute vattr = &var_tupdesc->attrs[i]; + Form_pg_attribute sattr = &tupleDesc->attrs[i]; - if (!vattr->attisdropped) - continue; /* already checked non-dropped cols */ - if (tableam_tops_tuple_attisnull(tuple, i + 1, tupleDesc)) - continue; /* null is always okay */ - if (vattr->attlen != sattr->attlen || vattr->attalign != sattr->attalign) - ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("table row type and query-specified row type do not match"), - errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.", i + 1))); - } + if (!vattr->attisdropped) + continue; /* already checked non-dropped cols */ + if (tableam_tops_tuple_attisnull(tuple, i + 1, tupleDesc)) + continue; /* null is always okay */ + if (vattr->attlen != sattr->attlen || vattr->attalign != sattr->attalign) + ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("table row type and query-specified row type do not match"), + errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.", i + 1))); + } - /* - * We have to make a copy of the tuple so we can safely insert the Datum - * overhead fields, which are not set in on-disk tuples. - */ - dtuple = (HeapTupleHeader)palloc(tuple->t_len); - rc = memcpy_s((char*)dtuple, tuple->t_len, (char*)tuple->t_data, tuple->t_len); - securec_check(rc, "\0", "\0"); + /* + * We have to make a copy of the tuple so we can safely insert the Datum + * overhead fields, which are not set in on-disk tuples. + */ + dtuple = (HeapTupleHeader)palloc(tuple->t_len); + rc = memcpy_s((char*)dtuple, tuple->t_len, (char*)tuple->t_data, tuple->t_len); + securec_check(rc, "\0", "\0"); - HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len); - HeapTupleHeaderSetTypeId(dtuple, variable->vartype); - HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod); + HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len); + HeapTupleHeaderSetTypeId(dtuple, variable->vartype); + HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod); - ReleaseTupleDesc(var_tupdesc); + ReleaseTupleDesc(var_tupdesc); - return PointerGetDatum(dtuple); + return PointerGetDatum(dtuple); } /* ---------------------------------------------------------------- - * ExecEvalConst - * - * Returns the value of a constant. - * - * Note that for pass-by-ref datatypes, we return a pointer to the - * actual constant node. This is one of the reasons why functions - * must treat their input arguments as read-only. - * ---------------------------------------------------------------- - */ +* ExecEvalConst +* +* Returns the value of a constant. +* +* Note that for pass-by-ref datatypes, we return a pointer to the +* actual constant node. This is one of the reasons why functions +* must treat their input arguments as read-only. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalConst(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Const* con = NULL; + Const* con = NULL; if (IsA(exprstate->expr, UserVar)) { bool found = false; UserVar *uservar = (UserVar *)exprstate->expr; @@ -1110,35 +1111,35 @@ static Datum ExecEvalConst(ExprState* exprstate, ExprContext* econtext, bool* is con = (Const*)exprstate->expr; } - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - *isNull = con->constisnull; + *isNull = con->constisnull; - /* if a const cursor, copy cursor option data to econtext */ - if (econtext->is_cursor && con->consttype == REFCURSOROID) { - CopyCursorInfoData(&econtext->cursor_data, &con->cursor_data); - econtext->dno = con->cursor_data.cur_dno; - } + /* if a const cursor, copy cursor option data to econtext */ + if (econtext->is_cursor && con->consttype == REFCURSOROID) { + CopyCursorInfoData(&econtext->cursor_data, &con->cursor_data); + econtext->dno = con->cursor_data.cur_dno; + } - return con->constvalue; + return con->constvalue; } /* ---------------------------------------------------------------- - * ExecEvalRownum: Returns the rownum - * ---------------------------------------------------------------- - */ +* ExecEvalRownum: Returns the rownum +* ---------------------------------------------------------------- +*/ static Datum ExecEvalRownum(RownumState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - if (isDone != NULL) - *isDone = ExprSingleResult; - *isNull = false; + if (isDone != NULL) + *isDone = ExprSingleResult; + *isNull = false; - if (ROWNUM_TYPE_COMPAT) { - return DirectFunctionCall1(int8_numeric, Int64GetDatum(exprstate->ps->ps_rownum + 1)); - } else { - return Int64GetDatum(exprstate->ps->ps_rownum + 1); - } + if (ROWNUM_TYPE_COMPAT) { + return DirectFunctionCall1(int8_numeric, Int64GetDatum(exprstate->ps->ps_rownum + 1)); + } else { + return Int64GetDatum(exprstate->ps->ps_rownum + 1); + } } /* ---------------------------------------------------------------- @@ -1171,370 +1172,370 @@ static Datum ExecEvalUserSetElm(ExprState* exprstate, ExprContext* econtext, boo return ((Const*)elemcopy.val)->constvalue; } /* ---------------------------------------------------------------- - * ExecEvalParamExec - * - * Returns the value of a PARAM_EXEC parameter. - * ---------------------------------------------------------------- - */ +* ExecEvalParamExec +* +* Returns the value of a PARAM_EXEC parameter. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalParamExec(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Param* expression = (Param*)exprstate->expr; - int thisParamId = expression->paramid; - ParamExecData* prm = NULL; + Param* expression = (Param*)exprstate->expr; + int thisParamId = expression->paramid; + ParamExecData* prm = NULL; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - /* - * PARAM_EXEC params (internal executor parameters) are stored in the - * ecxt_param_exec_vals array, and can be accessed by array index. - */ - prm = &(econtext->ecxt_param_exec_vals[thisParamId]); - if (prm->execPlan != NULL) { - /* Parameter not evaluated yet, so go do it */ - ExecSetParamPlan((SubPlanState*)prm->execPlan, econtext); - /* ExecSetParamPlan should have processed this param... */ - Assert(prm->execPlan == NULL); - prm->isConst = true; - prm->valueType = expression->paramtype; - } - *isNull = prm->isnull; - prm->isChanged = true; - return prm->value; + /* + * PARAM_EXEC params (internal executor parameters) are stored in the + * ecxt_param_exec_vals array, and can be accessed by array index. + */ + prm = &(econtext->ecxt_param_exec_vals[thisParamId]); + if (prm->execPlan != NULL) { + /* Parameter not evaluated yet, so go do it */ + ExecSetParamPlan((SubPlanState*)prm->execPlan, econtext); + /* ExecSetParamPlan should have processed this param... */ + Assert(prm->execPlan == NULL); + prm->isConst = true; + prm->valueType = expression->paramtype; + } + *isNull = prm->isnull; + prm->isChanged = true; + return prm->value; } /* ---------------------------------------------------------------- - * ExecEvalParamExtern - * - * Returns the value of a PARAM_EXTERN parameter. - * ---------------------------------------------------------------- - */ +* ExecEvalParamExtern +* +* Returns the value of a PARAM_EXTERN parameter. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalParamExtern(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Param* expression = (Param*)exprstate->expr; - int thisParamId = expression->paramid; - ParamListInfo paramInfo = econtext->ecxt_param_list_info; + Param* expression = (Param*)exprstate->expr; + int thisParamId = expression->paramid; + ParamListInfo paramInfo = econtext->ecxt_param_list_info; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - /* - * PARAM_EXTERN parameters must be sought in ecxt_param_list_info. - */ - if (paramInfo && thisParamId > 0 && thisParamId <= paramInfo->numParams) { - ParamExternData* prm = ¶mInfo->params[thisParamId - 1]; + /* + * PARAM_EXTERN parameters must be sought in ecxt_param_list_info. + */ + if (paramInfo && thisParamId > 0 && thisParamId <= paramInfo->numParams) { + ParamExternData* prm = ¶mInfo->params[thisParamId - 1]; - /* give hook a chance in case parameter is dynamic */ - if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL) - (*paramInfo->paramFetch)(paramInfo, thisParamId); + /* give hook a chance in case parameter is dynamic */ + if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL) + (*paramInfo->paramFetch)(paramInfo, thisParamId); - if (OidIsValid(prm->ptype)) { - /* safety check in case hook did something unexpected */ - if (prm->ptype != expression->paramtype) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), + if (OidIsValid(prm->ptype)) { + /* safety check in case hook did something unexpected */ + if (prm->ptype != expression->paramtype) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)", - thisParamId, - format_type_be(prm->ptype), - format_type_be(expression->paramtype)))); + thisParamId, + format_type_be(prm->ptype), + format_type_be(expression->paramtype)))); - *isNull = prm->isnull; + *isNull = prm->isnull; if (prm->tabInfo && prm->tabInfo->isnestedtable && plpgsql_estate) { plpgsql_estate->curr_nested_table_type = prm->ptype; plpgsql_estate->curr_nested_table_layers = prm->tabInfo->tableOfLayers; } - /* copy cursor option from param to econtext */ - if (econtext->is_cursor && prm->ptype == REFCURSOROID) { - CopyCursorInfoData(&econtext->cursor_data, &prm->cursor_data); - econtext->dno = thisParamId - 1; - } - return prm->value; - } - } + /* copy cursor option from param to econtext */ + if (econtext->is_cursor && prm->ptype == REFCURSOROID) { + CopyCursorInfoData(&econtext->cursor_data, &prm->cursor_data); + econtext->dno = thisParamId - 1; + } + return prm->value; + } + } - ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("no value found for parameter %d", thisParamId))); - return (Datum)0; /* keep compiler quiet */ + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("no value found for parameter %d", thisParamId))); + return (Datum)0; /* keep compiler quiet */ } void initExecTableOfIndexInfo(ExecTableOfIndexInfo* execTableOfIndexInfo, ExprContext* econtext) { - execTableOfIndexInfo->econtext = econtext; - execTableOfIndexInfo->tableOfIndex = NULL; - execTableOfIndexInfo->tableOfIndexType = InvalidOid; - execTableOfIndexInfo->isnestedtable = false; - execTableOfIndexInfo->tableOfLayers = 0; - execTableOfIndexInfo->paramid = -1; - execTableOfIndexInfo->paramtype = InvalidOid; + execTableOfIndexInfo->econtext = econtext; + execTableOfIndexInfo->tableOfIndex = NULL; + execTableOfIndexInfo->tableOfIndexType = InvalidOid; + execTableOfIndexInfo->isnestedtable = false; + execTableOfIndexInfo->tableOfLayers = 0; + execTableOfIndexInfo->paramid = -1; + execTableOfIndexInfo->paramtype = InvalidOid; } /* this function is only used for getting table of index inout param */ static bool get_tableofindex_param(Node* node, ExecTableOfIndexInfo* execTableOfIndexInfo) { - if (node == NULL) - return false; - if (IsA(node, Param)) { - execTableOfIndexInfo->paramid = ((Param*)node)->paramid; - execTableOfIndexInfo->paramtype = ((Param*)node)->paramtype; - return true; - } - return false; + if (node == NULL) + return false; + if (IsA(node, Param)) { + execTableOfIndexInfo->paramid = ((Param*)node)->paramid; + execTableOfIndexInfo->paramtype = ((Param*)node)->paramtype; + return true; + } + return false; } static bool IsTableOfFunc(Oid funcOid) { - const Oid array_function_start_oid = 7881; - const Oid array_function_end_oid = 7892; - const Oid array_indexby_delete_oid = 7896; - return (funcOid >= array_function_start_oid && funcOid <= array_function_end_oid) || - funcOid == array_indexby_delete_oid; + const Oid array_function_start_oid = 7881; + const Oid array_function_end_oid = 7892; + const Oid array_indexby_delete_oid = 7896; + return (funcOid >= array_function_start_oid && funcOid <= array_function_end_oid) || + funcOid == array_indexby_delete_oid; } /* ---------------------------------------------------------------- - * ExecEvalParamExternTableOfIndex - * - * Returns the value of a PARAM_EXTERN table of index and type parameter . - * ---------------------------------------------------------------- - */ +* ExecEvalParamExternTableOfIndex +* +* Returns the value of a PARAM_EXTERN table of index and type parameter . +* ---------------------------------------------------------------- +*/ void ExecEvalParamExternTableOfIndex(Node* node, ExecTableOfIndexInfo* execTableOfIndexInfo) { - if (get_tableofindex_param(node, execTableOfIndexInfo)) { - ExecEvalParamExternTableOfIndexById(execTableOfIndexInfo); - } + if (get_tableofindex_param(node, execTableOfIndexInfo)) { + ExecEvalParamExternTableOfIndexById(execTableOfIndexInfo); + } } bool ExecEvalParamExternTableOfIndexById(ExecTableOfIndexInfo* execTableOfIndexInfo) { - if (execTableOfIndexInfo->paramid == -1) { - return false; - } + if (execTableOfIndexInfo->paramid == -1) { + return false; + } - int thisParamId = execTableOfIndexInfo->paramid; - ParamListInfo paramInfo = execTableOfIndexInfo->econtext->ecxt_param_list_info; + int thisParamId = execTableOfIndexInfo->paramid; + ParamListInfo paramInfo = execTableOfIndexInfo->econtext->ecxt_param_list_info; - /* - * PARAM_EXTERN parameters must be sought in ecxt_param_list_info. - */ - if (paramInfo && thisParamId > 0 && thisParamId <= paramInfo->numParams) { - ParamExternData* prm = ¶mInfo->params[thisParamId - 1]; + /* + * PARAM_EXTERN parameters must be sought in ecxt_param_list_info. + */ + if (paramInfo && thisParamId > 0 && thisParamId <= paramInfo->numParams) { + ParamExternData* prm = ¶mInfo->params[thisParamId - 1]; - /* give hook a chance in case parameter is dynamic */ - if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL) - (*paramInfo->paramFetch)(paramInfo, thisParamId); + /* give hook a chance in case parameter is dynamic */ + if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL) + (*paramInfo->paramFetch)(paramInfo, thisParamId); - if (OidIsValid(prm->ptype) && prm->tabInfo != NULL && - prm->tabInfo->tableOfIndex != NULL && OidIsValid(prm->tabInfo->tableOfIndexType)) { - /* safety check in case hook did something unexpected */ - if (prm->ptype != execTableOfIndexInfo->paramtype) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), + if (OidIsValid(prm->ptype) && prm->tabInfo != NULL && + prm->tabInfo->tableOfIndex != NULL && OidIsValid(prm->tabInfo->tableOfIndexType)) { + /* safety check in case hook did something unexpected */ + if (prm->ptype != execTableOfIndexInfo->paramtype) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)", - thisParamId, - format_type_be(prm->ptype), - format_type_be(execTableOfIndexInfo->paramtype)))); - execTableOfIndexInfo->tableOfIndexType = prm->tabInfo->tableOfIndexType; - execTableOfIndexInfo->isnestedtable = prm->tabInfo->isnestedtable; - execTableOfIndexInfo->tableOfLayers = prm->tabInfo->tableOfLayers; - execTableOfIndexInfo->tableOfIndex = prm->tabInfo->tableOfIndex; - return true; - } - } - return false; + thisParamId, + format_type_be(prm->ptype), + format_type_be(execTableOfIndexInfo->paramtype)))); + execTableOfIndexInfo->tableOfIndexType = prm->tabInfo->tableOfIndexType; + execTableOfIndexInfo->isnestedtable = prm->tabInfo->isnestedtable; + execTableOfIndexInfo->tableOfLayers = prm->tabInfo->tableOfLayers; + execTableOfIndexInfo->tableOfIndex = prm->tabInfo->tableOfIndex; + return true; + } + } + return false; } /* ---------------------------------------------------------------- - * ExecEvalOper / ExecEvalFunc support routines - * ---------------------------------------------------------------- - */ +* ExecEvalOper / ExecEvalFunc support routines +* ---------------------------------------------------------------- +*/ /* - * GetAttributeByName - * GetAttributeByNum - * - * These functions return the value of the requested attribute - * out of the given tuple Datum. - * C functions which take a tuple as an argument are expected - * to use these. Ex: overpaid(EMP) might call GetAttributeByNum(). - * Note: these are actually rather slow because they do a typcache - * lookup on each call. - */ +* GetAttributeByName +* GetAttributeByNum +* +* These functions return the value of the requested attribute +* out of the given tuple Datum. +* C functions which take a tuple as an argument are expected +* to use these. Ex: overpaid(EMP) might call GetAttributeByNum(). +* Note: these are actually rather slow because they do a typcache +* lookup on each call. +*/ Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno, bool* isNull) { - Datum result; - Oid tupType; - int32 tupTypmod; - TupleDesc tupDesc; - HeapTupleData tmptup; + Datum result; + Oid tupType; + int32 tupTypmod; + TupleDesc tupDesc; + HeapTupleData tmptup; - if (!AttributeNumberIsValid(attrno)) - ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid attribute number %d", attrno))); + if (!AttributeNumberIsValid(attrno)) + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid attribute number %d", attrno))); - if (isNull == NULL) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + if (isNull == NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("a NULL isNull pointer was passed when get attribute by number."))); - if (tuple == NULL) { - /* Kinda bogus but compatible with old behavior... */ - *isNull = true; - return (Datum)0; - } + if (tuple == NULL) { + /* Kinda bogus but compatible with old behavior... */ + *isNull = true; + return (Datum)0; + } - tupType = HeapTupleHeaderGetTypeId(tuple); - tupTypmod = HeapTupleHeaderGetTypMod(tuple); - tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupType = HeapTupleHeaderGetTypeId(tuple); + tupTypmod = HeapTupleHeaderGetTypMod(tuple); + tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - /* - * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set all - * the fields in the struct just in case user tries to inspect system - * columns. - */ - tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); - ItemPointerSetInvalid(&(tmptup.t_self)); - tmptup.t_tableOid = InvalidOid; - tmptup.t_bucketId = InvalidBktId; - HeapTupleSetZeroBase(&tmptup); + /* + * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set all + * the fields in the struct just in case user tries to inspect system + * columns. + */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); + ItemPointerSetInvalid(&(tmptup.t_self)); + tmptup.t_tableOid = InvalidOid; + tmptup.t_bucketId = InvalidBktId; + HeapTupleSetZeroBase(&tmptup); #ifdef PGXC - tmptup.t_xc_node_id = 0; + tmptup.t_xc_node_id = 0; #endif - tmptup.t_data = tuple; + tmptup.t_data = tuple; - if (attrno == -3 || attrno == -5) { - elog(WARNING, "system attribute xmin or xmax, the results about this attribute are untrustworthy."); - } - result = tableam_tops_tuple_getattr(&tmptup, attrno, tupDesc, isNull); + if (attrno == -3 || attrno == -5) { + elog(WARNING, "system attribute xmin or xmax, the results about this attribute are untrustworthy."); + } + result = tableam_tops_tuple_getattr(&tmptup, attrno, tupDesc, isNull); - ReleaseTupleDesc(tupDesc); + ReleaseTupleDesc(tupDesc); - return result; + return result; } Datum GetAttributeByName(HeapTupleHeader tuple, const char* attname, bool* isNull) { - AttrNumber attrno; - Datum result; - Oid tupType; - int32 tupTypmod; - TupleDesc tupDesc; - HeapTupleData tmptup; - int i; + AttrNumber attrno; + Datum result; + Oid tupType; + int32 tupTypmod; + TupleDesc tupDesc; + HeapTupleData tmptup; + int i; - if (attname == NULL) - ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid null attribute name"))); + if (attname == NULL) + ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid null attribute name"))); - if (isNull == NULL) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + if (isNull == NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("a NULL isNull pointer was passed when get attribute by name."))); - if (tuple == NULL) { - /* Kinda bogus but compatible with old behavior... */ - *isNull = true; - return (Datum)0; - } + if (tuple == NULL) { + /* Kinda bogus but compatible with old behavior... */ + *isNull = true; + return (Datum)0; + } - tupType = HeapTupleHeaderGetTypeId(tuple); - tupTypmod = HeapTupleHeaderGetTypMod(tuple); - tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupType = HeapTupleHeaderGetTypeId(tuple); + tupTypmod = HeapTupleHeaderGetTypMod(tuple); + tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - attrno = InvalidAttrNumber; - for (i = 0; i < tupDesc->natts; i++) { - if (namestrcmp(&(tupDesc->attrs[i].attname), attname) == 0) { - attrno = tupDesc->attrs[i].attnum; - break; - } - } + attrno = InvalidAttrNumber; + for (i = 0; i < tupDesc->natts; i++) { + if (namestrcmp(&(tupDesc->attrs[i].attname), attname) == 0) { + attrno = tupDesc->attrs[i].attnum; + break; + } + } - if (attrno == InvalidAttrNumber) - ereport(ERROR, - (errcode(ERRCODE_INVALID_ATTRIBUTE), + if (attrno == InvalidAttrNumber) + ereport(ERROR, + (errcode(ERRCODE_INVALID_ATTRIBUTE), errmodule(MOD_EXECUTOR), errmsg("attribute \"%s\" does not exist", attname))); - /* - * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set all - * the fields in the struct just in case user tries to inspect system - * columns. - */ - tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); - ItemPointerSetInvalid(&(tmptup.t_self)); - tmptup.t_tableOid = InvalidOid; - tmptup.t_bucketId = InvalidBktId; - HeapTupleSetZeroBase(&tmptup); + /* + * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set all + * the fields in the struct just in case user tries to inspect system + * columns. + */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); + ItemPointerSetInvalid(&(tmptup.t_self)); + tmptup.t_tableOid = InvalidOid; + tmptup.t_bucketId = InvalidBktId; + HeapTupleSetZeroBase(&tmptup); #ifdef PGXC - tmptup.t_xc_node_id = 0; + tmptup.t_xc_node_id = 0; #endif - tmptup.t_data = tuple; + tmptup.t_data = tuple; - if (attrno == -3 || attrno == -5) { - elog(WARNING, "system attribute \"%s\", the results about this attribute are untrustworthy.", attname); - } - result = tableam_tops_tuple_getattr(&tmptup, attrno, tupDesc, isNull); + if (attrno == -3 || attrno == -5) { + elog(WARNING, "system attribute \"%s\", the results about this attribute are untrustworthy.", attname); + } + result = tableam_tops_tuple_getattr(&tmptup, attrno, tupDesc, isNull); - ReleaseTupleDesc(tupDesc); + ReleaseTupleDesc(tupDesc); - return result; + return result; } /* - * Find the real function return type based on the actual func args' types. - * @inPara arg_num: the number of func's args. - * @inPara actual_arg_types: the type array of actual func args'. - * @inPara fcache: the FuncExprState of this functin. - * @return Oid: the real func return type. - */ +* Find the real function return type based on the actual func args' types. +* @inPara arg_num: the number of func's args. +* @inPara actual_arg_types: the type array of actual func args'. +* @inPara fcache: the FuncExprState of this functin. +* @return Oid: the real func return type. +*/ static Oid getRealFuncRetype(int arg_num, Oid* actual_arg_types, FuncExprState* fcache) { - Oid funcid = fcache->func.fn_oid; - Oid rettype = fcache->func.fn_rettype; + Oid funcid = fcache->func.fn_oid; + Oid rettype = fcache->func.fn_rettype; - /* Find the declared arg types in PROCOID by funcid. */ - HeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); - if (!HeapTupleIsValid(proctup)) - ereport(ERROR, - (errcode(ERRCODE_CACHE_LOOKUP_FAILED), + /* Find the declared arg types in PROCOID by funcid. */ + HeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); + if (!HeapTupleIsValid(proctup)) + ereport(ERROR, + (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmodule(MOD_EXECUTOR), errmsg("cache lookup failed for function %u", funcid))); - oidvector* proargs = ProcedureGetArgTypes(proctup); - Oid* declared_arg_types = proargs->values; + oidvector* proargs = ProcedureGetArgTypes(proctup); + Oid* declared_arg_types = proargs->values; - /* Find the real return type based on the declared arg types and actual arg types.*/ - rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, arg_num, rettype, false); + /* Find the real return type based on the declared arg types and actual arg types.*/ + rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, arg_num, rettype, false); - ReleaseSysCache(proctup); - return rettype; + ReleaseSysCache(proctup); + return rettype; } /* - * Check whether the function is a set function supported by the vector engine. - */ +* Check whether the function is a set function supported by the vector engine. +*/ static bool isVectorEngineSupportSetFunc(Oid funcid) { - switch (funcid) { - case OID_REGEXP_SPLIT_TO_TABLE: // regexp_split_to_table - case OID_REGEXP_SPLIT_TO_TABLE_NO_FLAG: // regexp_split_to_table - case OID_ARRAY_UNNEST: // unnest - return true; - break; - default: - return false; - break; - } + switch (funcid) { + case OID_REGEXP_SPLIT_TO_TABLE: // regexp_split_to_table + case OID_REGEXP_SPLIT_TO_TABLE_NO_FLAG: // regexp_split_to_table + case OID_ARRAY_UNNEST: // unnest + return true; + break; + default: + return false; + break; + } } /* - * init_fcache - initialize a FuncExprState node during first use - */ +* init_fcache - initialize a FuncExprState node during first use +*/ template static void init_fcache( - Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF) + Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF) { - AclResult aclresult; - MemoryContext oldcontext; + AclResult aclresult; + MemoryContext oldcontext; Form_pg_proc procStruct; HeapTuple procTup; Oid definer = GetUserId(); - + if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) { /* Get the function's pg_proc entry */ @@ -1549,28 +1550,28 @@ static void init_fcache( ReleaseSysCache(procTup); } - /* Check permission to call function */ - aclresult = pg_proc_aclcheck(foid, definer, ACL_EXECUTE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(foid, definer, ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); - /* - * Safety check on nargs. Under normal circumstances this should never - * fail, as parser should check sooner. But possibly it might fail if - * server has been compiled with FUNC_MAX_ARGS smaller than some functions - * declared in pg_proc? - */ - if (list_length(fcache->args) > FUNC_MAX_ARGS) - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (list_length(fcache->args) > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg_plural("cannot pass more than %d argument to a function", - "cannot pass more than %d arguments to a function", - FUNC_MAX_ARGS, - FUNC_MAX_ARGS))); + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); - /* Set up the primary fmgr lookup information */ - fmgr_info_cxt(foid, &(fcache->func), fcacheCxt); - fmgr_info_set_expr((Node*)fcache->xprstate.expr, &(fcache->func)); + /* Set up the primary fmgr lookup information */ + fmgr_info_cxt(foid, &(fcache->func), fcacheCxt); + fmgr_info_set_expr((Node*)fcache->xprstate.expr, &(fcache->func)); /* palloc args in fcache's context */ oldcontext = MemoryContextSwitchTo(fcacheCxt); @@ -1593,464 +1594,478 @@ static void init_fcache( InitFunctionCallInfoData( fcache->fcinfo_data, &(fcache->func), list_length(fcache->args), input_collation, NULL, NULL); - if (vectorized) { - int nargs = list_length(fcache->args); - ListCell* cell = NULL; - GenericFunRuntime* genericRuntime = NULL; - errno_t rc; + if (vectorized) { + int nargs = list_length(fcache->args); + ListCell* cell = NULL; + GenericFunRuntime* genericRuntime = NULL; + errno_t rc; - if (fcache->fcinfo_data.flinfo->genericRuntime == NULL) { - genericRuntime = (GenericFunRuntime*)palloc0(sizeof(GenericFunRuntime)); - InitGenericFunRuntimeInfo(*genericRuntime, nargs); - fcache->fcinfo_data.flinfo->genericRuntime = genericRuntime; - } else { - genericRuntime = fcache->fcinfo_data.flinfo->genericRuntime; + if (fcache->fcinfo_data.flinfo->genericRuntime == NULL) { + genericRuntime = (GenericFunRuntime*)palloc0(sizeof(GenericFunRuntime)); + InitGenericFunRuntimeInfo(*genericRuntime, nargs); + fcache->fcinfo_data.flinfo->genericRuntime = genericRuntime; + } else { + genericRuntime = fcache->fcinfo_data.flinfo->genericRuntime; - /* if internalFinfo is not null, release the internalFinfo's memory and set the pointer to null */ - if (genericRuntime->internalFinfo != NULL) { - FreeFunctionCallInfoData(*(genericRuntime->internalFinfo)); - genericRuntime->internalFinfo = NULL; - } + /* if internalFinfo is not null, release the internalFinfo's memory and set the pointer to null */ + if (genericRuntime->internalFinfo != NULL) { + FreeFunctionCallInfoData(*(genericRuntime->internalFinfo)); + genericRuntime->internalFinfo = NULL; + } - /* reset the memory for reuse */ - rc = memset_s(genericRuntime->args, - sizeof(GenericFunRuntimeArg) * genericRuntime->compacity, - 0, - sizeof(GenericFunRuntimeArg) * genericRuntime->compacity); - securec_check(rc, "\0", "\0"); + /* reset the memory for reuse */ + rc = memset_s(genericRuntime->args, + sizeof(GenericFunRuntimeArg) * genericRuntime->compacity, + 0, + sizeof(GenericFunRuntimeArg) * genericRuntime->compacity); + securec_check(rc, "\0", "\0"); - rc = memset_s(genericRuntime->inputargs, - sizeof(Datum) * genericRuntime->compacity, - 0, - sizeof(Datum) * genericRuntime->compacity); - securec_check(rc, "\0", "\0"); + rc = memset_s(genericRuntime->inputargs, + sizeof(Datum) * genericRuntime->compacity, + 0, + sizeof(Datum) * genericRuntime->compacity); + securec_check(rc, "\0", "\0"); - rc = memset_s(genericRuntime->nulls, - sizeof(bool) * genericRuntime->compacity, - 0, - sizeof(bool) * genericRuntime->compacity); - securec_check(rc, "\0", "\0"); + rc = memset_s(genericRuntime->nulls, + sizeof(bool) * genericRuntime->compacity, + 0, + sizeof(bool) * genericRuntime->compacity); + securec_check(rc, "\0", "\0"); - /* we have to adjust the GenericFunRuntimeArg when - * a) nargs is larger than genericRuntime->compacity, which means the allocated memory is not enough to hold - * all the argumnets here, we should enlarge the memory. - * b) nargs is less than VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS while the allocated memory is much more - * than that. As VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS is already enough in most senerios, we should - * reduce the memory. - * - * NOTE: To avoid memory wasting and memory fragments, we free and initilized a new GenericFunRuntimeArg. - */ - if (unlikely(nargs > genericRuntime->compacity) || - (unlikely(genericRuntime->compacity > VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS) && - nargs <= VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS)) { - FreeGenericFunRuntimeInfo(*genericRuntime); - InitGenericFunRuntimeInfo(*genericRuntime, nargs); - } - } + /* we have to adjust the GenericFunRuntimeArg when + * a) nargs is larger than genericRuntime->compacity, which means the allocated memory is not enough to hold + * all the argumnets here, we should enlarge the memory. + * b) nargs is less than VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS while the allocated memory is much more + * than that. As VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS is already enough in most senerios, we should + * reduce the memory. + * + * NOTE: To avoid memory wasting and memory fragments, we free and initilized a new GenericFunRuntimeArg. + */ + if (unlikely(nargs > genericRuntime->compacity) || + (unlikely(genericRuntime->compacity > VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS) && + nargs <= VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS)) { + FreeGenericFunRuntimeInfo(*genericRuntime); + InitGenericFunRuntimeInfo(*genericRuntime, nargs); + } + } - ScalarVector* pVector = New(CurrentMemoryContext) ScalarVector[nargs]; + ScalarVector* pVector = New(CurrentMemoryContext) ScalarVector[nargs]; - int i = 0; - if (fcache->args && fcache->args->length > 0) { - Oid* actual_arg_types = (Oid*)palloc0(fcache->args->length * sizeof(Oid)); + int i = 0; + if (fcache->args && fcache->args->length > 0) { + Oid* actual_arg_types = (Oid*)palloc0(fcache->args->length * sizeof(Oid)); - foreach (cell, fcache->args) { - ExprState* argstate = (ExprState*)lfirst(cell); - Oid funcrettype; - TupleDesc tupdesc; - ScalarDesc desc; + foreach (cell, fcache->args) { + ExprState* argstate = (ExprState*)lfirst(cell); + Oid funcrettype; + TupleDesc tupdesc; + ScalarDesc desc; - (void)get_expr_result_type((Node*)argstate->expr, &funcrettype, &tupdesc); + (void)get_expr_result_type((Node*)argstate->expr, &funcrettype, &tupdesc); - desc.typeId = funcrettype; - desc.encoded = COL_IS_ENCODE(funcrettype); - fcache->fcinfo_data.flinfo->genericRuntime->args[i].argType = funcrettype; + desc.typeId = funcrettype; + desc.encoded = COL_IS_ENCODE(funcrettype); + fcache->fcinfo_data.flinfo->genericRuntime->args[i].argType = funcrettype; - pVector[i].init(CurrentMemoryContext, desc); - /* Record the real arg types from sub functions. */ - actual_arg_types[i] = funcrettype; - i++; - } + pVector[i].init(CurrentMemoryContext, desc); + /* Record the real arg types from sub functions. */ + actual_arg_types[i] = funcrettype; + i++; + } - /* Find the real return type for func with return type like ANYELEMENT. */ - fcache->fcinfo_data.flinfo->fn_rettype = getRealFuncRetype(i, actual_arg_types, fcache); - pfree_ext(actual_arg_types); - } - fcache->fcinfo_data.argVector = pVector; - } - (void)MemoryContextSwitchTo(oldcontext); + /* Find the real return type for func with return type like ANYELEMENT. */ + fcache->fcinfo_data.flinfo->fn_rettype = getRealFuncRetype(i, actual_arg_types, fcache); + pfree_ext(actual_arg_types); + } + fcache->fcinfo_data.argVector = pVector; + } + (void)MemoryContextSwitchTo(oldcontext); - if (vectorized) { - if (fcache->func.fn_retset == true) { - if (!isVectorEngineSupportSetFunc(fcache->func.fn_oid)) { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /* If function returns set, check if that's allowed by caller */ + if (fcache->xprstate.is_flt_frame) { + if (fcache->func.fn_retset && !allowSRF) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + /* Otherwise, ExecInitExpr should have marked the fcache correctly */ + Assert(fcache->func.fn_retset == fcache->funcReturnsSet); + } + + if (vectorized) { + if (fcache->func.fn_retset == true) { + if (!isVectorEngineSupportSetFunc(fcache->func.fn_oid)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_EXECUTOR), errmsg("set-return function not supported in vector eninge"))); - } - } - fcache->funcResultDesc = NULL; - } else { - /* If function returns set, prepare expected tuple descriptor */ - if (fcache->func.fn_retset && needDescForSRF) { - TypeFuncClass functypclass; - Oid funcrettype; - TupleDesc tupdesc; - MemoryContext oldmemcontext; + } + } + fcache->funcResultDesc = NULL; + } else { + /* If function returns set, prepare expected tuple descriptor */ + if (fcache->func.fn_retset && needDescForSRF) { + TypeFuncClass functypclass; + Oid funcrettype; + TupleDesc tupdesc; + MemoryContext oldmemcontext; - functypclass = get_expr_result_type(fcache->func.fn_expr, &funcrettype, &tupdesc); + functypclass = get_expr_result_type(fcache->func.fn_expr, &funcrettype, &tupdesc); - /* Must save tupdesc in fcache's context */ - oldmemcontext = MemoryContextSwitchTo(fcacheCxt); + /* Must save tupdesc in fcache's context */ + oldmemcontext = MemoryContextSwitchTo(fcacheCxt); - if (functypclass == TYPEFUNC_COMPOSITE) { - /* Composite data type, e.g. a table's row type */ - Assert(tupdesc); - /* Must copy it out of typcache for safety */ - fcache->funcResultDesc = CreateTupleDescCopy(tupdesc); - fcache->funcReturnsTuple = true; - } else if (functypclass == TYPEFUNC_SCALAR) { - /* Base data type, i.e. scalar */ - tupdesc = CreateTemplateTupleDesc(1, false, TableAmHeap); - TupleDescInitEntry(tupdesc, (AttrNumber)1, NULL, funcrettype, -1, 0); - fcache->funcResultDesc = tupdesc; - fcache->funcReturnsTuple = false; - } else if (functypclass == TYPEFUNC_RECORD) { - /* This will work if function doesn't need an expectedDesc */ - fcache->funcResultDesc = NULL; - fcache->funcReturnsTuple = true; - } else { - /* Else, we will fail if function needs an expectedDesc */ - fcache->funcResultDesc = NULL; - } + if (functypclass == TYPEFUNC_COMPOSITE) { + /* Composite data type, e.g. a table's row type */ + Assert(tupdesc); + /* Must copy it out of typcache for safety */ + fcache->funcResultDesc = CreateTupleDescCopy(tupdesc); + fcache->funcReturnsTuple = true; + } else if (functypclass == TYPEFUNC_SCALAR) { + /* Base data type, i.e. scalar */ + tupdesc = CreateTemplateTupleDesc(1, false, TableAmHeap); + TupleDescInitEntry(tupdesc, (AttrNumber)1, NULL, funcrettype, -1, 0); + fcache->funcResultDesc = tupdesc; + fcache->funcReturnsTuple = false; + } else if (functypclass == TYPEFUNC_RECORD) { + /* This will work if function doesn't need an expectedDesc */ + fcache->funcResultDesc = NULL; + fcache->funcReturnsTuple = true; + } else { + /* Else, we will fail if function needs an expectedDesc */ + fcache->funcResultDesc = NULL; + } - MemoryContextSwitchTo(oldmemcontext); - } else - fcache->funcResultDesc = NULL; - } + MemoryContextSwitchTo(oldmemcontext); + } else + fcache->funcResultDesc = NULL; + } + + /* Initialize additional state */ + fcache->funcResultStore = NULL; + fcache->funcResultSlot = NULL; + fcache->setArgsValid = false; + fcache->shutdown_reg = false; + if(fcache->xprstate.is_flt_frame){ + fcache->is_plpgsql_func_with_outparam = is_function_with_plpgsql_language_and_outparam(fcache->func.fn_oid); + fcache->has_refcursor = func_has_refcursor_args(fcache->func.fn_oid, &fcache->fcinfo_data); + } - /* Initialize additional state */ - fcache->funcResultStore = NULL; - fcache->funcResultSlot = NULL; - fcache->setArgsValid = false; - fcache->shutdown_reg = false; } void initVectorFcache(Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt) { - init_fcache(foid, input_collation, fcache, fcacheCxt, false, false); + init_fcache(foid, input_collation, fcache, fcacheCxt, false, false); } /* - * callback function in case a FuncExpr returning a set needs to be shut down - * before it has been run to completion - */ +* callback function in case a FuncExpr returning a set needs to be shut down +* before it has been run to completion +*/ static void ShutdownFuncExpr(Datum arg) { - FuncExprState* fcache = (FuncExprState*)DatumGetPointer(arg); + FuncExprState* fcache = (FuncExprState*)DatumGetPointer(arg); - /* If we have a slot, make sure it's let go of any tuplestore pointer */ - if (fcache->funcResultSlot) - (void)ExecClearTuple(fcache->funcResultSlot); + /* If we have a slot, make sure it's let go of any tuplestore pointer */ + if (fcache->funcResultSlot) + (void)ExecClearTuple(fcache->funcResultSlot); - /* Release any open tuplestore */ - if (fcache->funcResultStore) - tuplestore_end(fcache->funcResultStore); - fcache->funcResultStore = NULL; + /* Release any open tuplestore */ + if (fcache->funcResultStore) + tuplestore_end(fcache->funcResultStore); + fcache->funcResultStore = NULL; - /* Clear any active set-argument state */ - fcache->setArgsValid = false; + /* Clear any active set-argument state */ + fcache->setArgsValid = false; - /* execUtils will deregister the callback... */ - fcache->shutdown_reg = false; + /* execUtils will deregister the callback... */ + fcache->shutdown_reg = false; } /* - * get_cached_rowtype: utility function to lookup a rowtype tupdesc - * - * type_id, typmod: identity of the rowtype - * cache_field: where to cache the TupleDesc pointer in expression state node - * (field must be initialized to NULL) - * econtext: expression context we are executing in - * - * NOTE: because the shutdown callback will be called during plan rescan, - * must be prepared to re-do this during any node execution; cannot call - * just once during expression initialization - */ +* get_cached_rowtype: utility function to lookup a rowtype tupdesc +* +* type_id, typmod: identity of the rowtype +* cache_field: where to cache the TupleDesc pointer in expression state node +* (field must be initialized to NULL) +* econtext: expression context we are executing in +* +* NOTE: because the shutdown callback will be called during plan rescan, +* must be prepared to re-do this during any node execution; cannot call +* just once during expression initialization +*/ static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod, TupleDesc* cache_field, ExprContext* econtext) { - TupleDesc tupDesc = *cache_field; + TupleDesc tupDesc = *cache_field; - /* Do lookup if no cached value or if requested type changed */ - if (tupDesc == NULL || type_id != tupDesc->tdtypeid || typmod != tupDesc->tdtypmod) { - tupDesc = lookup_rowtype_tupdesc(type_id, typmod); + /* Do lookup if no cached value or if requested type changed */ + if (tupDesc == NULL || type_id != tupDesc->tdtypeid || typmod != tupDesc->tdtypmod) { + tupDesc = lookup_rowtype_tupdesc(type_id, typmod); - if (*cache_field) { - /* Release old tupdesc; but callback is already registered */ - ReleaseTupleDesc(*cache_field); - } else { - /* Need to register shutdown callback to release tupdesc */ - RegisterExprContextCallback(econtext, ShutdownTupleDescRef, PointerGetDatum(cache_field)); - } - *cache_field = tupDesc; - } - return tupDesc; + if (*cache_field) { + /* Release old tupdesc; but callback is already registered */ + ReleaseTupleDesc(*cache_field); + } else { + /* Need to register shutdown callback to release tupdesc */ + RegisterExprContextCallback(econtext, ShutdownTupleDescRef, PointerGetDatum(cache_field)); + } + *cache_field = tupDesc; + } + return tupDesc; } /* - * Callback function to release a tupdesc refcount at expression tree shutdown - */ +* Callback function to release a tupdesc refcount at expression tree shutdown +*/ static void ShutdownTupleDescRef(Datum arg) { - TupleDesc* cache_field = (TupleDesc*)DatumGetPointer(arg); + TupleDesc* cache_field = (TupleDesc*)DatumGetPointer(arg); - if (*cache_field) - ReleaseTupleDesc(*cache_field); - *cache_field = NULL; + if (*cache_field) + ReleaseTupleDesc(*cache_field); + *cache_field = NULL; } /* - * Evaluate arguments for a function. - */ +* Evaluate arguments for a function. +*/ template static ExprDoneCond ExecEvalFuncArgs( - FunctionCallInfo fcinfo, List* argList, ExprContext* econtext, int* plpgsql_var_dno) + FunctionCallInfo fcinfo, List* argList, ExprContext* econtext, int* plpgsql_var_dno) { - ExprDoneCond argIsDone; - int i; - ListCell* arg = NULL; + ExprDoneCond argIsDone; + int i; + ListCell* arg = NULL; - argIsDone = ExprSingleResult; /* default assumption */ + argIsDone = ExprSingleResult; /* default assumption */ - i = 0; - econtext->is_cursor = false; - u_sess->plsql_cxt.func_tableof_index = NIL; - bool is_have_huge_clob = false; - foreach (arg, argList) { - ExprState* argstate = (ExprState*)lfirst(arg); - ExprDoneCond thisArgIsDone; + i = 0; + econtext->is_cursor = false; + u_sess->plsql_cxt.func_tableof_index = NIL; + bool is_have_huge_clob = false; + foreach (arg, argList) { + ExprState* argstate = (ExprState*)lfirst(arg); + ExprDoneCond thisArgIsDone; - if (has_refcursor && argstate->resultType == REFCURSOROID) - econtext->is_cursor = true; - fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo->argnull[i], &thisArgIsDone); - ExecTableOfIndexInfo execTableOfIndexInfo; - initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext); - ExecEvalParamExternTableOfIndex((Node*)argstate->expr, &execTableOfIndexInfo); - if (execTableOfIndexInfo.tableOfIndex != NULL) { - MemoryContext oldCxt = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); - PLpgSQL_func_tableof_index* func_tableof = - (PLpgSQL_func_tableof_index*)palloc0(sizeof(PLpgSQL_func_tableof_index)); - func_tableof->varno = i; - func_tableof->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; - func_tableof->tableOfIndex = copyTableOfIndex(execTableOfIndexInfo.tableOfIndex); - u_sess->plsql_cxt.func_tableof_index = lappend(u_sess->plsql_cxt.func_tableof_index, func_tableof); - MemoryContextSwitchTo(oldCxt); - } + if (has_refcursor && argstate->resultType == REFCURSOROID) + econtext->is_cursor = true; + fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo->argnull[i], &thisArgIsDone); + ExecTableOfIndexInfo execTableOfIndexInfo; + initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext); + ExecEvalParamExternTableOfIndex((Node*)argstate->expr, &execTableOfIndexInfo); + if (execTableOfIndexInfo.tableOfIndex != NULL) { + MemoryContext oldCxt = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); + PLpgSQL_func_tableof_index* func_tableof = + (PLpgSQL_func_tableof_index*)palloc0(sizeof(PLpgSQL_func_tableof_index)); + func_tableof->varno = i; + func_tableof->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; + func_tableof->tableOfIndex = copyTableOfIndex(execTableOfIndexInfo.tableOfIndex); + u_sess->plsql_cxt.func_tableof_index = lappend(u_sess->plsql_cxt.func_tableof_index, func_tableof); + MemoryContextSwitchTo(oldCxt); + } - if (has_refcursor && econtext->is_cursor && plpgsql_var_dno != NULL) { - plpgsql_var_dno[i] = econtext->dno; - CopyCursorInfoData(&fcinfo->refcursor_data.argCursor[i], &econtext->cursor_data); - } - fcinfo->argTypes[i] = argstate->resultType; - econtext->is_cursor = false; - if (is_huge_clob(fcinfo->argTypes[i], fcinfo->argnull[i], fcinfo->arg[i])) { - is_have_huge_clob = true; - } + if (has_refcursor && econtext->is_cursor && plpgsql_var_dno != NULL) { + plpgsql_var_dno[i] = econtext->dno; + CopyCursorInfoData(&fcinfo->refcursor_data.argCursor[i], &econtext->cursor_data); + } + fcinfo->argTypes[i] = argstate->resultType; + econtext->is_cursor = false; + if (is_huge_clob(fcinfo->argTypes[i], fcinfo->argnull[i], fcinfo->arg[i])) { + is_have_huge_clob = true; + } - if (thisArgIsDone != ExprSingleResult) { - /* - * We allow only one argument to have a set value; we'd need much - * more complexity to keep track of multiple set arguments (cf. - * ExecTargetList) and it doesn't seem worth it. - */ - if (argIsDone != ExprSingleResult) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + if (thisArgIsDone != ExprSingleResult) { + /* + * We allow only one argument to have a set value; we'd need much + * more complexity to keep track of multiple set arguments (cf. + * ExecTargetList) and it doesn't seem worth it. + */ + if (argIsDone != ExprSingleResult) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("functions and operators can take at most one set argument"))); - argIsDone = thisArgIsDone; - } - i++; - } - check_huge_clob_paramter(fcinfo, is_have_huge_clob); + argIsDone = thisArgIsDone; + } + i++; + } + check_huge_clob_paramter(fcinfo, is_have_huge_clob); - Assert(i == fcinfo->nargs); + Assert(i == fcinfo->nargs); - return argIsDone; + return argIsDone; } /* - * ExecPrepareTuplestoreResult - * - * Subroutine for ExecMakeFunctionResult: prepare to extract rows from a - * tuplestore function result. We must set up a funcResultSlot (unless - * already done in a previous call cycle) and verify that the function - * returned the expected tuple descriptor. - */ +* ExecPrepareTuplestoreResult +* +* Subroutine for ExecMakeFunctionResult: prepare to extract rows from a +* tuplestore function result. We must set up a funcResultSlot (unless +* already done in a previous call cycle) and verify that the function +* returned the expected tuple descriptor. +*/ static void ExecPrepareTuplestoreResult( - FuncExprState* fcache, ExprContext* econtext, Tuplestorestate* resultStore, TupleDesc resultDesc) + FuncExprState* fcache, ExprContext* econtext, Tuplestorestate* resultStore, TupleDesc resultDesc) { - fcache->funcResultStore = resultStore; + fcache->funcResultStore = resultStore; - if (fcache->funcResultSlot == NULL) { - /* Create a slot so we can read data out of the tuplestore */ - TupleDesc slotDesc; - MemoryContext oldcontext; + if (fcache->funcResultSlot == NULL) { + /* Create a slot so we can read data out of the tuplestore */ + TupleDesc slotDesc; + MemoryContext oldcontext; - oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt); + oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt); - /* - * If we were not able to determine the result rowtype from context, - * and the function didn't return a tupdesc, we have to fail. - */ - if (fcache->funcResultDesc) - slotDesc = fcache->funcResultDesc; - else if (resultDesc) { - /* don't assume resultDesc is long-lived */ - slotDesc = CreateTupleDescCopy(resultDesc); - } else { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /* + * If we were not able to determine the result rowtype from context, + * and the function didn't return a tupdesc, we have to fail. + */ + if (fcache->funcResultDesc) + slotDesc = fcache->funcResultDesc; + else if (resultDesc) { + /* don't assume resultDesc is long-lived */ + slotDesc = CreateTupleDescCopy(resultDesc); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning setof record called in context that cannot accept type record"))); - slotDesc = NULL; /* keep compiler quiet */ - } + slotDesc = NULL; /* keep compiler quiet */ + } - fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc); - MemoryContextSwitchTo(oldcontext); - } + fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc); + MemoryContextSwitchTo(oldcontext); + } - /* - * If function provided a tupdesc, cross-check it. We only really need to - * do this for functions returning RECORD, but might as well do it always. - */ - if (resultDesc) { - if (fcache->funcResultDesc) - tupledesc_match(fcache->funcResultDesc, resultDesc); + /* + * If function provided a tupdesc, cross-check it. We only really need to + * do this for functions returning RECORD, but might as well do it always. + */ + if (resultDesc) { + if (fcache->funcResultDesc) + tupledesc_match(fcache->funcResultDesc, resultDesc); - /* - * If it is a dynamically-allocated TupleDesc, free it: it is - * typically allocated in a per-query context, so we must avoid - * leaking it across multiple usages. - */ - if (resultDesc->tdrefcount == -1) - FreeTupleDesc(resultDesc); - } + /* + * If it is a dynamically-allocated TupleDesc, free it: it is + * typically allocated in a per-query context, so we must avoid + * leaking it across multiple usages. + */ + if (resultDesc->tdrefcount == -1) + FreeTupleDesc(resultDesc); + } - /* Register cleanup callback if we didn't already */ - if (!fcache->shutdown_reg) { - RegisterExprContextCallback(econtext, ShutdownFuncExpr, PointerGetDatum(fcache)); - fcache->shutdown_reg = true; - } + /* Register cleanup callback if we didn't already */ + if (!fcache->shutdown_reg) { + RegisterExprContextCallback(econtext, ShutdownFuncExpr, PointerGetDatum(fcache)); + fcache->shutdown_reg = true; + } } /* - * Check that function result tuple type (src_tupdesc) matches or can - * be considered to match what the query expects (dst_tupdesc). If - * they don't match, ereport. - * - * We really only care about number of attributes and data type. - * Also, we can ignore type mismatch on columns that are dropped in the - * destination type, so long as the physical storage matches. This is - * helpful in some cases involving out-of-date cached plans. - */ +* Check that function result tuple type (src_tupdesc) matches or can +* be considered to match what the query expects (dst_tupdesc). If +* they don't match, ereport. +* +* We really only care about number of attributes and data type. +* Also, we can ignore type mismatch on columns that are dropped in the +* destination type, so long as the physical storage matches. This is +* helpful in some cases involving out-of-date cached plans. +*/ static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc) { - int i; + int i; - if (dst_tupdesc->natts != src_tupdesc->natts) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), + if (dst_tupdesc->natts != src_tupdesc->natts) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return row and query-specified return row do not match"), errdetail_plural("Returned row contains %d attribute, but query expects %d.", - "Returned row contains %d attributes, but query expects %d.", - src_tupdesc->natts, - src_tupdesc->natts, - dst_tupdesc->natts))); + "Returned row contains %d attributes, but query expects %d.", + src_tupdesc->natts, + src_tupdesc->natts, + dst_tupdesc->natts))); - for (i = 0; i < dst_tupdesc->natts; i++) { - Form_pg_attribute dattr = &dst_tupdesc->attrs[i]; - Form_pg_attribute sattr = &src_tupdesc->attrs[i]; + for (i = 0; i < dst_tupdesc->natts; i++) { + Form_pg_attribute dattr = &dst_tupdesc->attrs[i]; + Form_pg_attribute sattr = &src_tupdesc->attrs[i]; - if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid)) - continue; /* no worries */ - if (!dattr->attisdropped) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), + if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid)) + continue; /* no worries */ + if (!dattr->attisdropped) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return row and query-specified return row do not match"), errdetail("Returned type %s at ordinal position %d, but query expects %s.", - format_type_be(sattr->atttypid), - i + 1, - format_type_be(dattr->atttypid)))); + format_type_be(sattr->atttypid), + i + 1, + format_type_be(dattr->atttypid)))); - if (dattr->attlen != sattr->attlen || dattr->attalign != sattr->attalign) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), + if (dattr->attlen != sattr->attlen || dattr->attalign != sattr->attalign) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return row and query-specified return row do not match"), errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.", i + 1))); - } + } } -static void set_result_for_plpgsql_language_function_with_outparam(FuncExprState *fcache, Datum *result, bool *isNull) +void set_result_for_plpgsql_language_function_with_outparam(FuncExprState *fcache, Datum *result, bool *isNull) { - if (!IsA(fcache->xprstate.expr, FuncExpr)) { - return; - } - FuncExpr *func = (FuncExpr *)fcache->xprstate.expr; - if (!is_function_with_plpgsql_language_and_outparam(func->funcid)) { - return; - } - HeapTupleHeader td = DatumGetHeapTupleHeader(*result); - TupleDesc tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); - HeapTupleData tup; - tup.t_len = HeapTupleHeaderGetDatumLength(td); - tup.t_data = td; - Datum *values = (Datum *)palloc(sizeof(Datum) * tupdesc->natts); - bool *nulls = (bool *)palloc(sizeof(bool) * tupdesc->natts); - heap_deform_tuple(&tup, tupdesc, values, nulls); - *result = values[0]; - *isNull = nulls[0]; - pfree(values); - pfree(nulls); + if (!IsA(fcache->xprstate.expr, FuncExpr)) { + return; + } + FuncExpr *func = (FuncExpr *)fcache->xprstate.expr; + if (!is_function_with_plpgsql_language_and_outparam(func->funcid)) { + return; + } + HeapTupleHeader td = DatumGetHeapTupleHeader(*result); + TupleDesc tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); + HeapTupleData tup; + tup.t_len = HeapTupleHeaderGetDatumLength(td); + tup.t_data = td; + Datum *values = (Datum *)palloc(sizeof(Datum) * tupdesc->natts); + bool *nulls = (bool *)palloc(sizeof(bool) * tupdesc->natts); + heap_deform_tuple(&tup, tupdesc, values, nulls); + *result = values[0]; + *isNull = nulls[0]; + pfree(values); + pfree(nulls); } /* - * ExecMakeFunctionResult - * - * Evaluate the arguments to a function and then the function itself. - * init_fcache is presumed already run on the FuncExprState. - * - * This function handles the most general case, wherein the function or - * one of its arguments can return a set. - * - * Note: This function use template parameter can compile different function, - * reduce the assembly instructions so as to improve performance. - * - * Template parameter: - * @bool has_cursor_return - need store out-args cursor info. - * @bool has_refcursor - need store in-args cursor info. - * @bool isSetReturnFunc - indicate function returns a set. - */ +* ExecMakeFunctionResult +* +* Evaluate the arguments to a function and then the function itself. +* init_fcache is presumed already run on the FuncExprState. +* +* This function handles the most general case, wherein the function or +* one of its arguments can return a set. +* +* Note: This function use template parameter can compile different function, +* reduce the assembly instructions so as to improve performance. +* +* Template parameter: +* @bool has_cursor_return - need store out-args cursor info. +* @bool has_refcursor - need store in-args cursor info. +* @bool isSetReturnFunc - indicate function returns a set. +*/ template static Datum ExecMakeFunctionResult(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - List* arguments = NIL; - Datum result; - FunctionCallInfo fcinfo; - PgStat_FunctionCallUsage fcusage; - ReturnSetInfo rsinfo; /* for functions returning sets */ - ExprDoneCond argDone; - bool hasSetArg = false; - int i; - int* var_dno = NULL; + List* arguments = NIL; + Datum result; + FunctionCallInfo fcinfo; + PgStat_FunctionCallUsage fcusage; + ReturnSetInfo rsinfo; /* for functions returning sets */ + ExprDoneCond argDone; + bool hasSetArg = false; + int i; + int* var_dno = NULL; - econtext->plpgsql_estate = plpgsql_estate; - plpgsql_estate = NULL; + econtext->plpgsql_estate = plpgsql_estate; + plpgsql_estate = NULL; restart: - /* Guard against stack overflow due to overly complex expressions */ - check_stack_depth(); + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); - if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { + if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { /* * Initialize function cache if first time through. The expression node * could be either a FuncExpr or an OpExpr. @@ -2068,18 +2083,18 @@ restart: elog(ERROR, "unrecognized node type: %d", (int)nodeTag(fcache->xprstate.expr)); } } - /* - * If a previous call of the function returned a set result in the form of - * a tuplestore, continue reading rows from the tuplestore until it's - * empty. - */ - if (fcache->funcResultStore) { - /* it was provided before ... */ - if (unlikely(isDone == NULL)) { - ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("set-valued function called in context that cannot accept a set"))); - } - econtext->hasSetResultStore = true; + /* + * If a previous call of the function returned a set result in the form of + * a tuplestore, continue reading rows from the tuplestore until it's + * empty. + */ + if (fcache->funcResultStore) { + /* it was provided before ... */ + if (unlikely(isDone == NULL)) { + ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("set-valued function called in context that cannot accept a set"))); + } + econtext->hasSetResultStore = true; if (tuplestore_gettupleslot(fcache->funcResultStore, true, false, fcache->funcResultSlot)) { *isDone = ExprMultipleResult; if (fcache->funcReturnsTuple) { @@ -2106,38 +2121,38 @@ restart: Assert(!fcache->setArgsValid); } - /* - * arguments is a list of expressions to evaluate before passing to the - * function manager. We skip the evaluation if it was already done in the - * previous call (ie, we are continuing the evaluation of a set-valued - * function). Otherwise, collect the current argument values into fcinfo. - */ - fcinfo = &fcache->fcinfo_data; + /* + * arguments is a list of expressions to evaluate before passing to the + * function manager. We skip the evaluation if it was already done in the + * previous call (ie, we are continuing the evaluation of a set-valued + * function). Otherwise, collect the current argument values into fcinfo. + */ + fcinfo = &fcache->fcinfo_data; - if (has_cursor_return) { - /* init returnCursor to store out-args cursor info on ExprContext*/ - fcinfo->refcursor_data.returnCursor = - (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number); - } else { - fcinfo->refcursor_data.returnCursor = NULL; - } + if (has_cursor_return) { + /* init returnCursor to store out-args cursor info on ExprContext*/ + fcinfo->refcursor_data.returnCursor = + (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number); + } else { + fcinfo->refcursor_data.returnCursor = NULL; + } - if (has_refcursor) { - /* init argCursor to store in-args cursor info on ExprContext*/ - fcinfo->refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->nargs); - var_dno = (int*)palloc0(sizeof(int) * fcinfo->nargs); - for (i = 0; i < fcinfo->nargs; i++) { - var_dno[i] = -1; - } - } + if (has_refcursor) { + /* init argCursor to store in-args cursor info on ExprContext*/ + fcinfo->refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->nargs); + var_dno = (int*)palloc0(sizeof(int) * fcinfo->nargs); + for (i = 0; i < fcinfo->nargs; i++) { + var_dno[i] = -1; + } + } - arguments = fcache->args; - if (!fcache->setArgsValid) { - if (has_refcursor) - argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext, var_dno); - else - argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext); - if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { + arguments = fcache->args; + if (!fcache->setArgsValid) { + if (has_refcursor) + argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext, var_dno); + else + argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext); + if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { if (argDone != ExprSingleResult) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); @@ -2160,259 +2175,259 @@ restart: } else { hasSetArg = (argDone != ExprSingleResult); } - } else { - /* Re-use callinfo from previous evaluation */ - hasSetArg = fcache->setHasSetArg; - /* Reset flag (we may set it again below) */ - fcache->setArgsValid = false; - } + } else { + /* Re-use callinfo from previous evaluation */ + hasSetArg = fcache->setHasSetArg; + /* Reset flag (we may set it again below) */ + fcache->setArgsValid = false; + } - /* - * Now call the function, passing the evaluated parameter values. - */ - if (fcache->func.fn_retset || hasSetArg) { - /* - * We need to return a set result. Complain if caller not ready to - * accept one. - */ - if (isDone == NULL) - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); + /* + * Now call the function, passing the evaluated parameter values. + */ + if (fcache->func.fn_retset || hasSetArg) { + /* + * We need to return a set result. Complain if caller not ready to + * accept one. + */ + if (isDone == NULL) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); - /* - * Prepare a resultinfo node for communication. If the function - * doesn't itself return set, we don't pass the resultinfo to the - * function, but we need to fill it in anyway for internal use. - */ - if (fcache->func.fn_retset) - fcinfo->resultinfo = (Node*)&rsinfo; - rsinfo.type = T_ReturnSetInfo; - rsinfo.econtext = econtext; - rsinfo.expectedDesc = fcache->funcResultDesc; - rsinfo.allowedModes = (int)(SFRM_ValuePerCall | SFRM_Materialize); - /* note we do not set SFRM_Materialize_Random or _Preferred */ - rsinfo.returnMode = SFRM_ValuePerCall; - /* isDone is filled below */ - rsinfo.setResult = NULL; - rsinfo.setDesc = NULL; + /* + * Prepare a resultinfo node for communication. If the function + * doesn't itself return set, we don't pass the resultinfo to the + * function, but we need to fill it in anyway for internal use. + */ + if (fcache->func.fn_retset) + fcinfo->resultinfo = (Node*)&rsinfo; + rsinfo.type = T_ReturnSetInfo; + rsinfo.econtext = econtext; + rsinfo.expectedDesc = fcache->funcResultDesc; + rsinfo.allowedModes = (int)(SFRM_ValuePerCall | SFRM_Materialize); + /* note we do not set SFRM_Materialize_Random or _Preferred */ + rsinfo.returnMode = SFRM_ValuePerCall; + /* isDone is filled below */ + rsinfo.setResult = NULL; + rsinfo.setDesc = NULL; - /* - * This loop handles the situation where we have both a set argument - * and a set-valued function. Once we have exhausted the function's - * value(s) for a particular argument value, we have to get the next - * argument value and start the function over again. We might have to - * do it more than once, if the function produces an empty result set - * for a particular input value. - */ - for (;;) { - /* - * If function is strict, and there are any NULL arguments, skip - * calling the function (at least for this set of args). - */ - bool callit = true; + /* + * This loop handles the situation where we have both a set argument + * and a set-valued function. Once we have exhausted the function's + * value(s) for a particular argument value, we have to get the next + * argument value and start the function over again. We might have to + * do it more than once, if the function produces an empty result set + * for a particular input value. + */ + for (;;) { + /* + * If function is strict, and there are any NULL arguments, skip + * calling the function (at least for this set of args). + */ + bool callit = true; - if (fcache->func.fn_strict) { - for (i = 0; i < fcinfo->nargs; i++) { - if (fcinfo->argnull[i]) { - callit = false; - break; - } - } - } + if (fcache->func.fn_strict) { + for (i = 0; i < fcinfo->nargs; i++) { + if (fcinfo->argnull[i]) { + callit = false; + break; + } + } + } - if (callit) { - pgstat_init_function_usage(fcinfo, &fcusage); + if (callit) { + pgstat_init_function_usage(fcinfo, &fcusage); - fcinfo->isnull = false; - rsinfo.isDone = ExprSingleResult; - result = FunctionCallInvoke(fcinfo); - if (AUDIT_SYSTEM_EXEC_ENABLED) { + fcinfo->isnull = false; + rsinfo.isDone = ExprSingleResult; + result = FunctionCallInvoke(fcinfo); + if (AUDIT_SYSTEM_EXEC_ENABLED) { audit_system_function(fcinfo, AUDIT_OK); } *isNull = fcinfo->isnull; *isDone = rsinfo.isDone; - pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); - } else if (isSetReturnFunc) { - /* - * For a strict SRF, result for NULL is an empty set - * If SRF is strict and has any NULL arguments, this SRF - * need return empty set, so such rows were omitted entirely - * from the result set. - */ - result = (Datum)0; - *isNull = true; - *isDone = ExprEndResult; - } else { - /* - * For a strict non-SRF, result for NULL is a NULL. - * This branch in order to deal strict nested functions - * like "select plain_function(set_returning_function(...))". - * If some of the SRF outputs are NULL, and the plain function - * is strict, we expect to get NULL results for such rows - */ - result = (Datum)0; - *isNull = true; - *isDone = ExprSingleResult; - } + pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); + } else if (isSetReturnFunc) { + /* + * For a strict SRF, result for NULL is an empty set + * If SRF is strict and has any NULL arguments, this SRF + * need return empty set, so such rows were omitted entirely + * from the result set. + */ + result = (Datum)0; + *isNull = true; + *isDone = ExprEndResult; + } else { + /* + * For a strict non-SRF, result for NULL is a NULL. + * This branch in order to deal strict nested functions + * like "select plain_function(set_returning_function(...))". + * If some of the SRF outputs are NULL, and the plain function + * is strict, we expect to get NULL results for such rows + */ + result = (Datum)0; + *isNull = true; + *isDone = ExprSingleResult; + } - if (has_refcursor && econtext->plpgsql_estate != NULL) { - PLpgSQL_execstate* estate = econtext->plpgsql_estate; - /* copy in-args cursor option info */ - for (i = 0; i < fcinfo->nargs; i++) { - if (var_dno[i] >= 0) { - int dno = var_dno[i]; - Cursor_Data* cursor_data = &fcinfo->refcursor_data.argCursor[i]; + if (has_refcursor && econtext->plpgsql_estate != NULL) { + PLpgSQL_execstate* estate = econtext->plpgsql_estate; + /* copy in-args cursor option info */ + for (i = 0; i < fcinfo->nargs; i++) { + if (var_dno[i] >= 0) { + int dno = var_dno[i]; + Cursor_Data* cursor_data = &fcinfo->refcursor_data.argCursor[i]; #ifdef USE_ASSERT_CHECKING - PLpgSQL_datum* datum = estate->datums[dno]; + PLpgSQL_datum* datum = estate->datums[dno]; #endif - Assert(datum->dtype == PLPGSQL_DTYPE_VAR); - Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID); + Assert(datum->dtype == PLPGSQL_DTYPE_VAR); + Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID); - ExecCopyDataToDatum(estate->datums, dno, cursor_data); - } - } + ExecCopyDataToDatum(estate->datums, dno, cursor_data); + } + } - if (fcinfo->refcursor_data.return_number > 0) { - /* copy function returns cursor option info. - * for simple expr in exec_eval_expr, we can not get the result type, - * so cursor_return_data mallocs here. - */ - if (estate->cursor_return_data == NULL && estate->tuple_store_cxt != NULL) { - MemoryContext oldcontext = MemoryContextSwitchTo(estate->tuple_store_cxt); - estate->cursor_return_data = - (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number); - estate->cursor_return_numbers = fcinfo->refcursor_data.return_number; - (void)MemoryContextSwitchTo(oldcontext); - } + if (fcinfo->refcursor_data.return_number > 0) { + /* copy function returns cursor option info. + * for simple expr in exec_eval_expr, we can not get the result type, + * so cursor_return_data mallocs here. + */ + if (estate->cursor_return_data == NULL && estate->tuple_store_cxt != NULL) { + MemoryContext oldcontext = MemoryContextSwitchTo(estate->tuple_store_cxt); + estate->cursor_return_data = + (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number); + estate->cursor_return_numbers = fcinfo->refcursor_data.return_number; + (void)MemoryContextSwitchTo(oldcontext); + } - if (estate->cursor_return_data != NULL) { - for (i = 0; i < fcinfo->refcursor_data.return_number; i++) { - int rc = memcpy_s(&estate->cursor_return_data[i], sizeof(Cursor_Data), - &fcinfo->refcursor_data.returnCursor[i], sizeof(Cursor_Data)); - securec_check(rc, "\0", "\0"); - } - } - } - } + if (estate->cursor_return_data != NULL) { + for (i = 0; i < fcinfo->refcursor_data.return_number; i++) { + int rc = memcpy_s(&estate->cursor_return_data[i], sizeof(Cursor_Data), + &fcinfo->refcursor_data.returnCursor[i], sizeof(Cursor_Data)); + securec_check(rc, "\0", "\0"); + } + } + } + } - /* Which protocol does function want to use? */ - if (rsinfo.returnMode == SFRM_ValuePerCall) { - if (*isDone != ExprEndResult) { - /* - * Got a result from current argument. If function itself - * returns set, save the current argument values to re-use - * on the next call. - */ - if (fcache->func.fn_retset && *isDone == ExprMultipleResult) { - fcache->setHasSetArg = hasSetArg; - fcache->setArgsValid = true; - /* Register cleanup callback if we didn't already */ - if (!fcache->shutdown_reg) { - RegisterExprContextCallback(econtext, ShutdownFuncExpr, PointerGetDatum(fcache)); - fcache->shutdown_reg = true; - } - } + /* Which protocol does function want to use? */ + if (rsinfo.returnMode == SFRM_ValuePerCall) { + if (*isDone != ExprEndResult) { + /* + * Got a result from current argument. If function itself + * returns set, save the current argument values to re-use + * on the next call. + */ + if (fcache->func.fn_retset && *isDone == ExprMultipleResult) { + fcache->setHasSetArg = hasSetArg; + fcache->setArgsValid = true; + /* Register cleanup callback if we didn't already */ + if (!fcache->shutdown_reg) { + RegisterExprContextCallback(econtext, ShutdownFuncExpr, PointerGetDatum(fcache)); + fcache->shutdown_reg = true; + } + } - /* - * Make sure we say we are returning a set, even if the - * function itself doesn't return sets. - */ - if (hasSetArg) { - *isDone = ExprMultipleResult; - } - break; - } - } else if (rsinfo.returnMode == SFRM_Materialize) { - /* check we're on the same page as the function author */ - if (rsinfo.isDone != ExprSingleResult) - ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), - errmsg("table-function protocol for materialize mode was not followed"))); - if (rsinfo.setResult != NULL) { - /* prepare to return values from the tuplestore */ - ExecPrepareTuplestoreResult(fcache, econtext, rsinfo.setResult, rsinfo.setDesc); - /* remember whether we had set arguments */ - fcache->setHasSetArg = hasSetArg; - /* loop back to top to start returning from tuplestore */ - goto restart; - } - /* if setResult was left null, treat it as empty set */ - *isDone = ExprEndResult; - *isNull = true; - result = (Datum)0; - } else { - ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), - errmsg("unrecognized table-function returnMode: %d", (int)rsinfo.returnMode))); - } + /* + * Make sure we say we are returning a set, even if the + * function itself doesn't return sets. + */ + if (hasSetArg) { + *isDone = ExprMultipleResult; + } + break; + } + } else if (rsinfo.returnMode == SFRM_Materialize) { + /* check we're on the same page as the function author */ + if (rsinfo.isDone != ExprSingleResult) + ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), + errmsg("table-function protocol for materialize mode was not followed"))); + if (rsinfo.setResult != NULL) { + /* prepare to return values from the tuplestore */ + ExecPrepareTuplestoreResult(fcache, econtext, rsinfo.setResult, rsinfo.setDesc); + /* remember whether we had set arguments */ + fcache->setHasSetArg = hasSetArg; + /* loop back to top to start returning from tuplestore */ + goto restart; + } + /* if setResult was left null, treat it as empty set */ + *isDone = ExprEndResult; + *isNull = true; + result = (Datum)0; + } else { + ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), + errmsg("unrecognized table-function returnMode: %d", (int)rsinfo.returnMode))); + } - /* Else, done with this argument */ - if (!hasSetArg) { - break; /* input not a set, so done */ - } + /* Else, done with this argument */ + if (!hasSetArg) { + break; /* input not a set, so done */ + } - /* Re-eval args to get the next element of the input set */ - if (has_refcursor) { - argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext, var_dno); - } else { - argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext); - } + /* Re-eval args to get the next element of the input set */ + if (has_refcursor) { + argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext, var_dno); + } else { + argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext); + } - if (argDone != ExprMultipleResult) { - /* End of argument set, so we're done. */ - *isNull = true; - *isDone = ExprEndResult; - result = (Datum)0; - break; - } + if (argDone != ExprMultipleResult) { + /* End of argument set, so we're done. */ + *isNull = true; + *isDone = ExprEndResult; + result = (Datum)0; + break; + } - /* - * If we reach here, loop around to run the function on the new - * argument. - */ - } - } else { - /* - * Non-set case: much easier. - * - * In common cases, this code path is unreachable because we'd have - * selected ExecMakeFunctionResultNoSets instead. However, it's - * possible to get here if an argument sometimes produces set results - * and sometimes scalar results. For example, a CASE expression might - * call a set-returning function in only some of its arms. - */ - if (isDone != NULL) - *isDone = ExprSingleResult; + /* + * If we reach here, loop around to run the function on the new + * argument. + */ + } + } else { + /* + * Non-set case: much easier. + * + * In common cases, this code path is unreachable because we'd have + * selected ExecMakeFunctionResultNoSets instead. However, it's + * possible to get here if an argument sometimes produces set results + * and sometimes scalar results. For example, a CASE expression might + * call a set-returning function in only some of its arms. + */ + if (isDone != NULL) + *isDone = ExprSingleResult; - /* - * If function is strict, and there are any NULL arguments, skip - * calling the function and return NULL. - */ - if (fcache->func.fn_strict) { - for (i = 0; i < fcinfo->nargs; i++) { - if (fcinfo->argnull[i]) { - *isNull = true; - return (Datum)0; - } - } - } + /* + * If function is strict, and there are any NULL arguments, skip + * calling the function and return NULL. + */ + if (fcache->func.fn_strict) { + for (i = 0; i < fcinfo->nargs; i++) { + if (fcinfo->argnull[i]) { + *isNull = true; + return (Datum)0; + } + } + } - pgstat_init_function_usage(fcinfo, &fcusage); + pgstat_init_function_usage(fcinfo, &fcusage); - fcinfo->isnull = false; - result = FunctionCallInvoke(fcinfo); - *isNull = fcinfo->isnull; + fcinfo->isnull = false; + result = FunctionCallInvoke(fcinfo); + *isNull = fcinfo->isnull; - pgstat_end_function_usage(&fcusage, true); - } + pgstat_end_function_usage(&fcusage, true); + } - if (has_refcursor) { - pfree_ext(fcinfo->refcursor_data.argCursor); - pfree_ext(var_dno); - } + if (has_refcursor) { + pfree_ext(fcinfo->refcursor_data.argCursor); + pfree_ext(var_dno); + } - set_result_for_plpgsql_language_function_with_outparam(fcache, &result, isNull); + set_result_for_plpgsql_language_function_with_outparam(fcache, &result, isNull); - return result; + return result; } /* @@ -2436,7 +2451,7 @@ Datum ExecMakeFunctionResultSet(FuncExprState *fcache, ExprContext *econtext, bo fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } - } + } else { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; @@ -2449,124 +2464,124 @@ Datum ExecMakeFunctionResultSet(FuncExprState *fcache, ExprContext *econtext, bo } /* - * ExecMakeFunctionResultNoSets - * - * Simplified version of ExecMakeFunctionResult that can only handle - * non-set cases. Hand-tuned for speed. - * - * Note: This function use template parameter can compile different function, - * reduce the assembly instructions so as to improve performance. - * - * Template parameter: - * @bool has_cursor_return - need store out-args cursor info. - * @bool has_refcursor - need store in-args cursor info. - */ +* ExecMakeFunctionResultNoSets +* +* Simplified version of ExecMakeFunctionResult that can only handle +* non-set cases. Hand-tuned for speed. +* +* Note: This function use template parameter can compile different function, +* reduce the assembly instructions so as to improve performance. +* +* Template parameter: +* @bool has_cursor_return - need store out-args cursor info. +* @bool has_refcursor - need store in-args cursor info. +*/ template static Datum ExecMakeFunctionResultNoSets( - FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - ListCell* arg = NULL; - Datum result; - FunctionCallInfo fcinfo; - PgStat_FunctionCallUsage fcusage; - int i; - int* var_dno = NULL; + ListCell* arg = NULL; + Datum result; + FunctionCallInfo fcinfo; + PgStat_FunctionCallUsage fcusage; + int i; + int* var_dno = NULL; - FunctionScanState *node = NULL; - FuncExpr *fexpr = NULL; + FunctionScanState *node = NULL; + FuncExpr *fexpr = NULL; - bool savedIsSTP = u_sess->SPI_cxt.is_stp; - bool savedProConfigIsSet = u_sess->SPI_cxt.is_proconfig_set; - bool proIsProcedure = false; - bool supportTranaction = false; - bool is_have_huge_clob = false; + bool savedIsSTP = u_sess->SPI_cxt.is_stp; + bool savedProConfigIsSet = u_sess->SPI_cxt.is_proconfig_set; + bool proIsProcedure = false; + bool supportTranaction = false; + bool is_have_huge_clob = false; #ifdef ENABLE_MULTIPLE_NODES - if (IS_PGXC_COORDINATOR && (t_thrd.proc->workingVersionNum >= STP_SUPPORT_COMMIT_ROLLBACK)) { - supportTranaction = true; - } + if (IS_PGXC_COORDINATOR && (t_thrd.proc->workingVersionNum >= STP_SUPPORT_COMMIT_ROLLBACK)) { + supportTranaction = true; + } #else - supportTranaction = true; + supportTranaction = true; #endif - bool needResetErrMsg = (u_sess->SPI_cxt.forbidden_commit_rollback_err_msg[0] == '\0'); + bool needResetErrMsg = (u_sess->SPI_cxt.forbidden_commit_rollback_err_msg[0] == '\0'); - /* Only allow commit at CN, therefore only need to set atomic and - * relevant check at CN level. - */ - if (supportTranaction && IsA(fcache->xprstate.expr, FuncExpr)) { - fexpr = (FuncExpr *) fcache->xprstate.expr; - node = makeNode(FunctionScanState); - if (!u_sess->SPI_cxt.is_allow_commit_rollback) { - node->atomic = true; - } - else if (IsAfterTriggerBegin()) { - node->atomic = true; - stp_set_commit_rollback_err_msg(STP_XACT_AFTER_TRIGGER_BEGIN); - } - /* - * If proconfig is set we can't allow transaction commands because of the - * way the GUC stacking works: The transaction boundary would have to pop - * the proconfig setting off the stack. That restriction could be lifted - * by redesigning the GUC nesting mechanism a bit. - */ - if (!fcache->prokind) { - bool isNullSTP = false; - HeapTuple tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid)); - if (!HeapTupleIsValid(tp)) { - ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("cache lookup failed for function %u", fexpr->funcid))); - } - if (!heap_attisnull(tp, Anum_pg_proc_proconfig, NULL) || u_sess->SPI_cxt.is_proconfig_set) { - u_sess->SPI_cxt.is_proconfig_set = true; - node->atomic = true; - stp_set_commit_rollback_err_msg(STP_XACT_GUC_IN_OPT_CLAUSE); - } - /* immutable or stable function should not support commit/rollback */ - bool isNullVolatile = false; - Datum provolatile = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_provolatile, &isNullVolatile); - if (!isNullVolatile && CharGetDatum(provolatile) != PROVOLATILE_VOLATILE) { - node->atomic = true; - stp_set_commit_rollback_err_msg(STP_XACT_IMMUTABLE); - } + /* Only allow commit at CN, therefore only need to set atomic and + * relevant check at CN level. + */ + if (supportTranaction && IsA(fcache->xprstate.expr, FuncExpr)) { + fexpr = (FuncExpr *) fcache->xprstate.expr; + node = makeNode(FunctionScanState); + if (!u_sess->SPI_cxt.is_allow_commit_rollback) { + node->atomic = true; + } + else if (IsAfterTriggerBegin()) { + node->atomic = true; + stp_set_commit_rollback_err_msg(STP_XACT_AFTER_TRIGGER_BEGIN); + } + /* + * If proconfig is set we can't allow transaction commands because of the + * way the GUC stacking works: The transaction boundary would have to pop + * the proconfig setting off the stack. That restriction could be lifted + * by redesigning the GUC nesting mechanism a bit. + */ + if (!fcache->prokind) { + bool isNullSTP = false; + HeapTuple tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid)); + if (!HeapTupleIsValid(tp)) { + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("cache lookup failed for function %u", fexpr->funcid))); + } + if (!heap_attisnull(tp, Anum_pg_proc_proconfig, NULL) || u_sess->SPI_cxt.is_proconfig_set) { + u_sess->SPI_cxt.is_proconfig_set = true; + node->atomic = true; + stp_set_commit_rollback_err_msg(STP_XACT_GUC_IN_OPT_CLAUSE); + } + /* immutable or stable function should not support commit/rollback */ + bool isNullVolatile = false; + Datum provolatile = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_provolatile, &isNullVolatile); + if (!isNullVolatile && CharGetDatum(provolatile) != PROVOLATILE_VOLATILE) { + node->atomic = true; + stp_set_commit_rollback_err_msg(STP_XACT_IMMUTABLE); + } - Datum datum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_prokind, &isNullSTP); - proIsProcedure = PROC_IS_PRO(CharGetDatum(datum)); - if (proIsProcedure) { - fcache->prokind = 'p'; - } else { - fcache->prokind = 'f'; - } + Datum datum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_prokind, &isNullSTP); + proIsProcedure = PROC_IS_PRO(CharGetDatum(datum)); + if (proIsProcedure) { + fcache->prokind = 'p'; + } else { + fcache->prokind = 'f'; + } - /* if proIsProcedure is ture means it was a stored procedure */ - u_sess->SPI_cxt.is_stp = savedIsSTP; - ReleaseSysCache(tp); - } else { - proIsProcedure = PROC_IS_PRO(fcache->prokind); - u_sess->SPI_cxt.is_stp = savedIsSTP; - } - } + /* if proIsProcedure is ture means it was a stored procedure */ + u_sess->SPI_cxt.is_stp = savedIsSTP; + ReleaseSysCache(tp); + } else { + proIsProcedure = PROC_IS_PRO(fcache->prokind); + u_sess->SPI_cxt.is_stp = savedIsSTP; + } + } - /* Guard against stack overflow due to overly complex expressions */ - check_stack_depth(); + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - econtext->plpgsql_estate = plpgsql_estate; - plpgsql_estate = NULL; + econtext->plpgsql_estate = plpgsql_estate; + plpgsql_estate = NULL; - /* inlined, simplified version of ExecEvalFuncArgs */ - fcinfo = &fcache->fcinfo_data; + /* inlined, simplified version of ExecEvalFuncArgs */ + fcinfo = &fcache->fcinfo_data; - /* init the number of arguments to a function*/ - InitFunctionCallInfoArgs(*fcinfo, list_length(fcache->args), 1); + /* init the number of arguments to a function*/ + InitFunctionCallInfoArgs(*fcinfo, list_length(fcache->args), 1); - /* Only allow commit at CN, therefore need to set callcontext in CN only */ - if (supportTranaction) { - fcinfo->context = (Node *)node; - } + /* Only allow commit at CN, therefore need to set callcontext in CN only */ + if (supportTranaction) { + fcinfo->context = (Node *)node; + } - if (econtext) { + if (econtext) { fcinfo->can_ignore = econtext->can_ignore; } @@ -2581,713 +2596,714 @@ static Datum ExecMakeFunctionResultNoSets( fcinfo->flinfo->fn_oid == SYS_CONNECT_BY_PATH_FUNCOID) { fcinfo->swinfo.sw_econtext = (Node *)econtext; fcinfo->swinfo.sw_exprstate = (Node *)linitial(fcache->args); + fcinfo->swinfo.sw_is_flt_frame = false; } - if (has_cursor_return) { - /* init returnCursor to store out-args cursor info on ExprContext*/ - fcinfo->refcursor_data.returnCursor = - (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number); - } else { - fcinfo->refcursor_data.returnCursor = NULL; - } + if (has_cursor_return) { + /* init returnCursor to store out-args cursor info on ExprContext*/ + fcinfo->refcursor_data.returnCursor = + (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number); + } else { + fcinfo->refcursor_data.returnCursor = NULL; + } - if (has_refcursor) { - /* init argCursor to store in-args cursor info on ExprContext */ - fcinfo->refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->nargs); - var_dno = (int*)palloc0(sizeof(int) * fcinfo->nargs); - for (i = 0; i < fcinfo->nargs; i++) { - var_dno[i] = -1; - } - } + if (has_refcursor) { + /* init argCursor to store in-args cursor info on ExprContext */ + fcinfo->refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->nargs); + var_dno = (int*)palloc0(sizeof(int) * fcinfo->nargs); + for (i = 0; i < fcinfo->nargs; i++) { + var_dno[i] = -1; + } + } - i = 0; - econtext->is_cursor = false; - u_sess->plsql_cxt.func_tableof_index = NIL; - foreach (arg, fcache->args) { - ExprState* argstate = (ExprState*)lfirst(arg); + i = 0; + econtext->is_cursor = false; + u_sess->plsql_cxt.func_tableof_index = NIL; + foreach (arg, fcache->args) { + ExprState* argstate = (ExprState*)lfirst(arg); - fcinfo->argTypes[i] = argstate->resultType; - if (has_refcursor && fcinfo->argTypes[i] == REFCURSOROID) - econtext->is_cursor = true; - fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo->argnull[i], NULL); - if (is_huge_clob(fcinfo->argTypes[i], fcinfo->argnull[i], fcinfo->arg[i])) { - is_have_huge_clob = true; - } - ExecTableOfIndexInfo execTableOfIndexInfo; - initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext); - ExecEvalParamExternTableOfIndex((Node*)argstate->expr, &execTableOfIndexInfo); - if (execTableOfIndexInfo.tableOfIndex != NULL) { - if (!IsTableOfFunc(fcache->func.fn_oid)) { - MemoryContext oldCxt = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); - PLpgSQL_func_tableof_index* func_tableof = - (PLpgSQL_func_tableof_index*)palloc0(sizeof(PLpgSQL_func_tableof_index)); - func_tableof->varno = i; - func_tableof->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; - func_tableof->tableOfIndex = copyTableOfIndex(execTableOfIndexInfo.tableOfIndex); - u_sess->plsql_cxt.func_tableof_index = lappend(u_sess->plsql_cxt.func_tableof_index, func_tableof); - MemoryContextSwitchTo(oldCxt); - } + fcinfo->argTypes[i] = argstate->resultType; + if (has_refcursor && fcinfo->argTypes[i] == REFCURSOROID) + econtext->is_cursor = true; + fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo->argnull[i], NULL); + if (is_huge_clob(fcinfo->argTypes[i], fcinfo->argnull[i], fcinfo->arg[i])) { + is_have_huge_clob = true; + } + ExecTableOfIndexInfo execTableOfIndexInfo; + initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext); + ExecEvalParamExternTableOfIndex((Node*)argstate->expr, &execTableOfIndexInfo); + if (execTableOfIndexInfo.tableOfIndex != NULL) { + if (!IsTableOfFunc(fcache->func.fn_oid)) { + MemoryContext oldCxt = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); + PLpgSQL_func_tableof_index* func_tableof = + (PLpgSQL_func_tableof_index*)palloc0(sizeof(PLpgSQL_func_tableof_index)); + func_tableof->varno = i; + func_tableof->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; + func_tableof->tableOfIndex = copyTableOfIndex(execTableOfIndexInfo.tableOfIndex); + u_sess->plsql_cxt.func_tableof_index = lappend(u_sess->plsql_cxt.func_tableof_index, func_tableof); + MemoryContextSwitchTo(oldCxt); + } - u_sess->SPI_cxt.cur_tableof_index->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; - u_sess->SPI_cxt.cur_tableof_index->tableOfIndex = execTableOfIndexInfo.tableOfIndex; - u_sess->SPI_cxt.cur_tableof_index->tableOfNestLayer = execTableOfIndexInfo.tableOfLayers; - /* for nest table of output, save layer of this var tableOfGetNestLayer in ExecEvalArrayRef, - or set to zero for get whole nest table. */ - u_sess->SPI_cxt.cur_tableof_index->tableOfGetNestLayer = -1; - } + u_sess->SPI_cxt.cur_tableof_index->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; + u_sess->SPI_cxt.cur_tableof_index->tableOfIndex = execTableOfIndexInfo.tableOfIndex; + u_sess->SPI_cxt.cur_tableof_index->tableOfNestLayer = execTableOfIndexInfo.tableOfLayers; + /* for nest table of output, save layer of this var tableOfGetNestLayer in ExecEvalArrayRef, + or set to zero for get whole nest table. */ + u_sess->SPI_cxt.cur_tableof_index->tableOfGetNestLayer = -1; + } - if (has_refcursor && econtext->is_cursor) { - var_dno[i] = econtext->dno; - CopyCursorInfoData(&fcinfo->refcursor_data.argCursor[i], &econtext->cursor_data); - } - econtext->is_cursor = false; - i++; - } + if (has_refcursor && econtext->is_cursor) { + var_dno[i] = econtext->dno; + CopyCursorInfoData(&fcinfo->refcursor_data.argCursor[i], &econtext->cursor_data); + } + econtext->is_cursor = false; + i++; + } - /* - * If function is strict, and there are any NULL arguments, skip calling - * the function and return NULL. - */ - if (fcache->func.fn_strict) { - while (--i >= 0) { - if (fcinfo->argnull[i]) { - *isNull = true; - u_sess->SPI_cxt.is_stp = savedIsSTP; - u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet; - if (needResetErrMsg) { - stp_reset_commit_rolback_err_msg(); - } - return (Datum)0; - } - } - } + /* + * If function is strict, and there are any NULL arguments, skip calling + * the function and return NULL. + */ + if (fcache->func.fn_strict) { + while (--i >= 0) { + if (fcinfo->argnull[i]) { + *isNull = true; + u_sess->SPI_cxt.is_stp = savedIsSTP; + u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet; + if (needResetErrMsg) { + stp_reset_commit_rolback_err_msg(); + } + return (Datum)0; + } + } + } - pgstat_init_function_usage(fcinfo, &fcusage); + pgstat_init_function_usage(fcinfo, &fcusage); - fcinfo->isnull = false; - check_huge_clob_paramter(fcinfo, is_have_huge_clob); - if (u_sess->instr_cxt.global_instr != NULL && fcinfo->flinfo->fn_addr == plpgsql_call_handler) { - StreamInstrumentation* save_global_instr = u_sess->instr_cxt.global_instr; - u_sess->instr_cxt.global_instr = NULL; - result = FunctionCallInvoke(fcinfo); // node will be free at here or else; - u_sess->instr_cxt.global_instr = save_global_instr; - } else { - if (fcinfo->argTypes[0] == CLOBOID && fcinfo->argTypes[1] == CLOBOID && fcinfo->flinfo->fn_addr == textcat) { - bool is_null = false; - if (fcinfo->arg[0] != 0 && VARATT_IS_EXTERNAL_LOB(fcinfo->arg[0])) { - struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(fcinfo->arg[0])); - fcinfo->arg[0] = fetch_lob_value_from_tuple(lob_pointer, InvalidOid, &is_null); - } - if (fcinfo->arg[1] != 0 && VARATT_IS_EXTERNAL_LOB(fcinfo->arg[1])) { - struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(fcinfo->arg[1])); - fcinfo->arg[1] = fetch_lob_value_from_tuple(lob_pointer, InvalidOid, &is_null); - } - } - result = FunctionCallInvoke(fcinfo); - } - *isNull = fcinfo->isnull; + fcinfo->isnull = false; + check_huge_clob_paramter(fcinfo, is_have_huge_clob); + if (u_sess->instr_cxt.global_instr != NULL && fcinfo->flinfo->fn_addr == plpgsql_call_handler) { + StreamInstrumentation* save_global_instr = u_sess->instr_cxt.global_instr; + u_sess->instr_cxt.global_instr = NULL; + result = FunctionCallInvoke(fcinfo); // node will be free at here or else; + u_sess->instr_cxt.global_instr = save_global_instr; + } else { + if (fcinfo->argTypes[0] == CLOBOID && fcinfo->argTypes[1] == CLOBOID && fcinfo->flinfo->fn_addr == textcat) { + bool is_null = false; + if (fcinfo->arg[0] != 0 && VARATT_IS_EXTERNAL_LOB(fcinfo->arg[0])) { + struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(fcinfo->arg[0])); + fcinfo->arg[0] = fetch_lob_value_from_tuple(lob_pointer, InvalidOid, &is_null); + } + if (fcinfo->arg[1] != 0 && VARATT_IS_EXTERNAL_LOB(fcinfo->arg[1])) { + struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(fcinfo->arg[1])); + fcinfo->arg[1] = fetch_lob_value_from_tuple(lob_pointer, InvalidOid, &is_null); + } + } + result = FunctionCallInvoke(fcinfo); + } + *isNull = fcinfo->isnull; if (AUDIT_SYSTEM_EXEC_ENABLED) { audit_system_function(fcinfo, AUDIT_OK); } - if (has_refcursor && econtext->plpgsql_estate != NULL) { - PLpgSQL_execstate* estate = econtext->plpgsql_estate; - for (i = 0; i < fcinfo->nargs; i++) { - /* copy in-args cursor option info */ - if (var_dno[i] >= 0) { - int dno = var_dno[i]; - Cursor_Data* cursor_data = &fcinfo->refcursor_data.argCursor[i]; + if (has_refcursor && econtext->plpgsql_estate != NULL) { + PLpgSQL_execstate* estate = econtext->plpgsql_estate; + for (i = 0; i < fcinfo->nargs; i++) { + /* copy in-args cursor option info */ + if (var_dno[i] >= 0) { + int dno = var_dno[i]; + Cursor_Data* cursor_data = &fcinfo->refcursor_data.argCursor[i]; #ifdef USE_ASSERT_CHECKING - PLpgSQL_datum* datum = estate->datums[dno]; + PLpgSQL_datum* datum = estate->datums[dno]; #endif - Assert(datum->dtype == PLPGSQL_DTYPE_VAR); - Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID); + Assert(datum->dtype == PLPGSQL_DTYPE_VAR); + Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID); - ExecCopyDataToDatum(estate->datums, dno, cursor_data); - } - } + ExecCopyDataToDatum(estate->datums, dno, cursor_data); + } + } - if (fcinfo->flinfo->fn_rettype == REFCURSOROID) { - /* copy function returns cursor option info. - * for simple expr in exec_eval_expr, we can not get the result type, - * so cursor_return_data mallocs here. - */ - if (estate->cursor_return_data == NULL) { - estate->cursor_return_data = (Cursor_Data*)palloc0(sizeof(Cursor_Data)); - estate->cursor_return_numbers = 1; - } - int rc = memcpy_s(estate->cursor_return_data, - sizeof(Cursor_Data), - fcinfo->refcursor_data.returnCursor, - sizeof(Cursor_Data)); - securec_check(rc, "\0", "\0"); - } - } + if (fcinfo->flinfo->fn_rettype == REFCURSOROID) { + /* copy function returns cursor option info. + * for simple expr in exec_eval_expr, we can not get the result type, + * so cursor_return_data mallocs here. + */ + if (estate->cursor_return_data == NULL) { + estate->cursor_return_data = (Cursor_Data*)palloc0(sizeof(Cursor_Data)); + estate->cursor_return_numbers = 1; + } + int rc = memcpy_s(estate->cursor_return_data, + sizeof(Cursor_Data), + fcinfo->refcursor_data.returnCursor, + sizeof(Cursor_Data)); + securec_check(rc, "\0", "\0"); + } + } - pgstat_end_function_usage(&fcusage, true); + pgstat_end_function_usage(&fcusage, true); - if (has_refcursor) { - if (fcinfo->refcursor_data.argCursor != NULL) - pfree_ext(fcinfo->refcursor_data.argCursor); - if (var_dno != NULL) - pfree_ext(var_dno); - } + if (has_refcursor) { + if (fcinfo->refcursor_data.argCursor != NULL) + pfree_ext(fcinfo->refcursor_data.argCursor); + if (var_dno != NULL) + pfree_ext(var_dno); + } - u_sess->SPI_cxt.is_stp = savedIsSTP; - u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet; - if (needResetErrMsg) { - stp_reset_commit_rolback_err_msg(); - } + u_sess->SPI_cxt.is_stp = savedIsSTP; + u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet; + if (needResetErrMsg) { + stp_reset_commit_rolback_err_msg(); + } - set_result_for_plpgsql_language_function_with_outparam(fcache, &result, isNull); + set_result_for_plpgsql_language_function_with_outparam(fcache, &result, isNull); - return result; + return result; } /* - * @Description: jugde function has parameter that is refcursor or return type is refcursor - * @in Funcid - function oid - * @in fcinfo - function call info - * @return - has refcursor - */ -static bool func_has_refcursor_args(Oid Funcid, FunctionCallInfoData* fcinfo) +* @Description: jugde function has parameter that is refcursor or return type is refcursor +* @in Funcid - function oid +* @in fcinfo - function call info +* @return - has refcursor +*/ +extern bool func_has_refcursor_args(Oid Funcid, FunctionCallInfoData* fcinfo) { - HeapTuple proctup = NULL; - Form_pg_proc procStruct; - int allarg; - Oid* p_argtypes = NULL; - char** p_argnames = NULL; - char* p_argmodes = NULL; - bool use_cursor = false; - bool return_refcursor = false; - int out_count = 0; /* out arg count */ + HeapTuple proctup = NULL; + Form_pg_proc procStruct; + int allarg; + Oid* p_argtypes = NULL; + char** p_argnames = NULL; + char* p_argmodes = NULL; + bool use_cursor = false; + bool return_refcursor = false; + int out_count = 0; /* out arg count */ - proctup = SearchSysCache(PROCOID, ObjectIdGetDatum(Funcid), 0, 0, 0); + proctup = SearchSysCache(PROCOID, ObjectIdGetDatum(Funcid), 0, 0, 0); - /* - * function may be deleted after clist be searched. - */ - if (!HeapTupleIsValid(proctup)) { - ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function doesn't exist "))); - } + /* + * function may be deleted after clist be searched. + */ + if (!HeapTupleIsValid(proctup)) { + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function doesn't exist "))); + } - /* get the all args informations, only "in" parameters if p_argmodes is null */ - allarg = get_func_arg_info(proctup, &p_argtypes, &p_argnames, &p_argmodes); - procStruct = (Form_pg_proc)GETSTRUCT(proctup); + /* get the all args informations, only "in" parameters if p_argmodes is null */ + allarg = get_func_arg_info(proctup, &p_argtypes, &p_argnames, &p_argmodes); + procStruct = (Form_pg_proc)GETSTRUCT(proctup); - fcinfo->refcursor_data.return_number = 0; - fcinfo->refcursor_data.returnCursor = NULL; - for (int i = 0; i < allarg; i++) { - if (p_argmodes != NULL && (p_argmodes[i] == 'o' || p_argmodes[i] == 'b')) { - out_count++; - if (p_argtypes[i] == REFCURSOROID) - return_refcursor = true; - } else { - if (p_argtypes[i] == REFCURSOROID) - use_cursor = true; - } - } + fcinfo->refcursor_data.return_number = 0; + fcinfo->refcursor_data.returnCursor = NULL; + for (int i = 0; i < allarg; i++) { + if (p_argmodes != NULL && (p_argmodes[i] == 'o' || p_argmodes[i] == 'b')) { + out_count++; + if (p_argtypes[i] == REFCURSOROID) + return_refcursor = true; + } else { + if (p_argtypes[i] == REFCURSOROID) + use_cursor = true; + } + } - if (procStruct->prorettype == REFCURSOROID) { - use_cursor = true; - fcinfo->refcursor_data.return_number = 1; - } else if (return_refcursor) { - fcinfo->refcursor_data.return_number = out_count; - } + if (procStruct->prorettype == REFCURSOROID) { + use_cursor = true; + fcinfo->refcursor_data.return_number = 1; + } else if (return_refcursor) { + fcinfo->refcursor_data.return_number = out_count; + } - ReleaseSysCache(proctup); - return use_cursor; + ReleaseSysCache(proctup); + return use_cursor; } /* - * ExecMakeTableFunctionResult - * - * Evaluate a table function, producing a materialized result in a Tuplestore - * object. - */ +* ExecMakeTableFunctionResult +* +* Evaluate a table function, producing a materialized result in a Tuplestore +* object. +*/ Tuplestorestate* ExecMakeTableFunctionResult( - ExprState* funcexpr, ExprContext* econtext, TupleDesc expectedDesc, bool randomAccess, FunctionScanState* node) + ExprState* funcexpr, ExprContext* econtext, TupleDesc expectedDesc, bool randomAccess, FunctionScanState* node) { - Tuplestorestate* tupstore = NULL; - TupleDesc tupdesc = NULL; - Oid funcrettype; - bool returnsTuple = false; - bool returnsSet = false; - FunctionCallInfoData fcinfo; - PgStat_FunctionCallUsage fcusage; - ReturnSetInfo rsinfo; - HeapTupleData tmptup; - MemoryContext callerContext; - MemoryContext oldcontext; - bool direct_function_call = false; - bool first_time = true; - int* var_dno = NULL; - bool has_refcursor = false; - bool has_out_param = false; + Tuplestorestate* tupstore = NULL; + TupleDesc tupdesc = NULL; + Oid funcrettype; + bool returnsTuple = false; + bool returnsSet = false; + FunctionCallInfoData fcinfo; + PgStat_FunctionCallUsage fcusage; + ReturnSetInfo rsinfo; + HeapTupleData tmptup; + MemoryContext callerContext; + MemoryContext oldcontext; + bool direct_function_call = false; + bool first_time = true; + int* var_dno = NULL; + bool has_refcursor = false; + bool has_out_param = false; - FuncExpr *fexpr = NULL; - bool savedIsSTP = u_sess->SPI_cxt.is_stp; - bool savedProConfigIsSet = u_sess->SPI_cxt.is_proconfig_set; - bool proIsProcedure = false; - bool supportTranaction = false; + FuncExpr *fexpr = NULL; + bool savedIsSTP = u_sess->SPI_cxt.is_stp; + bool savedProConfigIsSet = u_sess->SPI_cxt.is_proconfig_set; + bool proIsProcedure = false; + bool supportTranaction = false; #ifdef ENABLE_MULTIPLE_NODES - if (IS_PGXC_COORDINATOR && (t_thrd.proc->workingVersionNum >= STP_SUPPORT_COMMIT_ROLLBACK)) { - supportTranaction = true; - } + if (IS_PGXC_COORDINATOR && (t_thrd.proc->workingVersionNum >= STP_SUPPORT_COMMIT_ROLLBACK)) { + supportTranaction = true; + } #else - supportTranaction = true; + supportTranaction = true; #endif - bool needResetErrMsg = (u_sess->SPI_cxt.forbidden_commit_rollback_err_msg[0] == '\0'); + bool needResetErrMsg = (u_sess->SPI_cxt.forbidden_commit_rollback_err_msg[0] == '\0'); - /* Only allow commit at CN, therefore only need to set atomic and relevant check at CN level. */ - if (supportTranaction && IsA(funcexpr->expr, FuncExpr)) { - fexpr = (FuncExpr*)funcexpr->expr; - char prokind = (reinterpret_cast(funcexpr))->prokind; - if (!u_sess->SPI_cxt.is_allow_commit_rollback) { - node->atomic = true; - } - else if (IsAfterTriggerBegin()) { - node->atomic = true; - stp_set_commit_rollback_err_msg(STP_XACT_AFTER_TRIGGER_BEGIN); - } - /* - * If proconfig is set we can't allow transaction commands because of the - * way the GUC stacking works: The transaction boundary would have to pop - * the proconfig setting off the stack. That restriction could be lifted - * by redesigning the GUC nesting mechanism a bit. - */ - if (!prokind) { - HeapTuple tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid)); - bool isNull = false; - if (!HeapTupleIsValid(tp)) { - elog(ERROR, "cache lookup failed for function %u", fexpr->funcid); - } + /* Only allow commit at CN, therefore only need to set atomic and relevant check at CN level. */ + if (supportTranaction && IsA(funcexpr->expr, FuncExpr)) { + fexpr = (FuncExpr*)funcexpr->expr; + char prokind = (reinterpret_cast(funcexpr))->prokind; + if (!u_sess->SPI_cxt.is_allow_commit_rollback) { + node->atomic = true; + } + else if (IsAfterTriggerBegin()) { + node->atomic = true; + stp_set_commit_rollback_err_msg(STP_XACT_AFTER_TRIGGER_BEGIN); + } + /* + * If proconfig is set we can't allow transaction commands because of the + * way the GUC stacking works: The transaction boundary would have to pop + * the proconfig setting off the stack. That restriction could be lifted + * by redesigning the GUC nesting mechanism a bit. + */ + if (!prokind) { + HeapTuple tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid)); + bool isNull = false; + if (!HeapTupleIsValid(tp)) { + elog(ERROR, "cache lookup failed for function %u", fexpr->funcid); + } - /* immutable or stable function do not support commit/rollback */ - bool isNullVolatile = false; - Datum provolatile = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_provolatile, &isNullVolatile); - if (!isNullVolatile && CharGetDatum(provolatile) != PROVOLATILE_VOLATILE) { - node->atomic = true; - stp_set_commit_rollback_err_msg(STP_XACT_IMMUTABLE); - } + /* immutable or stable function do not support commit/rollback */ + bool isNullVolatile = false; + Datum provolatile = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_provolatile, &isNullVolatile); + if (!isNullVolatile && CharGetDatum(provolatile) != PROVOLATILE_VOLATILE) { + node->atomic = true; + stp_set_commit_rollback_err_msg(STP_XACT_IMMUTABLE); + } - Datum datum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_prokind, &isNull); - proIsProcedure = PROC_IS_PRO(CharGetDatum(datum)); - if (proIsProcedure) { - (reinterpret_cast(funcexpr))->prokind = 'p'; - } else { - (reinterpret_cast(funcexpr))->prokind = 'f'; - } - /* if proIsProcedure means it was a stored procedure */ - u_sess->SPI_cxt.is_stp = savedIsSTP; - if (!heap_attisnull(tp, Anum_pg_proc_proconfig, NULL) || u_sess->SPI_cxt.is_proconfig_set) { - u_sess->SPI_cxt.is_proconfig_set = true; - node->atomic = true; - stp_set_commit_rollback_err_msg(STP_XACT_GUC_IN_OPT_CLAUSE); - } - ReleaseSysCache(tp); - } else { - proIsProcedure = PROC_IS_PRO(prokind); - u_sess->SPI_cxt.is_stp = savedIsSTP; - } - } + Datum datum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_prokind, &isNull); + proIsProcedure = PROC_IS_PRO(CharGetDatum(datum)); + if (proIsProcedure) { + (reinterpret_cast(funcexpr))->prokind = 'p'; + } else { + (reinterpret_cast(funcexpr))->prokind = 'f'; + } + /* if proIsProcedure means it was a stored procedure */ + u_sess->SPI_cxt.is_stp = savedIsSTP; + if (!heap_attisnull(tp, Anum_pg_proc_proconfig, NULL) || u_sess->SPI_cxt.is_proconfig_set) { + u_sess->SPI_cxt.is_proconfig_set = true; + node->atomic = true; + stp_set_commit_rollback_err_msg(STP_XACT_GUC_IN_OPT_CLAUSE); + } + ReleaseSysCache(tp); + } else { + proIsProcedure = PROC_IS_PRO(prokind); + u_sess->SPI_cxt.is_stp = savedIsSTP; + } + } - callerContext = CurrentMemoryContext; + callerContext = CurrentMemoryContext; - if (unlikely(funcexpr == NULL)) { - ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("The input function expression is NULL."))); - } - funcrettype = exprType((Node*)funcexpr->expr); + if (unlikely(funcexpr == NULL)) { + ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("The input function expression is NULL."))); + } + funcrettype = exprType((Node*)funcexpr->expr); - returnsTuple = type_is_rowtype(funcrettype); - econtext->plpgsql_estate = plpgsql_estate; - plpgsql_estate = NULL; + returnsTuple = type_is_rowtype(funcrettype); + econtext->plpgsql_estate = plpgsql_estate; + plpgsql_estate = NULL; - /* - * Prepare a resultinfo node for communication. We always do this even if - * not expecting a set result, so that we can pass expectedDesc. In the - * generic-expression case, the expression doesn't actually get to see the - * resultinfo, but set it up anyway because we use some of the fields as - * our own state variables. - */ - rsinfo.type = T_ReturnSetInfo; - rsinfo.econtext = econtext; - rsinfo.expectedDesc = expectedDesc; - rsinfo.allowedModes = (int)(SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred); - if (randomAccess) - rsinfo.allowedModes |= (int)SFRM_Materialize_Random; - rsinfo.returnMode = SFRM_ValuePerCall; - /* isDone is filled below */ - rsinfo.setResult = NULL; - rsinfo.setDesc = NULL; + /* + * Prepare a resultinfo node for communication. We always do this even if + * not expecting a set result, so that we can pass expectedDesc. In the + * generic-expression case, the expression doesn't actually get to see the + * resultinfo, but set it up anyway because we use some of the fields as + * our own state variables. + */ + rsinfo.type = T_ReturnSetInfo; + rsinfo.econtext = econtext; + rsinfo.expectedDesc = expectedDesc; + rsinfo.allowedModes = (int)(SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred); + if (randomAccess) + rsinfo.allowedModes |= (int)SFRM_Materialize_Random; + rsinfo.returnMode = SFRM_ValuePerCall; + /* isDone is filled below */ + rsinfo.setResult = NULL; + rsinfo.setDesc = NULL; - /* - * Normally the passed expression tree will be a FuncExprState, since the - * grammar only allows a function call at the top level of a table - * function reference. However, if the function doesn't return set then - * the planner might have replaced the function call via constant-folding - * or inlining. So if we see any other kind of expression node, execute - * it via the general ExecEvalExpr() code; the only difference is that we - * don't get a chance to pass a special ReturnSetInfo to any functions - * buried in the expression. - */ - if (funcexpr && IsA(funcexpr, FuncExprState) && IsA(funcexpr->expr, FuncExpr)) { - FuncExprState* fcache = (FuncExprState*)funcexpr; - ExprDoneCond argDone; + /* + * Normally the passed expression tree will be a FuncExprState, since the + * grammar only allows a function call at the top level of a table + * function reference. However, if the function doesn't return set then + * the planner might have replaced the function call via constant-folding + * or inlining. So if we see any other kind of expression node, execute + * it via the general ExecEvalExpr() code; the only difference is that we + * don't get a chance to pass a special ReturnSetInfo to any functions + * buried in the expression. + */ + if (funcexpr && IsA(funcexpr, FuncExprState) && IsA(funcexpr->expr, FuncExpr)) { + FuncExprState* fcache = (FuncExprState*)funcexpr; + ExprDoneCond argDone; - /* - * This path is similar to ExecMakeFunctionResult. - */ - direct_function_call = true; + /* + * This path is similar to ExecMakeFunctionResult. + */ + direct_function_call = true; - /* - * Initialize function cache if first time through - */ - if (fcache->func.fn_oid == InvalidOid) { - FuncExpr* func = (FuncExpr*)fcache->xprstate.expr; + /* + * Initialize function cache if first time through + */ + if (!funcexpr->is_flt_frame && (fcache->func.fn_oid == InvalidOid)) { + FuncExpr* func = (FuncExpr*)fcache->xprstate.expr; - init_fcache(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, true, false); - } - returnsSet = fcache->func.fn_retset; - InitFunctionCallInfoData(fcinfo, - &(fcache->func), - list_length(fcache->args), - fcache->fcinfo_data.fncollation, - (Node*)node, - (Node*)&rsinfo); + init_fcache(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, true, false); + } + returnsSet = fcache->func.fn_retset; + InitFunctionCallInfoData(fcinfo, + &(fcache->func), + list_length(fcache->args), + fcache->fcinfo_data.fncollation, + (Node*)node, + (Node*)&rsinfo); - has_refcursor = func_has_refcursor_args(fcinfo.flinfo->fn_oid, &fcinfo); + has_refcursor = func_has_refcursor_args(fcinfo.flinfo->fn_oid, &fcinfo); - has_out_param = (is_function_with_plpgsql_language_and_outparam(fcinfo.flinfo->fn_oid) != InvalidOid); - if (u_sess->attr.attr_sql.sql_compatibility == A_FORMAT && has_out_param) { - returnsTuple = type_is_rowtype(RECORDOID); - } + has_out_param = (is_function_with_plpgsql_language_and_outparam(fcinfo.flinfo->fn_oid) != InvalidOid); + if (u_sess->attr.attr_sql.sql_compatibility == A_FORMAT && has_out_param) { + returnsTuple = type_is_rowtype(RECORDOID); + } - int cursor_return_number = fcinfo.refcursor_data.return_number; - if (cursor_return_number > 0) { - /* init returnCursor to store out-args cursor info on FunctionScan context*/ - fcinfo.refcursor_data.returnCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * cursor_return_number); - } else { - fcinfo.refcursor_data.returnCursor = NULL; - } + int cursor_return_number = fcinfo.refcursor_data.return_number; + if (cursor_return_number > 0) { + /* init returnCursor to store out-args cursor info on FunctionScan context*/ + fcinfo.refcursor_data.returnCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * cursor_return_number); + } else { + fcinfo.refcursor_data.returnCursor = NULL; + } - if (has_refcursor) { - /* init argCursor to store in-args cursor info on FunctionScan context*/ - fcinfo.refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo.nargs); - var_dno = (int*)palloc0(sizeof(int) * fcinfo.nargs); - int rc = memset_s(var_dno, sizeof(int) * fcinfo.nargs, -1, sizeof(int) * fcinfo.nargs); - securec_check(rc, "\0", "\0"); - } + if (has_refcursor) { + /* init argCursor to store in-args cursor info on FunctionScan context*/ + fcinfo.refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo.nargs); + var_dno = (int*)palloc0(sizeof(int) * fcinfo.nargs); + int rc = memset_s(var_dno, sizeof(int) * fcinfo.nargs, -1, sizeof(int) * fcinfo.nargs); + securec_check(rc, "\0", "\0"); + } - /* - * Evaluate the function's argument list. - * - * Note: ideally, we'd do this in the per-tuple context, but then the - * argument values would disappear when we reset the context in the - * inner loop. So do it in caller context. Perhaps we should make a - * separate context just to hold the evaluated arguments? - */ - if (has_refcursor) - argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext, var_dno); - else - argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext); - /* We don't allow sets in the arguments of the table function */ - if (argDone != ExprSingleResult) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /* + * Evaluate the function's argument list. + * + * Note: ideally, we'd do this in the per-tuple context, but then the + * argument values would disappear when we reset the context in the + * inner loop. So do it in caller context. Perhaps we should make a + * separate context just to hold the evaluated arguments? + */ + if (has_refcursor) + argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext, var_dno); + else + argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext); + /* We don't allow sets in the arguments of the table function */ + if (!funcexpr->is_flt_frame && (argDone != ExprSingleResult)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); - /* - * If function is strict, and there are any NULL arguments, skip - * calling the function and act like it returned NULL (or an empty - * set, in the returns-set case). - */ - if (fcache->func.fn_strict) { - int i; + /* + * If function is strict, and there are any NULL arguments, skip + * calling the function and act like it returned NULL (or an empty + * set, in the returns-set case). + */ + if (fcache->func.fn_strict) { + int i; - for (i = 0; i < fcinfo.nargs; i++) { - if (fcinfo.argnull[i]) - goto no_function_result; - } - } - } else { - /* Treat funcexpr as a generic expression */ - direct_function_call = false; - InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, (Node*)node, NULL); - } + for (i = 0; i < fcinfo.nargs; i++) { + if (fcinfo.argnull[i]) + goto no_function_result; + } + } + } else { + /* Treat funcexpr as a generic expression */ + direct_function_call = false; + InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, (Node*)node, NULL); + } - /* - * Switch to short-lived context for calling the function or expression. - */ - MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + /* + * Switch to short-lived context for calling the function or expression. + */ + MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - /* - * Loop to handle the ValuePerCall protocol (which is also the same - * behavior needed in the generic ExecEvalExpr path). - */ - for (;;) { - Datum result; + /* + * Loop to handle the ValuePerCall protocol (which is also the same + * behavior needed in the generic ExecEvalExpr path). + */ + for (;;) { + Datum result; - CHECK_FOR_INTERRUPTS(); + CHECK_FOR_INTERRUPTS(); - /* - * reset per-tuple memory context before each call of the function or - * expression. This cleans up any local memory the function may leak - * when called. - */ - ResetExprContext(econtext); + /* + * reset per-tuple memory context before each call of the function or + * expression. This cleans up any local memory the function may leak + * when called. + */ + ResetExprContext(econtext); - /* Call the function or expression one time */ - if (direct_function_call) { - pgstat_init_function_usage(&fcinfo, &fcusage); + /* Call the function or expression one time */ + if (direct_function_call) { + pgstat_init_function_usage(&fcinfo, &fcusage); - fcinfo.isnull = false; - rsinfo.isDone = ExprSingleResult; - result = FunctionCallInvoke(&fcinfo); + fcinfo.isnull = false; + rsinfo.isDone = ExprSingleResult; + result = FunctionCallInvoke(&fcinfo); if (AUDIT_SYSTEM_EXEC_ENABLED) { audit_system_function(&fcinfo, AUDIT_OK); } - if (econtext->plpgsql_estate != NULL) { - PLpgSQL_execstate* estate = econtext->plpgsql_estate; - bool isVaildReturn = (fcinfo.refcursor_data.return_number > 0 && - estate->cursor_return_data != NULL && fcinfo.refcursor_data.returnCursor != NULL); - if (isVaildReturn) { - bool isVaildReturnNum = (fcinfo.refcursor_data.return_number > estate->cursor_return_numbers); - if (isVaildReturnNum) { - pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); - ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmodule(MOD_PLSQL), - errmsg("The expected output of the cursor:%d and function:%d does not match", - estate->cursor_return_numbers, fcinfo.refcursor_data.return_number))); - } - for (int i = 0; i < fcinfo.refcursor_data.return_number; i++) { - CopyCursorInfoData(&estate->cursor_return_data[i], &fcinfo.refcursor_data.returnCursor[i]); - } - } + if (econtext->plpgsql_estate != NULL) { + PLpgSQL_execstate* estate = econtext->plpgsql_estate; + bool isVaildReturn = (fcinfo.refcursor_data.return_number > 0 && + estate->cursor_return_data != NULL && fcinfo.refcursor_data.returnCursor != NULL); + if (isVaildReturn) { + bool isVaildReturnNum = (fcinfo.refcursor_data.return_number > estate->cursor_return_numbers); + if (isVaildReturnNum) { + pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmodule(MOD_PLSQL), + errmsg("The expected output of the cursor:%d and function:%d does not match", + estate->cursor_return_numbers, fcinfo.refcursor_data.return_number))); + } + for (int i = 0; i < fcinfo.refcursor_data.return_number; i++) { + CopyCursorInfoData(&estate->cursor_return_data[i], &fcinfo.refcursor_data.returnCursor[i]); + } + } - if (var_dno != NULL) { - for (int i = 0; i < fcinfo.nargs; i++) { - if (var_dno[i] >= 0) { - int dno = var_dno[i]; - Cursor_Data* cursor_data = &fcinfo.refcursor_data.argCursor[i]; - PLpgSQL_execstate* execstate = econtext->plpgsql_estate; + if (var_dno != NULL) { + for (int i = 0; i < fcinfo.nargs; i++) { + if (var_dno[i] >= 0) { + int dno = var_dno[i]; + Cursor_Data* cursor_data = &fcinfo.refcursor_data.argCursor[i]; + PLpgSQL_execstate* execstate = econtext->plpgsql_estate; #ifdef USE_ASSERT_CHECKING - PLpgSQL_datum* datum = execstate->datums[dno]; + PLpgSQL_datum* datum = execstate->datums[dno]; #endif - Assert(datum->dtype == PLPGSQL_DTYPE_VAR); - Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID); + Assert(datum->dtype == PLPGSQL_DTYPE_VAR); + Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID); - ExecCopyDataToDatum(execstate->datums, dno, cursor_data); - } - } - } - } + ExecCopyDataToDatum(execstate->datums, dno, cursor_data); + } + } + } + } - pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); - } else { - result = ExecEvalExpr(funcexpr, econtext, &fcinfo.isnull, &rsinfo.isDone); - } + pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); + } else { + result = ExecEvalExpr(funcexpr, econtext, &fcinfo.isnull, &rsinfo.isDone); + } - /* Which protocol does function want to use? */ - if (rsinfo.returnMode == SFRM_ValuePerCall) { - /* - * Check for end of result set. - */ - if (rsinfo.isDone == ExprEndResult) { - break; - } + /* Which protocol does function want to use? */ + if (rsinfo.returnMode == SFRM_ValuePerCall) { + /* + * Check for end of result set. + */ + if (rsinfo.isDone == ExprEndResult) { + break; + } - /* - * Can't do anything very useful with NULL rowtype values. For a - * function returning set, we consider this a protocol violation - * (but another alternative would be to just ignore the result and - * "continue" to get another row). For a function not returning - * set, we fall out of the loop; we'll cons up an all-nulls result - * row below. - */ - if (returnsTuple && fcinfo.isnull && !has_out_param) { - if (!returnsSet) { - break; - } - ereport(ERROR, - (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + /* + * Can't do anything very useful with NULL rowtype values. For a + * function returning set, we consider this a protocol violation + * (but another alternative would be to just ignore the result and + * "continue" to get another row). For a function not returning + * set, we fall out of the loop; we'll cons up an all-nulls result + * row below. + */ + if (returnsTuple && fcinfo.isnull && !has_out_param) { + if (!returnsSet) { + break; + } + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("function returning set of rows cannot return null value"))); - } + } - /* - * If first time through, build tupdesc and tuplestore for result - */ - if (first_time) { - oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); - if (returnsTuple) { - /* - * Use the type info embedded in the rowtype Datum to look - * up the needed tupdesc. Make a copy for the query. - */ - HeapTupleHeader td; + /* + * If first time through, build tupdesc and tuplestore for result + */ + if (first_time) { + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + if (returnsTuple) { + /* + * Use the type info embedded in the rowtype Datum to look + * up the needed tupdesc. Make a copy for the query. + */ + HeapTupleHeader td; - td = DatumGetHeapTupleHeader(result); - if (IsA(funcexpr->expr, Const)) { - tupdesc = lookup_rowtype_tupdesc_copy( - ((Const*)funcexpr->expr)->consttype, HeapTupleHeaderGetTypMod(td)); - } else { - tupdesc = - lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); - } - } else { - /* - * Scalar type, so make a single-column descriptor - */ - tupdesc = CreateTemplateTupleDesc(1, false, TableAmHeap); - TupleDescInitEntry(tupdesc, (AttrNumber)1, "column", funcrettype, -1, 0); - } - tupstore = tuplestore_begin_heap(randomAccess, false, u_sess->attr.attr_memory.work_mem); - MemoryContextSwitchTo(oldcontext); - rsinfo.setResult = tupstore; - rsinfo.setDesc = tupdesc; - } + td = DatumGetHeapTupleHeader(result); + if (IsA(funcexpr->expr, Const)) { + tupdesc = lookup_rowtype_tupdesc_copy( + ((Const*)funcexpr->expr)->consttype, HeapTupleHeaderGetTypMod(td)); + } else { + tupdesc = + lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); + } + } else { + /* + * Scalar type, so make a single-column descriptor + */ + tupdesc = CreateTemplateTupleDesc(1, false, TableAmHeap); + TupleDescInitEntry(tupdesc, (AttrNumber)1, "column", funcrettype, -1, 0); + } + tupstore = tuplestore_begin_heap(randomAccess, false, u_sess->attr.attr_memory.work_mem); + MemoryContextSwitchTo(oldcontext); + rsinfo.setResult = tupstore; + rsinfo.setDesc = tupdesc; + } - /* - * Store current resultset item. - */ - if (returnsTuple) { - HeapTupleHeader td; + /* + * Store current resultset item. + */ + if (returnsTuple) { + HeapTupleHeader td; - td = DatumGetHeapTupleHeader(result); + td = DatumGetHeapTupleHeader(result); - /* - * Verify all returned rows have same subtype; necessary in - * case the type is RECORD. - */ - if ((HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid || - HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod) && - nodeTag(funcexpr->expr) != T_Const) { - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("rows returned by function are not all of the same row type"), - errdetail("return type id %u, tuple decription id %u, return typmod %d " - "tuple decription, typmod %d", - HeapTupleHeaderGetTypeId(td), - tupdesc->tdtypeid, - HeapTupleHeaderGetTypMod(td), - tupdesc->tdtypmod))); - } + /* + * Verify all returned rows have same subtype; necessary in + * case the type is RECORD. + */ + if ((HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid || + HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod) && + nodeTag(funcexpr->expr) != T_Const) { + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("rows returned by function are not all of the same row type"), + errdetail("return type id %u, tuple decription id %u, return typmod %d " + "tuple decription, typmod %d", + HeapTupleHeaderGetTypeId(td), + tupdesc->tdtypeid, + HeapTupleHeaderGetTypMod(td), + tupdesc->tdtypmod))); + } - /* - * tuplestore_puttuple needs a HeapTuple not a bare - * HeapTupleHeader, but it doesn't need all the fields. - */ - tmptup.t_len = HeapTupleHeaderGetDatumLength(td); - tmptup.t_data = td; + /* + * tuplestore_puttuple needs a HeapTuple not a bare + * HeapTupleHeader, but it doesn't need all the fields. + */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(td); + tmptup.t_data = td; - tuplestore_puttuple(tupstore, &tmptup); - } else { - tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull); - } + tuplestore_puttuple(tupstore, &tmptup); + } else { + tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull); + } - /* - * Are we done? - */ - if (rsinfo.isDone != ExprMultipleResult) { - break; - } - } else if (rsinfo.returnMode == SFRM_Materialize) { - /* check we're on the same page as the function author */ - if (!first_time || rsinfo.isDone != ExprSingleResult) { - ereport(ERROR, - (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), + /* + * Are we done? + */ + if (rsinfo.isDone != ExprMultipleResult) { + break; + } + } else if (rsinfo.returnMode == SFRM_Materialize) { + /* check we're on the same page as the function author */ + if (!first_time || rsinfo.isDone != ExprSingleResult) { + ereport(ERROR, + (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("table-function protocol for materialize mode was not followed"))); - } - /* Done evaluating the set result */ - break; - } else { - ereport(ERROR, - (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), + } + /* Done evaluating the set result */ + break; + } else { + ereport(ERROR, + (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("unrecognized table-function returnMode: %d", (int)rsinfo.returnMode))); - } + } - first_time = false; - } + first_time = false; + } no_function_result: - /* - * If we got nothing from the function (ie, an empty-set or NULL result), - * we have to create the tuplestore to return, and if it's a - * non-set-returning function then insert a single all-nulls row. - */ - if (rsinfo.setResult == NULL) { - MemoryContextSwitchTo(econtext->ecxt_per_query_memory); - tupstore = tuplestore_begin_heap(randomAccess, false, u_sess->attr.attr_memory.work_mem); - rsinfo.setResult = tupstore; - if (!returnsSet) { - int natts = expectedDesc->natts; - Datum* nulldatums = NULL; - bool* nullflags = NULL; - errno_t rc = EOK; + /* + * If we got nothing from the function (ie, an empty-set or NULL result), + * we have to create the tuplestore to return, and if it's a + * non-set-returning function then insert a single all-nulls row. + */ + if (rsinfo.setResult == NULL) { + MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + tupstore = tuplestore_begin_heap(randomAccess, false, u_sess->attr.attr_memory.work_mem); + rsinfo.setResult = tupstore; + if (!returnsSet) { + int natts = expectedDesc->natts; + Datum* nulldatums = NULL; + bool* nullflags = NULL; + errno_t rc = EOK; - MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - nulldatums = (Datum*)palloc0(natts * sizeof(Datum)); - nullflags = (bool*)palloc(natts * sizeof(bool)); - rc = memset_s(nullflags, natts * sizeof(bool), true, natts * sizeof(bool)); - securec_check(rc, "\0", "\0"); - MemoryContextSwitchTo(econtext->ecxt_per_query_memory); - tuplestore_putvalues(tupstore, expectedDesc, nulldatums, nullflags); - } - } + MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + nulldatums = (Datum*)palloc0(natts * sizeof(Datum)); + nullflags = (bool*)palloc(natts * sizeof(bool)); + rc = memset_s(nullflags, natts * sizeof(bool), true, natts * sizeof(bool)); + securec_check(rc, "\0", "\0"); + MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + tuplestore_putvalues(tupstore, expectedDesc, nulldatums, nullflags); + } + } - /* - * If function provided a tupdesc, cross-check it. We only really need to - * do this for functions returning RECORD, but might as well do it always. - */ - if (rsinfo.setDesc) { - tupledesc_match(expectedDesc, rsinfo.setDesc); + /* + * If function provided a tupdesc, cross-check it. We only really need to + * do this for functions returning RECORD, but might as well do it always. + */ + if (rsinfo.setDesc) { + tupledesc_match(expectedDesc, rsinfo.setDesc); - /* - * If it is a dynamically-allocated TupleDesc, free it: it is - * typically allocated in a per-query context, so we must avoid - * leaking it across multiple usages. - */ - if (rsinfo.setDesc->tdrefcount == -1) - FreeTupleDesc(rsinfo.setDesc); - } + /* + * If it is a dynamically-allocated TupleDesc, free it: it is + * typically allocated in a per-query context, so we must avoid + * leaking it across multiple usages. + */ + if (rsinfo.setDesc->tdrefcount == -1) + FreeTupleDesc(rsinfo.setDesc); + } - MemoryContextSwitchTo(callerContext); - econtext->plpgsql_estate = NULL; + MemoryContextSwitchTo(callerContext); + econtext->plpgsql_estate = NULL; - if (has_refcursor) { - if (fcinfo.refcursor_data.argCursor != NULL) - pfree_ext(fcinfo.refcursor_data.argCursor); - if (fcinfo.refcursor_data.returnCursor != NULL) - pfree_ext(fcinfo.refcursor_data.returnCursor); - if (var_dno != NULL) - pfree_ext(var_dno); - } + if (has_refcursor) { + if (fcinfo.refcursor_data.argCursor != NULL) + pfree_ext(fcinfo.refcursor_data.argCursor); + if (fcinfo.refcursor_data.returnCursor != NULL) + pfree_ext(fcinfo.refcursor_data.returnCursor); + if (var_dno != NULL) + pfree_ext(var_dno); + } - /* reset the u_sess->SPI_cxt.is_stp, u_sess->SPI_cxt.is_proconfig_set - and error message value */ - u_sess->SPI_cxt.is_stp = savedIsSTP; - u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet; - if (needResetErrMsg) { - stp_reset_commit_rolback_err_msg(); - } + /* reset the u_sess->SPI_cxt.is_stp, u_sess->SPI_cxt.is_proconfig_set + and error message value */ + u_sess->SPI_cxt.is_stp = savedIsSTP; + u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet; + if (needResetErrMsg) { + stp_reset_commit_rolback_err_msg(); + } - /* All done, pass back the tuplestore */ - return rsinfo.setResult; + /* All done, pass back the tuplestore */ + return rsinfo.setResult; } /* ---------------------------------------------------------------- - * ExecEvalFunc - * ExecEvalOper - * - * Evaluate the functional result of a list of arguments by calling the - * function manager. - * ---------------------------------------------------------------- - */ +* ExecEvalFunc +* ExecEvalOper +* +* Evaluate the functional result of a list of arguments by calling the +* function manager. +* ---------------------------------------------------------------- +*/ /* ---------------------------------------------------------------- - * ExecEvalFunc - * ---------------------------------------------------------------- - */ -static Datum ExecEvalFunc(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) +* ExecEvalFunc +* ---------------------------------------------------------------- +*/ +static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { /* This is called only the first time through */ FuncExpr* func = (FuncExpr*)fcache->xprstate.expr; @@ -3296,8 +3312,8 @@ static Datum ExecEvalFunc(FuncExprState* fcache, ExprContext* econtext, bool* is bool has_refcursor = false; int cursor_return_number = 0; - if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { - init_fcache(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, false, false); + if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { + if (fcache->xprstate.is_flt_frame) { init_fcache(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, false, false); has_refcursor = func_has_refcursor_args(func->funcid, &fcache->fcinfo_data); cursor_return_number = fcache->fcinfo_data.refcursor_data.return_number; @@ -3328,110 +3344,106 @@ static Datum ExecEvalFunc(FuncExprState* fcache, ExprContext* econtext, bool* is has_refcursor = func_has_refcursor_args(func->funcid, &fcache->fcinfo_data); cursor_return_number = fcache->fcinfo_data.refcursor_data.return_number; - if (func->funcformat == COERCE_EXPLICIT_CAST || func->funcformat == COERCE_IMPLICIT_CAST) { - target_type = func->funcresulttype; - source_type = fcache->fcinfo_data.argTypes[0]; + Assert(!fcache->func.fn_retset); - HeapTuple proc_tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(func->funcid), 0, 0, 0); - if (HeapTupleIsValid(proc_tuple)) { - Form_pg_proc proc_struct = (Form_pg_proc)GETSTRUCT(proc_tuple); - source_type = proc_struct->proargtypes.values[0]; - ReleaseSysCache(proc_tuple); - } - HeapTuple cast_tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(source_type), - ObjectIdGetDatum(target_type)); + if (has_refcursor) { + if (cursor_return_number > 0) { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; + return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); + } else { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; + return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); + } + } else { + if (cursor_return_number > 0) { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; + return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); + } else { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; + return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); + } + } + } - if (HeapTupleIsValid(cast_tuple)) { - Relation cast_rel = heap_open(CastRelationId, AccessShareLock); - int castowner_Anum = Anum_pg_cast_castowner; - if (castowner_Anum <= (int)HeapTupleHeaderGetNatts(cast_tuple->t_data, cast_rel->rd_att)) { - bool isnull = true; - Datum datum = fastgetattr(cast_tuple, Anum_pg_cast_castowner, cast_rel->rd_att, &isnull); - if (!isnull) { - u_sess->exec_cxt.cast_owner = DatumGetObjectId(datum); - } else { - u_sess->exec_cxt.cast_owner = InvalidCastOwnerId; - } - } - heap_close(cast_rel, AccessShareLock); - ReleaseSysCache(cast_tuple); - } - } + init_fcache(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, false, true); - /* - * We need to invoke ExecMakeFunctionResult if either the function itself - * or any of its input expressions can return a set. Otherwise, invoke - * ExecMakeFunctionResultNoSets. In either case, change the evalfunc - * pointer to go directly there on subsequent uses. - */ - if (fcache->func.fn_retset) { - if (has_refcursor) { - if (cursor_return_number > 0) { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; - return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); - } else { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; - return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); - } - } else { - if (cursor_return_number > 0) { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; - return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); - } else { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; - return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); - } - } - } else if (expression_returns_set((Node*)func->args)) { - if (has_refcursor) { - if (cursor_return_number > 0) { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; - return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); - } else { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; - return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); - } - } else { - if (cursor_return_number > 0) { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; - return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); - } else { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; - return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); - } - } - } else { - if (has_refcursor) { - if (cursor_return_number > 0) { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; - return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); - } else { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; - return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); - } - } else { - if (cursor_return_number > 0) { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; - return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); - } else { - fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; - return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); - } - } - } + has_refcursor = func_has_refcursor_args(func->funcid, &fcache->fcinfo_data); + cursor_return_number = fcache->fcinfo_data.refcursor_data.return_number; + + /* + * We need to invoke ExecMakeFunctionResult if either the function itself + * or any of its input expressions can return a set. Otherwise, invoke + * ExecMakeFunctionResultNoSets. In either case, change the evalfunc + * pointer to go directly there on subsequent uses. + */ + if (fcache->func.fn_retset) { + if (has_refcursor) { + if (cursor_return_number > 0) { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; + return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); + } else { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; + return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); + } + } else { + if (cursor_return_number > 0) { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; + return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); + } else { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; + return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); + } + } + } else if (expression_returns_set((Node *)func->args)) { + if (has_refcursor) { + if (cursor_return_number > 0) { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; + return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); + } else { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; + return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); + } + } else { + if (cursor_return_number > 0) { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; + return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); + } else { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; + return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); + } + } + } else { + if (has_refcursor) { + if (cursor_return_number > 0) { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; + return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); + } else { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; + return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); + } + } else { + if (cursor_return_number > 0) { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; + return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); + } else { + fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; + return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); + } + } + } } /* ---------------------------------------------------------------- - * ExecEvalOper - * ---------------------------------------------------------------- - */ +* ExecEvalOper +* ---------------------------------------------------------------- +*/ static Datum ExecEvalOper(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { /* This is called only the first time through */ OpExpr* op = (OpExpr*)fcache->xprstate.expr; bool has_refcursor = false; int cursor_return_number = 0; - if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { + if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { init_fcache(op->opfuncid, op->inputcollid, fcache, econtext->ecxt_per_query_memory, false, false); has_refcursor = func_has_refcursor_args(op->opfuncid, &fcache->fcinfo_data); cursor_return_number = fcache->fcinfo_data.refcursor_data.return_number; @@ -3524,26 +3536,26 @@ static Datum ExecEvalOper(FuncExprState* fcache, ExprContext* econtext, bool* is } /* ---------------------------------------------------------------- - * ExecEvalDistinct - * - * IS DISTINCT FROM must evaluate arguments to determine whether - * they are NULL; if either is NULL then the result is already - * known. If neither is NULL, then proceed to evaluate the - * function. Note that this is *always* derived from the equals - * operator, but since we need special processing of the arguments - * we can not simply reuse ExecEvalOper() or ExecEvalFunc(). - * ---------------------------------------------------------------- - */ +* ExecEvalDistinct +* +* IS DISTINCT FROM must evaluate arguments to determine whether +* they are NULL; if either is NULL then the result is already +* known. If neither is NULL, then proceed to evaluate the +* function. Note that this is *always* derived from the equals +* operator, but since we need special processing of the arguments +* we can not simply reuse ExecEvalOper() or ExecEvalFunc(). +* ---------------------------------------------------------------- +*/ static Datum ExecEvalDistinct(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Datum result; - FunctionCallInfo fcinfo; - ExprDoneCond argDone; + Datum result; + FunctionCallInfo fcinfo; + ExprDoneCond argDone; - /* Set default values for result flags: non-null, not a set result */ - *isNull = false; - if (isDone != NULL) - *isDone = ExprSingleResult; + /* Set default values for result flags: non-null, not a set result */ + *isNull = false; + if (isDone != NULL) + *isDone = ExprSingleResult; /* * Initialize function cache if first time through @@ -3558,69 +3570,69 @@ static Datum ExecEvalDistinct(FuncExprState* fcache, ExprContext* econtext, bool } } - /* - * Evaluate arguments - */ - fcinfo = &fcache->fcinfo_data; - argDone = ExecEvalFuncArgs(fcinfo, fcache->args, econtext); - if (argDone != ExprSingleResult) - ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("IS DISTINCT FROM does not support set arguments"))); - Assert(fcinfo->nargs == 2); + /* + * Evaluate arguments + */ + fcinfo = &fcache->fcinfo_data; + argDone = ExecEvalFuncArgs(fcinfo, fcache->args, econtext); + if (argDone != ExprSingleResult) + ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("IS DISTINCT FROM does not support set arguments"))); + Assert(fcinfo->nargs == 2); - if (fcinfo->argnull[0] && fcinfo->argnull[1]) { - /* Both NULL? Then is not distinct... */ - result = BoolGetDatum(FALSE); - } else if (fcinfo->argnull[0] || fcinfo->argnull[1]) { - /* Only one is NULL? Then is distinct... */ - result = BoolGetDatum(TRUE); - } else { - fcinfo->isnull = false; - result = FunctionCallInvoke(fcinfo); - *isNull = fcinfo->isnull; - /* Must invert result of "=" */ - result = BoolGetDatum(!DatumGetBool(result)); - } + if (fcinfo->argnull[0] && fcinfo->argnull[1]) { + /* Both NULL? Then is not distinct... */ + result = BoolGetDatum(FALSE); + } else if (fcinfo->argnull[0] || fcinfo->argnull[1]) { + /* Only one is NULL? Then is distinct... */ + result = BoolGetDatum(TRUE); + } else { + fcinfo->isnull = false; + result = FunctionCallInvoke(fcinfo); + *isNull = fcinfo->isnull; + /* Must invert result of "=" */ + result = BoolGetDatum(!DatumGetBool(result)); + } - return result; + return result; } /* - * ExecEvalScalarArrayOp - * - * Evaluate "scalar op ANY/ALL (array)". The operator always yields boolean, - * and we combine the results across all array elements using OR and AND - * (for ANY and ALL respectively). Of course we short-circuit as soon as - * the result is known. - */ +* ExecEvalScalarArrayOp +* +* Evaluate "scalar op ANY/ALL (array)". The operator always yields boolean, +* and we combine the results across all array elements using OR and AND +* (for ANY and ALL respectively). Of course we short-circuit as soon as +* the result is known. +*/ static Datum ExecEvalScalarArrayOp( - ScalarArrayOpExprState* sstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + ScalarArrayOpExprState* sstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - ScalarArrayOpExpr* opexpr = (ScalarArrayOpExpr*)sstate->fxprstate.xprstate.expr; - bool useOr = opexpr->useOr; - ArrayType* arr = NULL; - int nitems; - Datum result; - bool resultnull = false; - FunctionCallInfo fcinfo; - ExprDoneCond argDone; - int i; - int16 typlen; - bool typbyval = false; - char typalign; - char* s = NULL; - bits8* bitmap = NULL; - int bitmask; + ScalarArrayOpExpr* opexpr = (ScalarArrayOpExpr*)sstate->fxprstate.xprstate.expr; + bool useOr = opexpr->useOr; + ArrayType* arr = NULL; + int nitems; + Datum result; + bool resultnull = false; + FunctionCallInfo fcinfo; + ExprDoneCond argDone; + int i; + int16 typlen; + bool typbyval = false; + char typalign; + char* s = NULL; + bits8* bitmap = NULL; + int bitmask; - /* Set default values for result flags: non-null, not a set result */ - *isNull = false; - if (isDone != NULL) - *isDone = ExprSingleResult; + /* Set default values for result flags: non-null, not a set result */ + *isNull = false; + if (isDone != NULL) + *isDone = ExprSingleResult; - /* - * Initialize function cache if first time through - */ - if (sstate->fxprstate.func.fn_oid == InvalidOid) { - if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { + /* + * Initialize function cache if first time through + */ + if (sstate->fxprstate.func.fn_oid == InvalidOid) { + if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { init_fcache(opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate, econtext->ecxt_per_query_memory, false, false); } else { @@ -3630,71 +3642,71 @@ static Datum ExecEvalScalarArrayOp( } } - /* - * Evaluate arguments - */ - fcinfo = &sstate->fxprstate.fcinfo_data; - /* init the number of arguments to a function. */ - InitFunctionCallInfoArgs(*fcinfo, 2, 1); - argDone = ExecEvalFuncArgs(fcinfo, sstate->fxprstate.args, econtext); - if (argDone != ExprSingleResult) - ereport( - ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("op ANY/ALL (array) does not support set arguments"))); - Assert(fcinfo->nargs == 2); + /* + * Evaluate arguments + */ + fcinfo = &sstate->fxprstate.fcinfo_data; + /* init the number of arguments to a function. */ + InitFunctionCallInfoArgs(*fcinfo, 2, 1); + argDone = ExecEvalFuncArgs(fcinfo, sstate->fxprstate.args, econtext); + if (argDone != ExprSingleResult) + ereport( + ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("op ANY/ALL (array) does not support set arguments"))); + Assert(fcinfo->nargs == 2); - /* - * If the array is NULL then we return NULL --- it's not very meaningful - * to do anything else, even if the operator isn't strict. - */ - if (fcinfo->argnull[1]) { - *isNull = true; - return (Datum)0; - } - /* Else okay to fetch and detoast the array */ - arr = DatumGetArrayTypeP(fcinfo->arg[1]); + /* + * If the array is NULL then we return NULL --- it's not very meaningful + * to do anything else, even if the operator isn't strict. + */ + if (fcinfo->argnull[1]) { + *isNull = true; + return (Datum)0; + } + /* Else okay to fetch and detoast the array */ + arr = DatumGetArrayTypeP(fcinfo->arg[1]); - /* - * If the array is empty, we return either FALSE or TRUE per the useOr - * flag. This is correct even if the scalar is NULL; since we would - * evaluate the operator zero times, it matters not whether it would want - * to return NULL. - */ - nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); - if (nitems <= 0) - return BoolGetDatum(!useOr); + /* + * If the array is empty, we return either FALSE or TRUE per the useOr + * flag. This is correct even if the scalar is NULL; since we would + * evaluate the operator zero times, it matters not whether it would want + * to return NULL. + */ + nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); + if (nitems <= 0) + return BoolGetDatum(!useOr); - /* - * If the scalar is NULL, and the function is strict, return NULL; no - * point in iterating the loop. - */ - if (fcinfo->argnull[0] && sstate->fxprstate.func.fn_strict) { - *isNull = true; - return (Datum)0; - } + /* + * If the scalar is NULL, and the function is strict, return NULL; no + * point in iterating the loop. + */ + if (fcinfo->argnull[0] && sstate->fxprstate.func.fn_strict) { + *isNull = true; + return (Datum)0; + } - /* - * We arrange to look up info about the element type only once per series - * of calls, assuming the element type doesn't change underneath us. - */ - if (sstate->element_type != ARR_ELEMTYPE(arr)) { - get_typlenbyvalalign(ARR_ELEMTYPE(arr), &sstate->typlen, &sstate->typbyval, &sstate->typalign); - sstate->element_type = ARR_ELEMTYPE(arr); - } - typlen = sstate->typlen; - typbyval = sstate->typbyval; - typalign = sstate->typalign; + /* + * We arrange to look up info about the element type only once per series + * of calls, assuming the element type doesn't change underneath us. + */ + if (sstate->element_type != ARR_ELEMTYPE(arr)) { + get_typlenbyvalalign(ARR_ELEMTYPE(arr), &sstate->typlen, &sstate->typbyval, &sstate->typalign); + sstate->element_type = ARR_ELEMTYPE(arr); + } + typlen = sstate->typlen; + typbyval = sstate->typbyval; + typalign = sstate->typalign; - result = BoolGetDatum(!useOr); - resultnull = false; + result = BoolGetDatum(!useOr); + resultnull = false; - /* Loop over the array elements */ - s = (char*)ARR_DATA_PTR(arr); - bitmap = ARR_NULLBITMAP(arr); - bitmask = 1; + /* Loop over the array elements */ + s = (char*)ARR_DATA_PTR(arr); + bitmap = ARR_NULLBITMAP(arr); + bitmask = 1; - for (i = 0; i < nitems; i++) { - Datum elt; - Datum thisresult; + for (i = 0; i < nitems; i++) { + Datum elt; + Datum thisresult; /* Get array element, checking for NULL */ if (bitmap && (*bitmap & bitmask) == 0) { @@ -3709,982 +3721,982 @@ static Datum ExecEvalScalarArrayOp( fcinfo->argTypes[1] = ARR_ELEMTYPE(arr); } - /* Call comparison function */ - if (fcinfo->argnull[1] && sstate->fxprstate.func.fn_strict) { - fcinfo->isnull = true; - thisresult = (Datum)0; - } else { - fcinfo->isnull = false; - thisresult = FunctionCallInvoke(fcinfo); - } + /* Call comparison function */ + if (fcinfo->argnull[1] && sstate->fxprstate.func.fn_strict) { + fcinfo->isnull = true; + thisresult = (Datum)0; + } else { + fcinfo->isnull = false; + thisresult = FunctionCallInvoke(fcinfo); + } - /* Combine results per OR or AND semantics */ - if (fcinfo->isnull) - resultnull = true; - else if (useOr) { - if (DatumGetBool(thisresult)) { - result = BoolGetDatum(true); - resultnull = false; - break; /* needn't look at any more elements */ - } - } else { - if (!DatumGetBool(thisresult)) { - result = BoolGetDatum(false); - resultnull = false; - break; /* needn't look at any more elements */ - } - } + /* Combine results per OR or AND semantics */ + if (fcinfo->isnull) + resultnull = true; + else if (useOr) { + if (DatumGetBool(thisresult)) { + result = BoolGetDatum(true); + resultnull = false; + break; /* needn't look at any more elements */ + } + } else { + if (!DatumGetBool(thisresult)) { + result = BoolGetDatum(false); + resultnull = false; + break; /* needn't look at any more elements */ + } + } - /* advance bitmap pointer if any */ - if (bitmap != NULL) { - bitmask <<= 1; - if (bitmask == 0x100) { - bitmap++; - bitmask = 1; - } - } - } + /* advance bitmap pointer if any */ + if (bitmap != NULL) { + bitmask <<= 1; + if (bitmask == 0x100) { + bitmap++; + bitmask = 1; + } + } + } - *isNull = resultnull; - return result; + *isNull = resultnull; + return result; } /* ---------------------------------------------------------------- - * ExecEvalNot - * ExecEvalOr - * ExecEvalAnd - * - * Evaluate boolean expressions, with appropriate short-circuiting. - * - * The query planner reformulates clause expressions in the - * qualification to conjunctive normal form. If we ever get - * an AND to evaluate, we can be sure that it's not a top-level - * clause in the qualification, but appears lower (as a function - * argument, for example), or in the target list. Not that you - * need to know this, mind you... - * ---------------------------------------------------------------- - */ +* ExecEvalNot +* ExecEvalOr +* ExecEvalAnd +* +* Evaluate boolean expressions, with appropriate short-circuiting. +* +* The query planner reformulates clause expressions in the +* qualification to conjunctive normal form. If we ever get +* an AND to evaluate, we can be sure that it's not a top-level +* clause in the qualification, but appears lower (as a function +* argument, for example), or in the target list. Not that you +* need to know this, mind you... +* ---------------------------------------------------------------- +*/ static Datum ExecEvalNot(BoolExprState* notclause, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - ExprState* clause = (ExprState*)linitial(notclause->args); - Datum expr_value; + ExprState* clause = (ExprState*)linitial(notclause->args); + Datum expr_value; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - expr_value = ExecEvalExpr(clause, econtext, isNull, NULL); + expr_value = ExecEvalExpr(clause, econtext, isNull, NULL); - /* - * if the expression evaluates to null, then we just cascade the null back - * to whoever called us. - */ - if (*isNull) - return expr_value; + /* + * if the expression evaluates to null, then we just cascade the null back + * to whoever called us. + */ + if (*isNull) + return expr_value; - /* - * evaluation of 'not' is simple.. expr is false, then return 'true' and - * vice versa. - */ - return BoolGetDatum(!DatumGetBool(expr_value)); + /* + * evaluation of 'not' is simple.. expr is false, then return 'true' and + * vice versa. + */ + return BoolGetDatum(!DatumGetBool(expr_value)); } /* ---------------------------------------------------------------- - * ExecEvalOr - * ---------------------------------------------------------------- - */ +* ExecEvalOr +* ---------------------------------------------------------------- +*/ static Datum ExecEvalOr(BoolExprState* orExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - List* clauses = orExpr->args; - ListCell* clause = NULL; - bool AnyNull = false; + List* clauses = orExpr->args; + ListCell* clause = NULL; + bool AnyNull = false; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - AnyNull = false; + AnyNull = false; - /* - * If any of the clauses is TRUE, the OR result is TRUE regardless of the - * states of the rest of the clauses, so we can stop evaluating and return - * TRUE immediately. If none are TRUE and one or more is NULL, we return - * NULL; otherwise we return FALSE. This makes sense when you interpret - * NULL as "don't know": if we have a TRUE then the OR is TRUE even if we - * aren't sure about some of the other inputs. If all the known inputs are - * FALSE, but we have one or more "don't knows", then we have to report - * that we "don't know" what the OR's result should be --- perhaps one of - * the "don't knows" would have been TRUE if we'd known its value. Only - * when all the inputs are known to be FALSE can we state confidently that - * the OR's result is FALSE. - */ - foreach (clause, clauses) { - ExprState* clausestate = (ExprState*)lfirst(clause); - Datum clause_value; + /* + * If any of the clauses is TRUE, the OR result is TRUE regardless of the + * states of the rest of the clauses, so we can stop evaluating and return + * TRUE immediately. If none are TRUE and one or more is NULL, we return + * NULL; otherwise we return FALSE. This makes sense when you interpret + * NULL as "don't know": if we have a TRUE then the OR is TRUE even if we + * aren't sure about some of the other inputs. If all the known inputs are + * FALSE, but we have one or more "don't knows", then we have to report + * that we "don't know" what the OR's result should be --- perhaps one of + * the "don't knows" would have been TRUE if we'd known its value. Only + * when all the inputs are known to be FALSE can we state confidently that + * the OR's result is FALSE. + */ + foreach (clause, clauses) { + ExprState* clausestate = (ExprState*)lfirst(clause); + Datum clause_value; - clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL); + clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL); - /* - * if we have a non-null true result, then return it. - */ - if (*isNull) - AnyNull = true; /* remember we got a null */ - else if (DatumGetBool(clause_value)) - return clause_value; - } + /* + * if we have a non-null true result, then return it. + */ + if (*isNull) + AnyNull = true; /* remember we got a null */ + else if (DatumGetBool(clause_value)) + return clause_value; + } - /* AnyNull is true if at least one clause evaluated to NULL */ - *isNull = AnyNull; - return BoolGetDatum(false); + /* AnyNull is true if at least one clause evaluated to NULL */ + *isNull = AnyNull; + return BoolGetDatum(false); } /* ---------------------------------------------------------------- - * ExecEvalAnd - * ---------------------------------------------------------------- - */ +* ExecEvalAnd +* ---------------------------------------------------------------- +*/ static Datum ExecEvalAnd(BoolExprState* andExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - List* clauses = andExpr->args; - ListCell* clause = NULL; - bool AnyNull = false; + List* clauses = andExpr->args; + ListCell* clause = NULL; + bool AnyNull = false; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - AnyNull = false; + AnyNull = false; - /* - * If any of the clauses is FALSE, the AND result is FALSE regardless of - * the states of the rest of the clauses, so we can stop evaluating and - * return FALSE immediately. If none are FALSE and one or more is NULL, - * we return NULL; otherwise we return TRUE. This makes sense when you - * interpret NULL as "don't know", using the same sort of reasoning as for - * OR, above. - */ - foreach (clause, clauses) { - ExprState* clausestate = (ExprState*)lfirst(clause); - Datum clause_value; + /* + * If any of the clauses is FALSE, the AND result is FALSE regardless of + * the states of the rest of the clauses, so we can stop evaluating and + * return FALSE immediately. If none are FALSE and one or more is NULL, + * we return NULL; otherwise we return TRUE. This makes sense when you + * interpret NULL as "don't know", using the same sort of reasoning as for + * OR, above. + */ + foreach (clause, clauses) { + ExprState* clausestate = (ExprState*)lfirst(clause); + Datum clause_value; - clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL); + clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL); - /* - * if we have a non-null false result, then return it. - */ - if (*isNull) - AnyNull = true; /* remember we got a null */ - else if (!DatumGetBool(clause_value)) - return clause_value; - } + /* + * if we have a non-null false result, then return it. + */ + if (*isNull) + AnyNull = true; /* remember we got a null */ + else if (!DatumGetBool(clause_value)) + return clause_value; + } - /* AnyNull is true if at least one clause evaluated to NULL */ - *isNull = AnyNull; - return BoolGetDatum(!AnyNull); + /* AnyNull is true if at least one clause evaluated to NULL */ + *isNull = AnyNull; + return BoolGetDatum(!AnyNull); } /* ---------------------------------------------------------------- - * ExecEvalConvertRowtype - * - * Evaluate a rowtype coercion operation. This may require - * rearranging field positions. - * ---------------------------------------------------------------- - */ +* ExecEvalConvertRowtype +* +* Evaluate a rowtype coercion operation. This may require +* rearranging field positions. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalConvertRowtype( - ConvertRowtypeExprState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + ConvertRowtypeExprState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - ConvertRowtypeExpr* convert = (ConvertRowtypeExpr*)cstate->xprstate.expr; - HeapTuple result; - Datum tupDatum; - HeapTupleHeader tuple; - HeapTupleData tmptup; + ConvertRowtypeExpr* convert = (ConvertRowtypeExpr*)cstate->xprstate.expr; + HeapTuple result; + Datum tupDatum; + HeapTupleHeader tuple; + HeapTupleData tmptup; - tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone); + tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone); - /* this test covers the isDone exception too: */ - if (*isNull) - return tupDatum; + /* this test covers the isDone exception too: */ + if (*isNull) + return tupDatum; - tuple = DatumGetHeapTupleHeader(tupDatum); + tuple = DatumGetHeapTupleHeader(tupDatum); - /* Lookup tupdescs if first time through or after rescan */ - if (cstate->indesc == NULL) { - get_cached_rowtype(exprType((Node*)convert->arg), -1, &cstate->indesc, econtext); - cstate->initialized = false; - } - if (cstate->outdesc == NULL) { - get_cached_rowtype(convert->resulttype, -1, &cstate->outdesc, econtext); - cstate->initialized = false; - } + /* Lookup tupdescs if first time through or after rescan */ + if (cstate->indesc == NULL) { + get_cached_rowtype(exprType((Node*)convert->arg), -1, &cstate->indesc, econtext); + cstate->initialized = false; + } + if (cstate->outdesc == NULL) { + get_cached_rowtype(convert->resulttype, -1, &cstate->outdesc, econtext); + cstate->initialized = false; + } - Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid); - Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod); + Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid); + Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod); - /* if first time through, initialize conversion map */ - if (!cstate->initialized) { - MemoryContext old_cxt; + /* if first time through, initialize conversion map */ + if (!cstate->initialized) { + MemoryContext old_cxt; - /* allocate map in long-lived memory context */ - old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + /* allocate map in long-lived memory context */ + old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); - /* prepare map from old to new attribute numbers */ - cstate->map = - convert_tuples_by_name(cstate->indesc, cstate->outdesc, gettext_noop("could not convert row type")); - cstate->initialized = true; + /* prepare map from old to new attribute numbers */ + cstate->map = + convert_tuples_by_name(cstate->indesc, cstate->outdesc, gettext_noop("could not convert row type")); + cstate->initialized = true; - MemoryContextSwitchTo(old_cxt); - } + MemoryContextSwitchTo(old_cxt); + } - /* - * No-op if no conversion needed (not clear this can happen here). - */ - if (cstate->map == NULL) - return tupDatum; + /* + * No-op if no conversion needed (not clear this can happen here). + */ + if (cstate->map == NULL) + return tupDatum; - /* - * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader. - */ - tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); - tmptup.t_data = tuple; + /* + * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader. + */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); + tmptup.t_data = tuple; - result = do_convert_tuple(&tmptup, cstate->map); + result = do_convert_tuple(&tmptup, cstate->map); - return HeapTupleGetDatum(result); + return HeapTupleGetDatum(result); } /* ---------------------------------------------------------------- - * ExecEvalCase - * - * Evaluate a CASE clause. Will have boolean expressions - * inside the WHEN clauses, and will have expressions - * for results. - * - thomas 1998-11-09 - * ---------------------------------------------------------------- - */ +* ExecEvalCase +* +* Evaluate a CASE clause. Will have boolean expressions +* inside the WHEN clauses, and will have expressions +* for results. +* - thomas 1998-11-09 +* ---------------------------------------------------------------- +*/ static Datum ExecEvalCase(CaseExprState* caseExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - List* clauses = caseExpr->args; - ListCell* clause = NULL; - Datum save_datum; - bool save_isNull = false; + List* clauses = caseExpr->args; + ListCell* clause = NULL; + Datum save_datum; + bool save_isNull = false; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - /* - * If there's a test expression, we have to evaluate it and save the value - * where the CaseTestExpr placeholders can find it. We must save and - * restore prior setting of econtext's caseValue fields, in case this node - * is itself within a larger CASE.Furthermore, don't assign to the - * econtext fields until after returning from evaluation of the test - * expression. We used to pass &econtext->caseValue_isNull to the - * recursive call, but that leads to aliasing that variable within said - * call, which can (and did) produce bugs when the test expression itself - * contains a CASE. - * - * If there's no test expression, we don't actually need to save and - * restore these fields; but it's less code to just do so unconditionally. - */ - save_datum = econtext->caseValue_datum; - save_isNull = econtext->caseValue_isNull; + /* + * If there's a test expression, we have to evaluate it and save the value + * where the CaseTestExpr placeholders can find it. We must save and + * restore prior setting of econtext's caseValue fields, in case this node + * is itself within a larger CASE.Furthermore, don't assign to the + * econtext fields until after returning from evaluation of the test + * expression. We used to pass &econtext->caseValue_isNull to the + * recursive call, but that leads to aliasing that variable within said + * call, which can (and did) produce bugs when the test expression itself + * contains a CASE. + * + * If there's no test expression, we don't actually need to save and + * restore these fields; but it's less code to just do so unconditionally. + */ + save_datum = econtext->caseValue_datum; + save_isNull = econtext->caseValue_isNull; - if (caseExpr->arg) { - bool arg_isNull = false; - econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg, econtext, &arg_isNull, NULL); - econtext->caseValue_isNull = arg_isNull; - } + if (caseExpr->arg) { + bool arg_isNull = false; + econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg, econtext, &arg_isNull, NULL); + econtext->caseValue_isNull = arg_isNull; + } - /* - * we evaluate each of the WHEN clauses in turn, as soon as one is true we - * return the corresponding result. If none are true then we return the - * value of the default clause, or NULL if there is none. - */ - foreach (clause, clauses) { - CaseWhenState* wclause = (CaseWhenState*)lfirst(clause); - Datum clause_value; - bool clause_isNull = false; + /* + * we evaluate each of the WHEN clauses in turn, as soon as one is true we + * return the corresponding result. If none are true then we return the + * value of the default clause, or NULL if there is none. + */ + foreach (clause, clauses) { + CaseWhenState* wclause = (CaseWhenState*)lfirst(clause); + Datum clause_value; + bool clause_isNull = false; - clause_value = ExecEvalExpr(wclause->expr, econtext, &clause_isNull, NULL); + clause_value = ExecEvalExpr(wclause->expr, econtext, &clause_isNull, NULL); - /* - * if we have a true test, then we return the result, since the case - * statement is satisfied. A NULL result from the test is not - * considered true. - */ - if (DatumGetBool(clause_value) && !clause_isNull) { - econtext->caseValue_datum = save_datum; - econtext->caseValue_isNull = save_isNull; - return ExecEvalExpr(wclause->result, econtext, isNull, isDone); - } - } + /* + * if we have a true test, then we return the result, since the case + * statement is satisfied. A NULL result from the test is not + * considered true. + */ + if (DatumGetBool(clause_value) && !clause_isNull) { + econtext->caseValue_datum = save_datum; + econtext->caseValue_isNull = save_isNull; + return ExecEvalExpr(wclause->result, econtext, isNull, isDone); + } + } - econtext->caseValue_datum = save_datum; - econtext->caseValue_isNull = save_isNull; + econtext->caseValue_datum = save_datum; + econtext->caseValue_isNull = save_isNull; - if (caseExpr->defresult) { - return ExecEvalExpr(caseExpr->defresult, econtext, isNull, isDone); - } + if (caseExpr->defresult) { + return ExecEvalExpr(caseExpr->defresult, econtext, isNull, isDone); + } - *isNull = true; - return (Datum)0; + *isNull = true; + return (Datum)0; } /* - * ExecEvalCaseTestExpr - * - * Return the value stored by CASE. - */ +* ExecEvalCaseTestExpr +* +* Return the value stored by CASE. +*/ static Datum ExecEvalCaseTestExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - if (isDone != NULL) - *isDone = ExprSingleResult; - *isNull = econtext->caseValue_isNull; - return econtext->caseValue_datum; + if (isDone != NULL) + *isDone = ExprSingleResult; + *isNull = econtext->caseValue_isNull; + return econtext->caseValue_datum; } /* - * ExecEvalGroupingFuncExpr - * - * Return a bitmask with a bit for each (unevaluated) argument expression - * (rightmost arg is least significant bit). - * - * A bit is set if the corresponding expression is NOT part of the set of - * grouping expressions in the current grouping set. - */ +* ExecEvalGroupingFuncExpr +* +* Return a bitmask with a bit for each (unevaluated) argument expression +* (rightmost arg is least significant bit). +* +* A bit is set if the corresponding expression is NOT part of the set of +* grouping expressions in the current grouping set. +*/ static Datum ExecEvalGroupingFuncExpr( - GroupingFuncExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + GroupingFuncExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - int result = 0; - int attnum = 0; - Bitmapset* grouped_cols = gstate->aggstate->grouped_cols; - ListCell* lc = NULL; + int result = 0; + int attnum = 0; + Bitmapset* grouped_cols = gstate->aggstate->grouped_cols; + ListCell* lc = NULL; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - *isNull = false; + *isNull = false; - foreach (lc, (gstate->clauses)) { - attnum = lfirst_int(lc); + foreach (lc, (gstate->clauses)) { + attnum = lfirst_int(lc); - result = (uint32)result << 1; + result = (uint32)result << 1; - if (!bms_is_member(attnum, grouped_cols)) - result = (uint32)result | 1; - } + if (!bms_is_member(attnum, grouped_cols)) + result = (uint32)result | 1; + } - return (Datum)result; + return (Datum)result; } /* ---------------------------------------------------------------- - * ExecEvalArray - ARRAY[] expressions - * ---------------------------------------------------------------- - */ +* ExecEvalArray - ARRAY[] expressions +* ---------------------------------------------------------------- +*/ static Datum ExecEvalArray(ArrayExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - ArrayExpr* arrayExpr = (ArrayExpr*)astate->xprstate.expr; - ArrayType* result = NULL; - ListCell* element = NULL; - Oid element_type = arrayExpr->element_typeid; - int ndims = 0; - int dims[MAXDIM]; - int lbs[MAXDIM]; + ArrayExpr* arrayExpr = (ArrayExpr*)astate->xprstate.expr; + ArrayType* result = NULL; + ListCell* element = NULL; + Oid element_type = arrayExpr->element_typeid; + int ndims = 0; + int dims[MAXDIM]; + int lbs[MAXDIM]; - /* Set default values for result flags: non-null, not a set result */ - *isNull = false; - if (isDone != NULL) - *isDone = ExprSingleResult; + /* Set default values for result flags: non-null, not a set result */ + *isNull = false; + if (isDone != NULL) + *isDone = ExprSingleResult; - if (!arrayExpr->multidims) { - /* Elements are presumably of scalar type */ - int nelems; - Datum* dvalues = NULL; - bool* dnulls = NULL; - int i = 0; + if (!arrayExpr->multidims) { + /* Elements are presumably of scalar type */ + int nelems; + Datum* dvalues = NULL; + bool* dnulls = NULL; + int i = 0; - ndims = 1; - nelems = list_length(astate->elements); + ndims = 1; + nelems = list_length(astate->elements); - /* Shouldn't happen here, but if length is 0, return empty array */ - if (nelems == 0) - return PointerGetDatum(construct_empty_array(element_type)); + /* Shouldn't happen here, but if length is 0, return empty array */ + if (nelems == 0) + return PointerGetDatum(construct_empty_array(element_type)); - dvalues = (Datum*)palloc(nelems * sizeof(Datum)); - dnulls = (bool*)palloc(nelems * sizeof(bool)); + dvalues = (Datum*)palloc(nelems * sizeof(Datum)); + dnulls = (bool*)palloc(nelems * sizeof(bool)); - /* loop through and build array of datums */ - foreach (element, astate->elements) { - ExprState* e = (ExprState*)lfirst(element); + /* loop through and build array of datums */ + foreach (element, astate->elements) { + ExprState* e = (ExprState*)lfirst(element); - dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i], NULL); - i++; - } + dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i], NULL); + i++; + } - /* setup for 1-D array of the given length */ - dims[0] = nelems; - lbs[0] = 1; + /* setup for 1-D array of the given length */ + dims[0] = nelems; + lbs[0] = 1; - result = construct_md_array( - dvalues, dnulls, ndims, dims, lbs, element_type, astate->elemlength, astate->elembyval, astate->elemalign); - } else { - /* Must be nested array expressions */ - int nbytes = 0; - int nitems = 0; - int outer_nelems = 0; - int elem_ndims = 0; - int* elem_dims = NULL; - int* elem_lbs = NULL; - bool firstone = true; - bool havenulls = false; - bool haveempty = false; - char** subdata; - bits8** subbitmaps; - int* subbytes = NULL; - int* subnitems = NULL; - int i; - int32 dataoffset; - char* dat = NULL; - int iitem; - errno_t rc = 0; + result = construct_md_array( + dvalues, dnulls, ndims, dims, lbs, element_type, astate->elemlength, astate->elembyval, astate->elemalign); + } else { + /* Must be nested array expressions */ + int nbytes = 0; + int nitems = 0; + int outer_nelems = 0; + int elem_ndims = 0; + int* elem_dims = NULL; + int* elem_lbs = NULL; + bool firstone = true; + bool havenulls = false; + bool haveempty = false; + char** subdata; + bits8** subbitmaps; + int* subbytes = NULL; + int* subnitems = NULL; + int i; + int32 dataoffset; + char* dat = NULL; + int iitem; + errno_t rc = 0; - i = list_length(astate->elements); - subdata = (char**)palloc(i * sizeof(char*)); - subbitmaps = (bits8**)palloc(i * sizeof(bits8*)); - subbytes = (int*)palloc(i * sizeof(int)); - subnitems = (int*)palloc(i * sizeof(int)); + i = list_length(astate->elements); + subdata = (char**)palloc(i * sizeof(char*)); + subbitmaps = (bits8**)palloc(i * sizeof(bits8*)); + subbytes = (int*)palloc(i * sizeof(int)); + subnitems = (int*)palloc(i * sizeof(int)); - /* loop through and get data area from each element */ - foreach (element, astate->elements) { - ExprState* e = (ExprState*)lfirst(element); - bool eisnull = false; - Datum arraydatum; - ArrayType* array = NULL; - int this_ndims; + /* loop through and get data area from each element */ + foreach (element, astate->elements) { + ExprState* e = (ExprState*)lfirst(element); + bool eisnull = false; + Datum arraydatum; + ArrayType* array = NULL; + int this_ndims; - arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL); - /* temporarily ignore null subarrays */ - if (eisnull) { - haveempty = true; - continue; - } + arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL); + /* temporarily ignore null subarrays */ + if (eisnull) { + haveempty = true; + continue; + } - array = DatumGetArrayTypeP(arraydatum); + array = DatumGetArrayTypeP(arraydatum); - /* run-time double-check on element type */ - if (element_type != ARR_ELEMTYPE(array)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), + /* run-time double-check on element type */ + if (element_type != ARR_ELEMTYPE(array)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot merge incompatible arrays"), errdetail("Array with element type %s cannot be " "included in ARRAY construct with element type %s.", - format_type_be(ARR_ELEMTYPE(array)), - format_type_be(element_type)))); + format_type_be(ARR_ELEMTYPE(array)), + format_type_be(element_type)))); - this_ndims = ARR_NDIM(array); - /* temporarily ignore zero-dimensional subarrays */ - if (this_ndims <= 0) { - haveempty = true; - continue; - } + this_ndims = ARR_NDIM(array); + /* temporarily ignore zero-dimensional subarrays */ + if (this_ndims <= 0) { + haveempty = true; + continue; + } - if (firstone) { - /* Get sub-array details from first member */ - elem_ndims = this_ndims; - ndims = elem_ndims + 1; - if (ndims <= 0 || ndims > MAXDIM) - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + if (firstone) { + /* Get sub-array details from first member */ + elem_ndims = this_ndims; + ndims = elem_ndims + 1; + if (ndims <= 0 || ndims > MAXDIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of array dimensions (%d) exceeds " "the maximum allowed (%d)", - ndims, - MAXDIM))); + ndims, + MAXDIM))); - elem_dims = (int*)palloc(elem_ndims * sizeof(int)); - rc = memcpy_s(elem_dims, elem_ndims * sizeof(int), ARR_DIMS(array), elem_ndims * sizeof(int)); - securec_check(rc, "\0", "\0"); + elem_dims = (int*)palloc(elem_ndims * sizeof(int)); + rc = memcpy_s(elem_dims, elem_ndims * sizeof(int), ARR_DIMS(array), elem_ndims * sizeof(int)); + securec_check(rc, "\0", "\0"); - elem_lbs = (int*)palloc(elem_ndims * sizeof(int)); - rc = memcpy_s(elem_lbs, elem_ndims * sizeof(int), ARR_LBOUND(array), elem_ndims * sizeof(int)); - securec_check(rc, "\0", "\0"); + elem_lbs = (int*)palloc(elem_ndims * sizeof(int)); + rc = memcpy_s(elem_lbs, elem_ndims * sizeof(int), ARR_LBOUND(array), elem_ndims * sizeof(int)); + securec_check(rc, "\0", "\0"); - firstone = false; - } else { - /* Check other sub-arrays are compatible */ - if (elem_ndims != this_ndims || memcmp(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int)) != 0 || - memcmp(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int)) != 0) - ereport(ERROR, - (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + firstone = false; + } else { + /* Check other sub-arrays are compatible */ + if (elem_ndims != this_ndims || memcmp(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int)) != 0 || + memcmp(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int)) != 0) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("multidimensional arrays must have array expressions with matching dimensions"))); - } + } - subdata[outer_nelems] = ARR_DATA_PTR(array); - subbitmaps[outer_nelems] = ARR_NULLBITMAP(array); - subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array); - nbytes += subbytes[outer_nelems]; - subnitems[outer_nelems] = ArrayGetNItems(this_ndims, ARR_DIMS(array)); - nitems += subnitems[outer_nelems]; - if (ARR_HASNULL(array)) - havenulls = true; - outer_nelems++; - } + subdata[outer_nelems] = ARR_DATA_PTR(array); + subbitmaps[outer_nelems] = ARR_NULLBITMAP(array); + subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array); + nbytes += subbytes[outer_nelems]; + subnitems[outer_nelems] = ArrayGetNItems(this_ndims, ARR_DIMS(array)); + nitems += subnitems[outer_nelems]; + if (ARR_HASNULL(array)) + havenulls = true; + outer_nelems++; + } - /* - * If all items were null or empty arrays, return an empty array; - * otherwise, if some were and some weren't, raise error. (Note: we - * must special-case this somehow to avoid trying to generate a 1-D - * array formed from empty arrays. It's not ideal...) - */ - if (haveempty) { - if (ndims == 0) /* didn't find any nonempty array */ - return PointerGetDatum(construct_empty_array(element_type)); - ereport(ERROR, - (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + /* + * If all items were null or empty arrays, return an empty array; + * otherwise, if some were and some weren't, raise error. (Note: we + * must special-case this somehow to avoid trying to generate a 1-D + * array formed from empty arrays. It's not ideal...) + */ + if (haveempty) { + if (ndims == 0) /* didn't find any nonempty array */ + return PointerGetDatum(construct_empty_array(element_type)); + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("multidimensional arrays must have array expressions with matching dimensions"))); - } + } - /* setup for multi-D array */ - dims[0] = outer_nelems; - lbs[0] = 1; - for (i = 1; i < ndims; i++) { - dims[i] = elem_dims[i - 1]; - lbs[i] = elem_lbs[i - 1]; - } + /* setup for multi-D array */ + dims[0] = outer_nelems; + lbs[0] = 1; + for (i = 1; i < ndims; i++) { + dims[i] = elem_dims[i - 1]; + lbs[i] = elem_lbs[i - 1]; + } - if (havenulls) { - dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems); - nbytes += dataoffset; - } else { - dataoffset = 0; /* marker for no null bitmap */ - nbytes += ARR_OVERHEAD_NONULLS(ndims); - } + if (havenulls) { + dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems); + nbytes += dataoffset; + } else { + dataoffset = 0; /* marker for no null bitmap */ + nbytes += ARR_OVERHEAD_NONULLS(ndims); + } - result = (ArrayType*)palloc(nbytes); - SET_VARSIZE(result, nbytes); - result->ndim = ndims; - result->dataoffset = dataoffset; - result->elemtype = element_type; - rc = memcpy_s(ARR_DIMS(result), ndims * sizeof(int), dims, ndims * sizeof(int)); - securec_check(rc, "\0", "\0"); - rc = memcpy_s(ARR_LBOUND(result), ndims * sizeof(int), lbs, ndims * sizeof(int)); - securec_check(rc, "\0", "\0"); + result = (ArrayType*)palloc(nbytes); + SET_VARSIZE(result, nbytes); + result->ndim = ndims; + result->dataoffset = dataoffset; + result->elemtype = element_type; + rc = memcpy_s(ARR_DIMS(result), ndims * sizeof(int), dims, ndims * sizeof(int)); + securec_check(rc, "\0", "\0"); + rc = memcpy_s(ARR_LBOUND(result), ndims * sizeof(int), lbs, ndims * sizeof(int)); + securec_check(rc, "\0", "\0"); - dat = ARR_DATA_PTR(result); + dat = ARR_DATA_PTR(result); - int len = (nbytes - ARR_DATA_OFFSET(result)); - iitem = 0; - for (i = 0; i < outer_nelems; i++) { - /* make sure the destMax of memcpy_s should never be zero. */ - if (subbytes[i] != 0) { - rc = memcpy_s(dat, len, subdata[i], subbytes[i]); - securec_check(rc, "\0", "\0"); - } + int len = (nbytes - ARR_DATA_OFFSET(result)); + iitem = 0; + for (i = 0; i < outer_nelems; i++) { + /* make sure the destMax of memcpy_s should never be zero. */ + if (subbytes[i] != 0) { + rc = memcpy_s(dat, len, subdata[i], subbytes[i]); + securec_check(rc, "\0", "\0"); + } - dat += subbytes[i]; - len -= subbytes[i]; - if (havenulls) - array_bitmap_copy(ARR_NULLBITMAP(result), iitem, subbitmaps[i], 0, subnitems[i]); - iitem += subnitems[i]; - } - } + dat += subbytes[i]; + len -= subbytes[i]; + if (havenulls) + array_bitmap_copy(ARR_NULLBITMAP(result), iitem, subbitmaps[i], 0, subnitems[i]); + iitem += subnitems[i]; + } + } - return PointerGetDatum(result); + return PointerGetDatum(result); } /* ---------------------------------------------------------------- - * ExecEvalRow - ROW() expressions - * ---------------------------------------------------------------- - */ +* ExecEvalRow - ROW() expressions +* ---------------------------------------------------------------- +*/ static Datum ExecEvalRow(RowExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - HeapTuple tuple; - Datum* values = NULL; - bool* isnull = NULL; - int natts; - ListCell* arg = NULL; - int i; - errno_t rc = EOK; + HeapTuple tuple; + Datum* values = NULL; + bool* isnull = NULL; + int natts; + ListCell* arg = NULL; + int i; + errno_t rc = EOK; - /* Set default values for result flags: non-null, not a set result */ - *isNull = false; - if (isDone != NULL) - *isDone = ExprSingleResult; + /* Set default values for result flags: non-null, not a set result */ + *isNull = false; + if (isDone != NULL) + *isDone = ExprSingleResult; - /* Allocate workspace */ - natts = rstate->tupdesc->natts; - values = (Datum*)palloc0(natts * sizeof(Datum)); - isnull = (bool*)palloc(natts * sizeof(bool)); + /* Allocate workspace */ + natts = rstate->tupdesc->natts; + values = (Datum*)palloc0(natts * sizeof(Datum)); + isnull = (bool*)palloc(natts * sizeof(bool)); - /* preset to nulls in case rowtype has some later-added columns */ - rc = memset_s(isnull, natts * sizeof(bool), true, natts * sizeof(bool)); - securec_check(rc, "\0", "\0"); + /* preset to nulls in case rowtype has some later-added columns */ + rc = memset_s(isnull, natts * sizeof(bool), true, natts * sizeof(bool)); + securec_check(rc, "\0", "\0"); - /* Evaluate field values */ - i = 0; - foreach (arg, rstate->args) { - ExprState* e = (ExprState*)lfirst(arg); + /* Evaluate field values */ + i = 0; + foreach (arg, rstate->args) { + ExprState* e = (ExprState*)lfirst(arg); - values[i] = ExecEvalExpr(e, econtext, &isnull[i], NULL); - i++; - } + values[i] = ExecEvalExpr(e, econtext, &isnull[i], NULL); + i++; + } - tuple = (HeapTuple)tableam_tops_form_tuple(rstate->tupdesc, values, isnull); + tuple = (HeapTuple)tableam_tops_form_tuple(rstate->tupdesc, values, isnull); - pfree_ext(values); - pfree_ext(isnull); + pfree_ext(values); + pfree_ext(isnull); - return HeapTupleGetDatum(tuple); + return HeapTupleGetDatum(tuple); } /* ---------------------------------------------------------------- - * ExecEvalRowCompare - ROW() comparison-op ROW() - * ---------------------------------------------------------------- - */ +* ExecEvalRowCompare - ROW() comparison-op ROW() +* ---------------------------------------------------------------- +*/ static Datum ExecEvalRowCompare(RowCompareExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - bool result = false; - RowCompareType rctype = ((RowCompareExpr*)rstate->xprstate.expr)->rctype; - int32 cmpresult = 0; - ListCell* l = NULL; - ListCell* r = NULL; - int i; + bool result = false; + RowCompareType rctype = ((RowCompareExpr*)rstate->xprstate.expr)->rctype; + int32 cmpresult = 0; + ListCell* l = NULL; + ListCell* r = NULL; + int i; - if (isDone != NULL) - *isDone = ExprSingleResult; - *isNull = true; /* until we get a result */ + if (isDone != NULL) + *isDone = ExprSingleResult; + *isNull = true; /* until we get a result */ - i = 0; - forboth(l, rstate->largs, r, rstate->rargs) - { - ExprState* le = (ExprState*)lfirst(l); - ExprState* re = (ExprState*)lfirst(r); - FunctionCallInfoData locfcinfo; + i = 0; + forboth(l, rstate->largs, r, rstate->rargs) + { + ExprState* le = (ExprState*)lfirst(l); + ExprState* re = (ExprState*)lfirst(r); + FunctionCallInfoData locfcinfo; - InitFunctionCallInfoData(locfcinfo, &(rstate->funcs[i]), 2, rstate->collations[i], NULL, NULL); - locfcinfo.arg[0] = ExecEvalExpr(le, econtext, &locfcinfo.argnull[0], NULL); - locfcinfo.arg[1] = ExecEvalExpr(re, econtext, &locfcinfo.argnull[1], NULL); - if (rstate->funcs[i].fn_strict && (locfcinfo.argnull[0] || locfcinfo.argnull[1])) - return (Datum)0; /* force NULL result */ - locfcinfo.isnull = false; - cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo)); - if (locfcinfo.isnull) - return (Datum)0; /* force NULL result */ - if (cmpresult != 0) - break; /* no need to compare remaining columns */ - i++; - } + InitFunctionCallInfoData(locfcinfo, &(rstate->funcs[i]), 2, rstate->collations[i], NULL, NULL); + locfcinfo.arg[0] = ExecEvalExpr(le, econtext, &locfcinfo.argnull[0], NULL); + locfcinfo.arg[1] = ExecEvalExpr(re, econtext, &locfcinfo.argnull[1], NULL); + if (rstate->funcs[i].fn_strict && (locfcinfo.argnull[0] || locfcinfo.argnull[1])) + return (Datum)0; /* force NULL result */ + locfcinfo.isnull = false; + cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo)); + if (locfcinfo.isnull) + return (Datum)0; /* force NULL result */ + if (cmpresult != 0) + break; /* no need to compare remaining columns */ + i++; + } - switch (rctype) { - /* EQ and NE cases aren't allowed here */ - case ROWCOMPARE_LT: - result = (cmpresult < 0); - break; - case ROWCOMPARE_LE: - result = (cmpresult <= 0); - break; - case ROWCOMPARE_GE: - result = (cmpresult >= 0); - break; - case ROWCOMPARE_GT: - result = (cmpresult > 0); - break; - default: - ereport(ERROR, - (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + switch (rctype) { + /* EQ and NE cases aren't allowed here */ + case ROWCOMPARE_LT: + result = (cmpresult < 0); + break; + case ROWCOMPARE_LE: + result = (cmpresult <= 0); + break; + case ROWCOMPARE_GE: + result = (cmpresult >= 0); + break; + case ROWCOMPARE_GT: + result = (cmpresult > 0); + break; + default: + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized RowCompareType: %d", (int)rctype))); - result = 0; /* keep compiler quiet */ - break; - } + result = 0; /* keep compiler quiet */ + break; + } - *isNull = false; - return BoolGetDatum(result); + *isNull = false; + return BoolGetDatum(result); } /* ---------------------------------------------------------------- - * ExecEvalCoalesce - * ---------------------------------------------------------------- - */ +* ExecEvalCoalesce +* ---------------------------------------------------------------- +*/ static Datum ExecEvalCoalesce( - CoalesceExprState* coalesceExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + CoalesceExprState* coalesceExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - ListCell* arg = NULL; + ListCell* arg = NULL; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; - /* Simply loop through until something NOT NULL is found */ - foreach (arg, coalesceExpr->args) { - ExprState* e = (ExprState*)lfirst(arg); - Datum value; + /* Simply loop through until something NOT NULL is found */ + foreach (arg, coalesceExpr->args) { + ExprState* e = (ExprState*)lfirst(arg); + Datum value; - value = ExecEvalExpr(e, econtext, isNull, NULL); - if (!*isNull) - return value; - } + value = ExecEvalExpr(e, econtext, isNull, NULL); + if (!*isNull) + return value; + } - /* Else return NULL */ - *isNull = true; - return (Datum)0; + /* Else return NULL */ + *isNull = true; + return (Datum)0; } /* ---------------------------------------------------------------- - * ExecEvalMinMax - * ---------------------------------------------------------------- - */ +* ExecEvalMinMax +* ---------------------------------------------------------------- +*/ static Datum ExecEvalMinMax(MinMaxExprState* minmaxExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Datum result = (Datum)0; - MinMaxExpr* minmax = (MinMaxExpr*)minmaxExpr->xprstate.expr; - Oid collation = minmax->inputcollid; - MinMaxOp op = minmax->op; - FunctionCallInfoData locfcinfo; - ListCell* arg = NULL; + Datum result = (Datum)0; + MinMaxExpr* minmax = (MinMaxExpr*)minmaxExpr->xprstate.expr; + Oid collation = minmax->inputcollid; + MinMaxOp op = minmax->op; + FunctionCallInfoData locfcinfo; + ListCell* arg = NULL; - if (isDone != NULL) - *isDone = ExprSingleResult; - *isNull = true; /* until we get a result */ + if (isDone != NULL) + *isDone = ExprSingleResult; + *isNull = true; /* until we get a result */ - InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2, collation, NULL, NULL); - locfcinfo.argnull[0] = false; - locfcinfo.argnull[1] = false; + InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2, collation, NULL, NULL); + locfcinfo.argnull[0] = false; + locfcinfo.argnull[1] = false; - foreach (arg, minmaxExpr->args) { - ExprState* e = (ExprState*)lfirst(arg); - Datum value; - bool valueIsNull = false; - int32 cmpresult; + foreach (arg, minmaxExpr->args) { + ExprState* e = (ExprState*)lfirst(arg); + Datum value; + bool valueIsNull = false; + int32 cmpresult; - value = ExecEvalExpr(e, econtext, &valueIsNull, NULL); - if (valueIsNull) - continue; /* ignore NULL inputs */ + value = ExecEvalExpr(e, econtext, &valueIsNull, NULL); + if (valueIsNull) + continue; /* ignore NULL inputs */ - if (*isNull) { - /* first nonnull input, adopt value */ - result = value; - *isNull = false; - } else { - /* apply comparison function */ - locfcinfo.arg[0] = result; - locfcinfo.arg[1] = value; - locfcinfo.isnull = false; - cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo)); - if (locfcinfo.isnull) /* probably should not happen */ - continue; - if (cmpresult > 0 && op == IS_LEAST) - result = value; - else if (cmpresult < 0 && op == IS_GREATEST) - result = value; - } - } + if (*isNull) { + /* first nonnull input, adopt value */ + result = value; + *isNull = false; + } else { + /* apply comparison function */ + locfcinfo.arg[0] = result; + locfcinfo.arg[1] = value; + locfcinfo.isnull = false; + cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo)); + if (locfcinfo.isnull) /* probably should not happen */ + continue; + if (cmpresult > 0 && op == IS_LEAST) + result = value; + else if (cmpresult < 0 && op == IS_GREATEST) + result = value; + } + } - return result; + return result; } /* ---------------------------------------------------------------- - * ExecEvalXml - * ---------------------------------------------------------------- - */ +* ExecEvalXml +* ---------------------------------------------------------------- +*/ static Datum ExecEvalXml(XmlExprState* xmlExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - XmlExpr* xexpr = (XmlExpr*)xmlExpr->xprstate.expr; - Datum value; - bool isnull = false; - ListCell* arg = NULL; - ListCell* narg = NULL; + XmlExpr* xexpr = (XmlExpr*)xmlExpr->xprstate.expr; + Datum value; + bool isnull = false; + ListCell* arg = NULL; + ListCell* narg = NULL; - if (isDone != NULL) - *isDone = ExprSingleResult; - *isNull = true; /* until we get a result */ + if (isDone != NULL) + *isDone = ExprSingleResult; + *isNull = true; /* until we get a result */ - switch (xexpr->op) { - case IS_XMLCONCAT: { - List* values = NIL; + switch (xexpr->op) { + case IS_XMLCONCAT: { + List* values = NIL; - foreach (arg, xmlExpr->args) { - ExprState* e = (ExprState*)lfirst(arg); + foreach (arg, xmlExpr->args) { + ExprState* e = (ExprState*)lfirst(arg); - value = ExecEvalExpr(e, econtext, &isnull, NULL); - if (!isnull) - values = lappend(values, DatumGetPointer(value)); - } + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (!isnull) + values = lappend(values, DatumGetPointer(value)); + } - if (list_length(values) > 0) { - *isNull = false; - return PointerGetDatum(xmlconcat(values)); - } else - return (Datum)0; - } break; + if (list_length(values) > 0) { + *isNull = false; + return PointerGetDatum(xmlconcat(values)); + } else + return (Datum)0; + } break; - case IS_XMLFOREST: { - StringInfoData buf; + case IS_XMLFOREST: { + StringInfoData buf; - initStringInfo(&buf); - forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names) - { - ExprState* e = (ExprState*)lfirst(arg); - char* argname = strVal(lfirst(narg)); + initStringInfo(&buf); + forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names) + { + ExprState* e = (ExprState*)lfirst(arg); + char* argname = strVal(lfirst(narg)); - value = ExecEvalExpr(e, econtext, &isnull, NULL); - if (!isnull) { - appendStringInfo(&buf, - "<%s>%s", - argname, - map_sql_value_to_xml_value(value, exprType((Node*)e->expr), true), - argname); - *isNull = false; - } - } + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (!isnull) { + appendStringInfo(&buf, + "<%s>%s", + argname, + map_sql_value_to_xml_value(value, exprType((Node*)e->expr), true), + argname); + *isNull = false; + } + } - if (*isNull) { - pfree_ext(buf.data); - return (Datum)0; - } else { - text* result = NULL; + if (*isNull) { + pfree_ext(buf.data); + return (Datum)0; + } else { + text* result = NULL; - result = cstring_to_text_with_len(buf.data, buf.len); - pfree_ext(buf.data); + result = cstring_to_text_with_len(buf.data, buf.len); + pfree_ext(buf.data); - return PointerGetDatum(result); - } - } break; + return PointerGetDatum(result); + } + } break; - case IS_XMLELEMENT: - *isNull = false; - return PointerGetDatum(xmlelement(xmlExpr, econtext)); - break; + case IS_XMLELEMENT: + *isNull = false; + return PointerGetDatum(xmlelement(xmlExpr, econtext)); + break; - case IS_XMLPARSE: { - ExprState* e = NULL; - text* data = NULL; - bool preserve_whitespace = false; + case IS_XMLPARSE: { + ExprState* e = NULL; + text* data = NULL; + bool preserve_whitespace = false; - /* arguments are known to be text, bool */ - Assert(list_length(xmlExpr->args) == 2); + /* arguments are known to be text, bool */ + Assert(list_length(xmlExpr->args) == 2); - e = (ExprState*)linitial(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); - if (isnull) - return (Datum)0; - data = DatumGetTextP(value); + e = (ExprState*)linitial(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + return (Datum)0; + data = DatumGetTextP(value); - e = (ExprState*)lsecond(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); - if (isnull) /* probably can't happen */ - return (Datum)0; - preserve_whitespace = DatumGetBool(value); + e = (ExprState*)lsecond(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) /* probably can't happen */ + return (Datum)0; + preserve_whitespace = DatumGetBool(value); - *isNull = false; + *isNull = false; - return PointerGetDatum(xmlparse(data, xexpr->xmloption, preserve_whitespace)); - } break; + return PointerGetDatum(xmlparse(data, xexpr->xmloption, preserve_whitespace)); + } break; - case IS_XMLPI: { - ExprState* e = NULL; - text* argument = NULL; + case IS_XMLPI: { + ExprState* e = NULL; + text* argument = NULL; - /* optional argument is known to be text */ - Assert(list_length(xmlExpr->args) <= 1); + /* optional argument is known to be text */ + Assert(list_length(xmlExpr->args) <= 1); - if (xmlExpr->args) { - e = (ExprState*)linitial(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); - if (isnull) - argument = NULL; - else - argument = DatumGetTextP(value); - } else { - argument = NULL; - isnull = false; - } + if (xmlExpr->args) { + e = (ExprState*)linitial(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + argument = NULL; + else + argument = DatumGetTextP(value); + } else { + argument = NULL; + isnull = false; + } - return PointerGetDatum(xmlpi(xexpr->name, argument, isnull, isNull)); - } break; + return PointerGetDatum(xmlpi(xexpr->name, argument, isnull, isNull)); + } break; - case IS_XMLROOT: { - ExprState* e = NULL; - xmltype* data = NULL; - text* version = NULL; - int standalone; + case IS_XMLROOT: { + ExprState* e = NULL; + xmltype* data = NULL; + text* version = NULL; + int standalone; - /* arguments are known to be xml, text, int */ - Assert(list_length(xmlExpr->args) == 3); + /* arguments are known to be xml, text, int */ + Assert(list_length(xmlExpr->args) == 3); - e = (ExprState*)linitial(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); - if (isnull) - return (Datum)0; - data = DatumGetXmlP(value); + e = (ExprState*)linitial(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + return (Datum)0; + data = DatumGetXmlP(value); - e = (ExprState*)lsecond(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); - if (isnull) - version = NULL; - else - version = DatumGetTextP(value); + e = (ExprState*)lsecond(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + version = NULL; + else + version = DatumGetTextP(value); - e = (ExprState*)lthird(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); - standalone = DatumGetInt32(value); + e = (ExprState*)lthird(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + standalone = DatumGetInt32(value); - *isNull = false; + *isNull = false; - return PointerGetDatum(xmlroot(data, version, standalone)); - } break; + return PointerGetDatum(xmlroot(data, version, standalone)); + } break; - case IS_XMLSERIALIZE: { - ExprState* e = NULL; + case IS_XMLSERIALIZE: { + ExprState* e = NULL; - /* argument type is known to be xml */ - Assert(list_length(xmlExpr->args) == 1); + /* argument type is known to be xml */ + Assert(list_length(xmlExpr->args) == 1); - e = (ExprState*)linitial(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); - if (isnull) - return (Datum)0; + e = (ExprState*)linitial(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + return (Datum)0; - *isNull = false; + *isNull = false; - return PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), xexpr->xmloption)); - } break; + return PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), xexpr->xmloption)); + } break; - case IS_DOCUMENT: { - ExprState* e = NULL; + case IS_DOCUMENT: { + ExprState* e = NULL; - /* optional argument is known to be xml */ - Assert(list_length(xmlExpr->args) == 1); + /* optional argument is known to be xml */ + Assert(list_length(xmlExpr->args) == 1); - e = (ExprState*)linitial(xmlExpr->args); - value = ExecEvalExpr(e, econtext, &isnull, NULL); - if (isnull) - return (Datum)0; - else { - *isNull = false; - return BoolGetDatum(xml_is_document(DatumGetXmlP(value))); - } - } break; - default: - break; - } + e = (ExprState*)linitial(xmlExpr->args); + value = ExecEvalExpr(e, econtext, &isnull, NULL); + if (isnull) + return (Datum)0; + else { + *isNull = false; + return BoolGetDatum(xml_is_document(DatumGetXmlP(value))); + } + } break; + default: + break; + } - ereport(ERROR, - (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized XML operation %d", xexpr->op))); - return (Datum)0; + return (Datum)0; } /* ---------------------------------------------------------------- - * ExecEvalNullIf - * - * Note that this is *always* derived from the equals operator, - * but since we need special processing of the arguments - * we can not simply reuse ExecEvalOper() or ExecEvalFunc(). - * ---------------------------------------------------------------- - */ +* ExecEvalNullIf +* +* Note that this is *always* derived from the equals operator, +* but since we need special processing of the arguments +* we can not simply reuse ExecEvalOper() or ExecEvalFunc(). +* ---------------------------------------------------------------- +*/ static Datum ExecEvalNullIf(FuncExprState* nullIfExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Datum result; - FunctionCallInfo fcinfo; - ExprDoneCond argDone; + Datum result; + FunctionCallInfo fcinfo; + ExprDoneCond argDone; - if (isDone != NULL) - *isDone = ExprSingleResult; + if (isDone != NULL) + *isDone = ExprSingleResult; /* * Initialize function cache if first time through @@ -4701,690 +4713,690 @@ static Datum ExecEvalNullIf(FuncExprState* nullIfExpr, ExprContext* econtext, bo } } - /* - * Evaluate arguments - */ - fcinfo = &nullIfExpr->fcinfo_data; - argDone = ExecEvalFuncArgs(fcinfo, nullIfExpr->args, econtext); - if (argDone != ExprSingleResult) - ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("NULLIF does not support set arguments"))); - Assert(fcinfo->nargs == 2); + /* + * Evaluate arguments + */ + fcinfo = &nullIfExpr->fcinfo_data; + argDone = ExecEvalFuncArgs(fcinfo, nullIfExpr->args, econtext); + if (argDone != ExprSingleResult) + ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("NULLIF does not support set arguments"))); + Assert(fcinfo->nargs == 2); - /* if either argument is NULL they can't be equal */ - if (!fcinfo->argnull[0] && !fcinfo->argnull[1]) { - fcinfo->isnull = false; - result = FunctionCallInvoke(fcinfo); - /* if the arguments are equal return null */ - if (!fcinfo->isnull && DatumGetBool(result)) { - *isNull = true; - return (Datum)0; - } - } + /* if either argument is NULL they can't be equal */ + if (!fcinfo->argnull[0] && !fcinfo->argnull[1]) { + fcinfo->isnull = false; + result = FunctionCallInvoke(fcinfo); + /* if the arguments are equal return null */ + if (!fcinfo->isnull && DatumGetBool(result)) { + *isNull = true; + return (Datum)0; + } + } - /* else return first argument */ - *isNull = fcinfo->argnull[0]; - return fcinfo->arg[0]; + /* else return first argument */ + *isNull = fcinfo->argnull[0]; + return fcinfo->arg[0]; } static Datum CheckRowTypeIsNull(TupleDesc tupDesc, HeapTupleData tmptup, NullTest *ntest) { - int att; + int att; - for (att = 1; att <= tupDesc->natts; att++) { - /* ignore dropped columns */ - if (tupDesc->attrs[att - 1].attisdropped) - continue; - if (tableam_tops_tuple_attisnull(&tmptup, att, tupDesc)) { - /* null field disproves IS NOT NULL */ - if (ntest->nulltesttype == IS_NOT_NULL) - return BoolGetDatum(false); - } else { - /* non-null field disproves IS NULL */ - if (ntest->nulltesttype == IS_NULL) - return BoolGetDatum(false); - } - } + for (att = 1; att <= tupDesc->natts; att++) { + /* ignore dropped columns */ + if (tupDesc->attrs[att - 1].attisdropped) + continue; + if (tableam_tops_tuple_attisnull(&tmptup, att, tupDesc)) { + /* null field disproves IS NOT NULL */ + if (ntest->nulltesttype == IS_NOT_NULL) + return BoolGetDatum(false); + } else { + /* non-null field disproves IS NULL */ + if (ntest->nulltesttype == IS_NULL) + return BoolGetDatum(false); + } + } - return BoolGetDatum(true); + return BoolGetDatum(true); } static Datum CheckRowTypeIsNullForAFormat(TupleDesc tupDesc, HeapTupleData tmptup, NullTest *ntest) { - int att; + int att; - for (att = 1; att <= tupDesc->natts; att++) { - /* ignore dropped columns */ - if (tupDesc->attrs[att - 1].attisdropped) - continue; - if (!tableam_tops_tuple_attisnull(&tmptup, att, tupDesc)) { - /* non-null field disproves IS NULL */ - if (ntest->nulltesttype == IS_NULL) { - return BoolGetDatum(false); - } else { - return BoolGetDatum(true); - } - } - } + for (att = 1; att <= tupDesc->natts; att++) { + /* ignore dropped columns */ + if (tupDesc->attrs[att - 1].attisdropped) + continue; + if (!tableam_tops_tuple_attisnull(&tmptup, att, tupDesc)) { + /* non-null field disproves IS NULL */ + if (ntest->nulltesttype == IS_NULL) { + return BoolGetDatum(false); + } else { + return BoolGetDatum(true); + } + } + } - /* non-null field disproves IS NULL */ - if (ntest->nulltesttype == IS_NULL) { - return BoolGetDatum(true); - } else { - return BoolGetDatum(false); - } + /* non-null field disproves IS NULL */ + if (ntest->nulltesttype == IS_NULL) { + return BoolGetDatum(true); + } else { + return BoolGetDatum(false); + } } /* ---------------------------------------------------------------- - * ExecEvalNullTest - * - * Evaluate a NullTest node. - * ---------------------------------------------------------------- - */ +* ExecEvalNullTest +* +* Evaluate a NullTest node. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalNullTest(NullTestState* nstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - NullTest* ntest = (NullTest*)nstate->xprstate.expr; - Datum result; + NullTest* ntest = (NullTest*)nstate->xprstate.expr; + Datum result; - result = ExecEvalExpr(nstate->arg, econtext, isNull, isDone); + result = ExecEvalExpr(nstate->arg, econtext, isNull, isDone); - if (isDone && *isDone == ExprEndResult) - return result; /* nothing to check */ + if (isDone && *isDone == ExprEndResult) + return result; /* nothing to check */ - if (ntest->argisrow && !(*isNull)) { - /* - * The SQL standard defines IS [NOT] NULL for a non-null rowtype - * argument as: - * - * "R IS NULL" is true if every field is the null value. - * - * "R IS NOT NULL" is true if no field is the null value. - * - * This definition is (apparently intentionally) not recursive; so our - * tests on the fields are primitive attisnull tests, not recursive - * checks to see if they are all-nulls or no-nulls rowtypes. - * - * The standard does not consider the possibility of zero-field rows, - * but here we consider them to vacuously satisfy both predicates. - * - * e.g. - * r | isnull | isnotnull - * -------------+--------+----------- - * (1,"(1,2)") | f | t - * (1,"(,)") | f | t - * (1,) | f | f - * (,"(1,2)") | f | f - * (,"(,)") | f | f - * (,) | t | f - * - */ - HeapTupleHeader tuple; - Oid tupType; - int32 tupTypmod; - TupleDesc tupDesc; - HeapTupleData tmptup; + if (ntest->argisrow && !(*isNull)) { + /* + * The SQL standard defines IS [NOT] NULL for a non-null rowtype + * argument as: + * + * "R IS NULL" is true if every field is the null value. + * + * "R IS NOT NULL" is true if no field is the null value. + * + * This definition is (apparently intentionally) not recursive; so our + * tests on the fields are primitive attisnull tests, not recursive + * checks to see if they are all-nulls or no-nulls rowtypes. + * + * The standard does not consider the possibility of zero-field rows, + * but here we consider them to vacuously satisfy both predicates. + * + * e.g. + * r | isnull | isnotnull + * -------------+--------+----------- + * (1,"(1,2)") | f | t + * (1,"(,)") | f | t + * (1,) | f | f + * (,"(1,2)") | f | f + * (,"(,)") | f | f + * (,) | t | f + * + */ + HeapTupleHeader tuple; + Oid tupType; + int32 tupTypmod; + TupleDesc tupDesc; + HeapTupleData tmptup; - tuple = DatumGetHeapTupleHeader(result); + tuple = DatumGetHeapTupleHeader(result); - tupType = HeapTupleHeaderGetTypeId(tuple); - tupTypmod = HeapTupleHeaderGetTypMod(tuple); + tupType = HeapTupleHeaderGetTypeId(tuple); + tupTypmod = HeapTupleHeaderGetTypMod(tuple); - /* Lookup tupdesc if first time through or if type changes */ - tupDesc = get_cached_rowtype(tupType, tupTypmod, &nstate->argdesc, econtext); + /* Lookup tupdesc if first time through or if type changes */ + tupDesc = get_cached_rowtype(tupType, tupTypmod, &nstate->argdesc, econtext); - /* - * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader. - */ - tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); - tmptup.t_data = tuple; + /* + * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader. + */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); + tmptup.t_data = tuple; - if (AFORMAT_NULL_TEST_MODE) { - return CheckRowTypeIsNullForAFormat(tupDesc, tmptup, ntest); - } else { - return CheckRowTypeIsNull(tupDesc, tmptup, ntest); - } - } else { - /* Simple scalar-argument case, or a null rowtype datum */ - switch (ntest->nulltesttype) { - case IS_NULL: - if (*isNull) { - *isNull = false; - return BoolGetDatum(true); - } else - return BoolGetDatum(false); - case IS_NOT_NULL: - if (*isNull) { - *isNull = false; - return BoolGetDatum(false); - } else - return BoolGetDatum(true); - default: - ereport(ERROR, - (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + if (AFORMAT_NULL_TEST_MODE) { + return CheckRowTypeIsNullForAFormat(tupDesc, tmptup, ntest); + } else { + return CheckRowTypeIsNull(tupDesc, tmptup, ntest); + } + } else { + /* Simple scalar-argument case, or a null rowtype datum */ + switch (ntest->nulltesttype) { + case IS_NULL: + if (*isNull) { + *isNull = false; + return BoolGetDatum(true); + } else + return BoolGetDatum(false); + case IS_NOT_NULL: + if (*isNull) { + *isNull = false; + return BoolGetDatum(false); + } else + return BoolGetDatum(true); + default: + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized nulltesttype: %d", (int)ntest->nulltesttype))); - return (Datum)0; /* keep compiler quiet */ - } - } + return (Datum)0; /* keep compiler quiet */ + } + } } /* ---------------------------------------------------------------- - * ExecEvalHashFilter - * - * Evaluate a HashFilter node. - * ---------------------------------------------------------------- - */ +* ExecEvalHashFilter +* +* Evaluate a HashFilter node. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalHashFilter(HashFilterState* hstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - HashFilter* htest = (HashFilter*)hstate->xprstate.expr; - Datum result = 0; - Datum value = 0; - uint64 hashValue = 0; - int modulo = 0; - int nodeIndex = 0; - ListCell *distkey = NULL; - ListCell *vartypes = NULL; - bool isFirst = true; - bool hasNonNullValue = false; + HashFilter* htest = (HashFilter*)hstate->xprstate.expr; + Datum result = 0; + Datum value = 0; + uint64 hashValue = 0; + int modulo = 0; + int nodeIndex = 0; + ListCell *distkey = NULL; + ListCell *vartypes = NULL; + bool isFirst = true; + bool hasNonNullValue = false; - if (isDone != NULL) - *isDone = ExprSingleResult; - *isNull = true; /* until we get a result */ + if (isDone != NULL) + *isDone = ExprSingleResult; + *isNull = true; /* until we get a result */ - /* Get every distribute key in arg and compute hash value */ - forboth(distkey, hstate->arg, vartypes, htest->typeOids) - { - ExprState* e = (ExprState*)lfirst(distkey); - Oid vartype = (Oid)lfirst_oid(vartypes); - value = ExecEvalExpr(e, econtext, isNull, isDone); + /* Get every distribute key in arg and compute hash value */ + forboth(distkey, hstate->arg, vartypes, htest->typeOids) + { + ExprState* e = (ExprState*)lfirst(distkey); + Oid vartype = (Oid)lfirst_oid(vartypes); + value = ExecEvalExpr(e, econtext, isNull, isDone); - int null_value_dn_index = (hstate->nodelist != NULL) ? hstate->nodelist[0] - : /* fetch first dn in group's dn list */ - 0; /* fetch first dn index */ + int null_value_dn_index = (hstate->nodelist != NULL) ? hstate->nodelist[0] + : /* fetch first dn in group's dn list */ + 0; /* fetch first dn index */ - if (*isNull) { - if (null_value_dn_index == u_sess->pgxc_cxt.PGXCNodeId) { - *isNull = false; - result = BoolGetDatum(true); - } else - result = BoolGetDatum(false); - } else { - if (isFirst) { - hashValue = compute_hash(vartype, value, LOCATOR_TYPE_HASH); - isFirst = false; - } else { - hashValue = (hashValue << 1) | ((hashValue & 0x80000000) ? 1 : 0); - hashValue ^= compute_hash(vartype, value, LOCATOR_TYPE_HASH); - } + if (*isNull) { + if (null_value_dn_index == u_sess->pgxc_cxt.PGXCNodeId) { + *isNull = false; + result = BoolGetDatum(true); + } else + result = BoolGetDatum(false); + } else { + if (isFirst) { + hashValue = compute_hash(vartype, value, LOCATOR_TYPE_HASH); + isFirst = false; + } else { + hashValue = (hashValue << 1) | ((hashValue & 0x80000000) ? 1 : 0); + hashValue ^= compute_hash(vartype, value, LOCATOR_TYPE_HASH); + } - hasNonNullValue = true; - } - } + hasNonNullValue = true; + } + } - /* If has non null value, it should get nodeId and deside if need filter the value or not. */ - if (hasNonNullValue) { - modulo = hstate->bucketMap[abs((int)hashValue) & (hstate->bucketCnt - 1)]; - nodeIndex = hstate->nodelist[modulo]; + /* If has non null value, it should get nodeId and deside if need filter the value or not. */ + if (hasNonNullValue) { + modulo = hstate->bucketMap[abs((int)hashValue) & (hstate->bucketCnt - 1)]; + nodeIndex = hstate->nodelist[modulo]; - /* If there are null value and non null value, and the last value in distkey is null, - we should set isNull is false. */ - *isNull = false; - /* Look into the handles and return correct position in array */ - if (nodeIndex == u_sess->pgxc_cxt.PGXCNodeId) - return BoolGetDatum(true); - else - return BoolGetDatum(false); - } else /* If all the value is null, return result. */ - return result; + /* If there are null value and non null value, and the last value in distkey is null, + we should set isNull is false. */ + *isNull = false; + /* Look into the handles and return correct position in array */ + if (nodeIndex == u_sess->pgxc_cxt.PGXCNodeId) + return BoolGetDatum(true); + else + return BoolGetDatum(false); + } else /* If all the value is null, return result. */ + return result; } /* ---------------------------------------------------------------- - * ExecEvalBooleanTest - * - * Evaluate a BooleanTest node. - * ---------------------------------------------------------------- - */ +* ExecEvalBooleanTest +* +* Evaluate a BooleanTest node. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalBooleanTest(GenericExprState* bstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - BooleanTest* btest = (BooleanTest*)bstate->xprstate.expr; - Datum result; + BooleanTest* btest = (BooleanTest*)bstate->xprstate.expr; + Datum result; - result = ExecEvalExpr(bstate->arg, econtext, isNull, isDone); + result = ExecEvalExpr(bstate->arg, econtext, isNull, isDone); - if (isDone && *isDone == ExprEndResult) - return result; /* nothing to check */ + if (isDone && *isDone == ExprEndResult) + return result; /* nothing to check */ - switch (btest->booltesttype) { - case IS_TRUE: - if (*isNull) { - *isNull = false; - return BoolGetDatum(false); - } else if (DatumGetBool(result)) - return BoolGetDatum(true); - else - return BoolGetDatum(false); - case IS_NOT_TRUE: - if (*isNull) { - *isNull = false; - return BoolGetDatum(true); - } else if (DatumGetBool(result)) - return BoolGetDatum(false); - else - return BoolGetDatum(true); - case IS_FALSE: - if (*isNull) { - *isNull = false; - return BoolGetDatum(false); - } else if (DatumGetBool(result)) - return BoolGetDatum(false); - else - return BoolGetDatum(true); - case IS_NOT_FALSE: - if (*isNull) { - *isNull = false; - return BoolGetDatum(true); - } else if (DatumGetBool(result)) - return BoolGetDatum(true); - else - return BoolGetDatum(false); - case IS_UNKNOWN: - if (*isNull) { - *isNull = false; - return BoolGetDatum(true); - } else - return BoolGetDatum(false); - case IS_NOT_UNKNOWN: - if (*isNull) { - *isNull = false; - return BoolGetDatum(false); - } else - return BoolGetDatum(true); - default: - ereport(ERROR, - (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + switch (btest->booltesttype) { + case IS_TRUE: + if (*isNull) { + *isNull = false; + return BoolGetDatum(false); + } else if (DatumGetBool(result)) + return BoolGetDatum(true); + else + return BoolGetDatum(false); + case IS_NOT_TRUE: + if (*isNull) { + *isNull = false; + return BoolGetDatum(true); + } else if (DatumGetBool(result)) + return BoolGetDatum(false); + else + return BoolGetDatum(true); + case IS_FALSE: + if (*isNull) { + *isNull = false; + return BoolGetDatum(false); + } else if (DatumGetBool(result)) + return BoolGetDatum(false); + else + return BoolGetDatum(true); + case IS_NOT_FALSE: + if (*isNull) { + *isNull = false; + return BoolGetDatum(true); + } else if (DatumGetBool(result)) + return BoolGetDatum(true); + else + return BoolGetDatum(false); + case IS_UNKNOWN: + if (*isNull) { + *isNull = false; + return BoolGetDatum(true); + } else + return BoolGetDatum(false); + case IS_NOT_UNKNOWN: + if (*isNull) { + *isNull = false; + return BoolGetDatum(false); + } else + return BoolGetDatum(true); + default: + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized booltesttype: %d", (int)btest->booltesttype))); - return (Datum)0; /* keep compiler quiet */ - } + return (Datum)0; /* keep compiler quiet */ + } } /* - * ExecEvalCoerceToDomain - * - * Test the provided data against the domain constraint(s). If the data - * passes the constraint specifications, pass it through (return the - * datum) otherwise throw an error. - */ +* ExecEvalCoerceToDomain +* +* Test the provided data against the domain constraint(s). If the data +* passes the constraint specifications, pass it through (return the +* datum) otherwise throw an error. +*/ static Datum ExecEvalCoerceToDomain( - CoerceToDomainState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + CoerceToDomainState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - CoerceToDomain* ctest = (CoerceToDomain*)cstate->xprstate.expr; - Datum result; - ListCell* l = NULL; + CoerceToDomain* ctest = (CoerceToDomain*)cstate->xprstate.expr; + Datum result; + ListCell* l = NULL; - result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone); + result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone); - if (isDone && *isDone == ExprEndResult) - return result; /* nothing to check */ + if (isDone && *isDone == ExprEndResult) + return result; /* nothing to check */ - foreach (l, cstate->constraints) { - DomainConstraintState* con = (DomainConstraintState*)lfirst(l); + foreach (l, cstate->constraints) { + DomainConstraintState* con = (DomainConstraintState*)lfirst(l); - switch (con->constrainttype) { - case DOM_CONSTRAINT_NOTNULL: - if (*isNull) - ereport(ERROR, - (errcode(ERRCODE_NOT_NULL_VIOLATION), + switch (con->constrainttype) { + case DOM_CONSTRAINT_NOTNULL: + if (*isNull) + ereport(ERROR, + (errcode(ERRCODE_NOT_NULL_VIOLATION), errmsg("domain %s does not allow null values", format_type_be(ctest->resulttype)))); - break; - case DOM_CONSTRAINT_CHECK: { - Datum conResult; - bool conIsNull = false; - Datum save_datum; - bool save_isNull = false; + break; + case DOM_CONSTRAINT_CHECK: { + Datum conResult; + bool conIsNull = false; + Datum save_datum; + bool save_isNull = false; - /* - * Set up value to be returned by CoerceToDomainValue - * nodes. We must save and restore prior setting of - * econtext's domainValue fields, in case this node is - * itself within a check expression for another domain. - */ - save_datum = econtext->domainValue_datum; - save_isNull = econtext->domainValue_isNull; + /* + * Set up value to be returned by CoerceToDomainValue + * nodes. We must save and restore prior setting of + * econtext's domainValue fields, in case this node is + * itself within a check expression for another domain. + */ + save_datum = econtext->domainValue_datum; + save_isNull = econtext->domainValue_isNull; - econtext->domainValue_datum = result; - econtext->domainValue_isNull = *isNull; + econtext->domainValue_datum = result; + econtext->domainValue_isNull = *isNull; - conResult = ExecEvalExpr(con->check_expr, econtext, &conIsNull, NULL); + conResult = ExecEvalExpr(con->check_expr, econtext, &conIsNull, NULL); - if (!conIsNull && !DatumGetBool(conResult)) - ereport(ERROR, - (errcode(ERRCODE_CHECK_VIOLATION), + if (!conIsNull && !DatumGetBool(conResult)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), errmsg("value for domain %s violates check constraint \"%s\"", - format_type_be(ctest->resulttype), - con->name))); - econtext->domainValue_datum = save_datum; - econtext->domainValue_isNull = save_isNull; + format_type_be(ctest->resulttype), + con->name))); + econtext->domainValue_datum = save_datum; + econtext->domainValue_isNull = save_isNull; - break; - } - default: - ereport(ERROR, - (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + break; + } + default: + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized constraint type: %d", (int)con->constrainttype))); - break; - } - } + break; + } + } - /* If all has gone well (constraints did not fail) return the datum */ - return result; + /* If all has gone well (constraints did not fail) return the datum */ + return result; } /* - * ExecEvalCoerceToDomainValue - * - * Return the value stored by CoerceToDomain. - */ +* ExecEvalCoerceToDomainValue +* +* Return the value stored by CoerceToDomain. +*/ static Datum ExecEvalCoerceToDomainValue( - ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - if (isDone != NULL) - *isDone = ExprSingleResult; - *isNull = econtext->domainValue_isNull; - return econtext->domainValue_datum; + if (isDone != NULL) + *isDone = ExprSingleResult; + *isNull = econtext->domainValue_isNull; + return econtext->domainValue_datum; } /* ---------------------------------------------------------------- - * ExecEvalFieldSelect - * - * Evaluate a FieldSelect node. - * ---------------------------------------------------------------- - */ +* ExecEvalFieldSelect +* +* Evaluate a FieldSelect node. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalFieldSelect(FieldSelectState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - FieldSelect* fselect = (FieldSelect*)fstate->xprstate.expr; - AttrNumber fieldnum = fselect->fieldnum; - Datum result; - Datum tupDatum; - HeapTupleHeader tuple; - Oid tupType; - int32 tupTypmod; - TupleDesc tupDesc; - Form_pg_attribute attr; - HeapTupleData tmptup; + FieldSelect* fselect = (FieldSelect*)fstate->xprstate.expr; + AttrNumber fieldnum = fselect->fieldnum; + Datum result; + Datum tupDatum; + HeapTupleHeader tuple; + Oid tupType; + int32 tupTypmod; + TupleDesc tupDesc; + Form_pg_attribute attr; + HeapTupleData tmptup; - tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); + tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); - /* this test covers the isDone exception too: */ - if (*isNull) - return tupDatum; + /* this test covers the isDone exception too: */ + if (*isNull) + return tupDatum; - tuple = DatumGetHeapTupleHeader(tupDatum); + tuple = DatumGetHeapTupleHeader(tupDatum); - tupType = HeapTupleHeaderGetTypeId(tuple); - tupTypmod = HeapTupleHeaderGetTypMod(tuple); + tupType = HeapTupleHeaderGetTypeId(tuple); + tupTypmod = HeapTupleHeaderGetTypMod(tuple); - /* Lookup tupdesc if first time through or if type changes */ - tupDesc = get_cached_rowtype(tupType, tupTypmod, &fstate->argdesc, econtext); + /* Lookup tupdesc if first time through or if type changes */ + tupDesc = get_cached_rowtype(tupType, tupTypmod, &fstate->argdesc, econtext); - /* - * Find field's attr record. Note we don't support system columns here: a - * datum tuple doesn't have valid values for most of the interesting - * system columns anyway. - */ - if (fieldnum <= 0) /* should never happen */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + /* + * Find field's attr record. Note we don't support system columns here: a + * datum tuple doesn't have valid values for most of the interesting + * system columns anyway. + */ + if (fieldnum <= 0) /* should never happen */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmodule(MOD_EXECUTOR), errmsg("unsupported reference to system column %d in FieldSelect", fieldnum))); - if (fieldnum > tupDesc->natts) /* should never happen */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_ATTRIBUTE), + if (fieldnum > tupDesc->natts) /* should never happen */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_ATTRIBUTE), errmodule(MOD_EXECUTOR), errmsg("attribute number %d exceeds number of columns %d", fieldnum, tupDesc->natts))); - attr = &tupDesc->attrs[fieldnum - 1]; + attr = &tupDesc->attrs[fieldnum - 1]; - /* Check for dropped column, and force a NULL result if so */ - if (attr->attisdropped) { - *isNull = true; - return (Datum)0; - } + /* Check for dropped column, and force a NULL result if so */ + if (attr->attisdropped) { + *isNull = true; + return (Datum)0; + } - /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */ - /* As in ExecEvalScalarVar, we should but can't check typmod */ - if (fselect->resulttype != attr->atttypid) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), + /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */ + /* As in ExecEvalScalarVar, we should but can't check typmod */ + if (fselect->resulttype != attr->atttypid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("attribute %d has wrong type", fieldnum), errdetail("Table has type %s, but query expects %s.", - format_type_be(attr->atttypid), - format_type_be(fselect->resulttype)))); + format_type_be(attr->atttypid), + format_type_be(fselect->resulttype)))); - /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */ - tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); - tmptup.t_data = tuple; + /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); + tmptup.t_data = tuple; - result = tableam_tops_tuple_getattr(&tmptup, fieldnum, tupDesc, isNull); - return result; + result = tableam_tops_tuple_getattr(&tmptup, fieldnum, tupDesc, isNull); + return result; } /* ---------------------------------------------------------------- - * ExecEvalFieldStore - * - * Evaluate a FieldStore node. - * ---------------------------------------------------------------- - */ +* ExecEvalFieldStore +* +* Evaluate a FieldStore node. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalFieldStore(FieldStoreState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - FieldStore* fstore = (FieldStore*)fstate->xprstate.expr; - HeapTuple tuple; - Datum tupDatum; - TupleDesc tupDesc; - Datum* values = NULL; - bool* isnull = NULL; - Datum save_datum; - bool save_isNull = false; - ListCell* l1 = NULL; - ListCell* l2 = NULL; - errno_t rc = EOK; + FieldStore* fstore = (FieldStore*)fstate->xprstate.expr; + HeapTuple tuple; + Datum tupDatum; + TupleDesc tupDesc; + Datum* values = NULL; + bool* isnull = NULL; + Datum save_datum; + bool save_isNull = false; + ListCell* l1 = NULL; + ListCell* l2 = NULL; + errno_t rc = EOK; - tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); + tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); - if (isDone != NULL && *isDone == ExprEndResult) - return tupDatum; + if (isDone != NULL && *isDone == ExprEndResult) + return tupDatum; - /* Lookup tupdesc if first time through or after rescan */ - tupDesc = get_cached_rowtype(fstore->resulttype, -1, &fstate->argdesc, econtext); + /* Lookup tupdesc if first time through or after rescan */ + tupDesc = get_cached_rowtype(fstore->resulttype, -1, &fstate->argdesc, econtext); - /* Allocate workspace */ - values = (Datum*)palloc(tupDesc->natts * sizeof(Datum)); - isnull = (bool*)palloc(tupDesc->natts * sizeof(bool)); + /* Allocate workspace */ + values = (Datum*)palloc(tupDesc->natts * sizeof(Datum)); + isnull = (bool*)palloc(tupDesc->natts * sizeof(bool)); - if (!*isNull) { - /* - * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We - * set all the fields in the struct just in case. - */ - HeapTupleHeader tuphdr; - HeapTupleData tmptup; + if (!*isNull) { + /* + * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We + * set all the fields in the struct just in case. + */ + HeapTupleHeader tuphdr; + HeapTupleData tmptup; - tuphdr = DatumGetHeapTupleHeader(tupDatum); - tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr); - ItemPointerSetInvalid(&(tmptup.t_self)); - tmptup.t_tableOid = InvalidOid; - tmptup.t_bucketId = InvalidBktId; + tuphdr = DatumGetHeapTupleHeader(tupDatum); + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr); + ItemPointerSetInvalid(&(tmptup.t_self)); + tmptup.t_tableOid = InvalidOid; + tmptup.t_bucketId = InvalidBktId; #ifdef PGXC - tmptup.t_xc_node_id = 0; + tmptup.t_xc_node_id = 0; #endif - HeapTupleSetZeroBase(&tmptup); - tmptup.t_data = tuphdr; + HeapTupleSetZeroBase(&tmptup); + tmptup.t_data = tuphdr; - tableam_tops_deform_tuple(&tmptup, tupDesc, values, isnull); - } else { - /* Convert null input tuple into an all-nulls row */ - rc = memset_s(isnull, tupDesc->natts * sizeof(bool), true, tupDesc->natts * sizeof(bool)); - securec_check(rc, "\0", "\0"); - } + tableam_tops_deform_tuple(&tmptup, tupDesc, values, isnull); + } else { + /* Convert null input tuple into an all-nulls row */ + rc = memset_s(isnull, tupDesc->natts * sizeof(bool), true, tupDesc->natts * sizeof(bool)); + securec_check(rc, "\0", "\0"); + } - /* Result is never null */ - *isNull = false; + /* Result is never null */ + *isNull = false; - save_datum = econtext->caseValue_datum; - save_isNull = econtext->caseValue_isNull; + save_datum = econtext->caseValue_datum; + save_isNull = econtext->caseValue_isNull; - forboth(l1, fstate->newvals, l2, fstore->fieldnums) - { - ExprState* newval = (ExprState*)lfirst(l1); - AttrNumber fieldnum = lfirst_int(l2); + forboth(l1, fstate->newvals, l2, fstore->fieldnums) + { + ExprState* newval = (ExprState*)lfirst(l1); + AttrNumber fieldnum = lfirst_int(l2); - Assert(fieldnum > 0 && fieldnum <= tupDesc->natts); + Assert(fieldnum > 0 && fieldnum <= tupDesc->natts); - /* - * Use the CaseTestExpr mechanism to pass down the old value of the - * field being replaced; this is needed in case the newval is itself a - * FieldStore or ArrayRef that has to obtain and modify the old value. - * It's safe to reuse the CASE mechanism because there cannot be a - * CASE between here and where the value would be needed, and a field - * assignment can't be within a CASE either. (So saving and restoring - * the caseValue is just paranoia, but let's do it anyway.) - */ - econtext->caseValue_datum = values[fieldnum - 1]; - econtext->caseValue_isNull = isnull[fieldnum - 1]; + /* + * Use the CaseTestExpr mechanism to pass down the old value of the + * field being replaced; this is needed in case the newval is itself a + * FieldStore or ArrayRef that has to obtain and modify the old value. + * It's safe to reuse the CASE mechanism because there cannot be a + * CASE between here and where the value would be needed, and a field + * assignment can't be within a CASE either. (So saving and restoring + * the caseValue is just paranoia, but let's do it anyway.) + */ + econtext->caseValue_datum = values[fieldnum - 1]; + econtext->caseValue_isNull = isnull[fieldnum - 1]; - values[fieldnum - 1] = ExecEvalExpr(newval, econtext, &isnull[fieldnum - 1], NULL); - } + values[fieldnum - 1] = ExecEvalExpr(newval, econtext, &isnull[fieldnum - 1], NULL); + } - econtext->caseValue_datum = save_datum; - econtext->caseValue_isNull = save_isNull; + econtext->caseValue_datum = save_datum; + econtext->caseValue_isNull = save_isNull; - tuple = (HeapTuple)tableam_tops_form_tuple(tupDesc, values, isnull); + tuple = (HeapTuple)tableam_tops_form_tuple(tupDesc, values, isnull); - pfree_ext(values); - pfree_ext(isnull); + pfree_ext(values); + pfree_ext(isnull); - return HeapTupleGetDatum(tuple); + return HeapTupleGetDatum(tuple); } /* ---------------------------------------------------------------- - * ExecEvalRelabelType - * - * Evaluate a RelabelType node. - * ---------------------------------------------------------------- - */ +* ExecEvalRelabelType +* +* Evaluate a RelabelType node. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalRelabelType(GenericExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); + return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); } /* ---------------------------------------------------------------- - * ExecEvalCoerceViaIO - * - * Evaluate a CoerceViaIO node. - * ---------------------------------------------------------------- - */ +* ExecEvalCoerceViaIO +* +* Evaluate a CoerceViaIO node. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalCoerceViaIO(CoerceViaIOState* iostate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Datum result; - Datum inputval; - char* string = NULL; + Datum result; + Datum inputval; + char* string = NULL; - inputval = ExecEvalExpr(iostate->arg, econtext, isNull, isDone); + inputval = ExecEvalExpr(iostate->arg, econtext, isNull, isDone); - if (isDone && *isDone == ExprEndResult) - return inputval; /* nothing to do */ + if (isDone && *isDone == ExprEndResult) + return inputval; /* nothing to do */ - if (*isNull) - string = NULL; /* output functions are not called on nulls */ - else - string = OutputFunctionCall(&iostate->outfunc, inputval); + if (*isNull) + string = NULL; /* output functions are not called on nulls */ + else + string = OutputFunctionCall(&iostate->outfunc, inputval); - result = InputFunctionCall(&iostate->infunc, string, iostate->intypioparam, -1); + result = InputFunctionCall(&iostate->infunc, string, iostate->intypioparam, -1); - /* The input function cannot change the null/not-null status */ - return result; + /* The input function cannot change the null/not-null status */ + return result; } /* ---------------------------------------------------------------- - * ExecEvalArrayCoerceExpr - * - * Evaluate an ArrayCoerceExpr node. - * ---------------------------------------------------------------- - */ +* ExecEvalArrayCoerceExpr +* +* Evaluate an ArrayCoerceExpr node. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalArrayCoerceExpr( - ArrayCoerceExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + ArrayCoerceExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)astate->xprstate.expr; - Datum result; - ArrayType* array = NULL; - FunctionCallInfoData locfcinfo; + ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)astate->xprstate.expr; + Datum result; + ArrayType* array = NULL; + FunctionCallInfoData locfcinfo; - result = ExecEvalExpr(astate->arg, econtext, isNull, isDone); + result = ExecEvalExpr(astate->arg, econtext, isNull, isDone); - if (isDone && *isDone == ExprEndResult) - return result; /* nothing to do */ - if (*isNull) - return result; /* nothing to do */ + if (isDone && *isDone == ExprEndResult) + return result; /* nothing to do */ + if (*isNull) + return result; /* nothing to do */ - /* - * If it's binary-compatible, modify the element type in the array header, - * but otherwise leave the array as we received it. - */ - if (!OidIsValid(acoerce->elemfuncid)) { - /* Detoast input array if necessary, and copy in any case */ - array = DatumGetArrayTypePCopy(result); - ARR_ELEMTYPE(array) = astate->resultelemtype; - PG_RETURN_ARRAYTYPE_P(array); - } + /* + * If it's binary-compatible, modify the element type in the array header, + * but otherwise leave the array as we received it. + */ + if (!OidIsValid(acoerce->elemfuncid)) { + /* Detoast input array if necessary, and copy in any case */ + array = DatumGetArrayTypePCopy(result); + ARR_ELEMTYPE(array) = astate->resultelemtype; + PG_RETURN_ARRAYTYPE_P(array); + } - /* Detoast input array if necessary, but don't make a useless copy */ - array = DatumGetArrayTypeP(result); + /* Detoast input array if necessary, but don't make a useless copy */ + array = DatumGetArrayTypeP(result); - /* Initialize function cache if first time through */ - if (astate->elemfunc.fn_oid == InvalidOid) { - AclResult aclresult; + /* Initialize function cache if first time through */ + if (astate->elemfunc.fn_oid == InvalidOid) { + AclResult aclresult; - /* Check permission to call function */ - aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(), ACL_EXECUTE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(acoerce->elemfuncid)); + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(acoerce->elemfuncid)); - /* Set up the primary fmgr lookup information */ - fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), econtext->ecxt_per_query_memory); - fmgr_info_set_expr((Node*)acoerce, &(astate->elemfunc)); - } + /* Set up the primary fmgr lookup information */ + fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node*)acoerce, &(astate->elemfunc)); + } - /* - * Use array_map to apply the function to each array element. - * - * We pass on the desttypmod and isExplicit flags whether or not the - * function wants them. - * - * Note: coercion functions are assumed to not use collation. - */ - InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3, InvalidOid, NULL, NULL); - locfcinfo.arg[0] = PointerGetDatum(array); - locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod); - locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit); - locfcinfo.argnull[0] = false; - locfcinfo.argnull[1] = false; - locfcinfo.argnull[2] = false; + /* + * Use array_map to apply the function to each array element. + * + * We pass on the desttypmod and isExplicit flags whether or not the + * function wants them. + * + * Note: coercion functions are assumed to not use collation. + */ + InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3, InvalidOid, NULL, NULL); + locfcinfo.arg[0] = PointerGetDatum(array); + locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod); + locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit); + locfcinfo.argnull[0] = false; + locfcinfo.argnull[1] = false; + locfcinfo.argnull[2] = false; - return array_map(&locfcinfo, ARR_ELEMTYPE(array), astate->resultelemtype, astate->amstate); + return array_map(&locfcinfo, ARR_ELEMTYPE(array), astate->resultelemtype, astate->amstate); } /* ---------------------------------------------------------------- - * ExecEvalCurrentOfExpr - * - * The planner must convert CURRENT OF into a TidScan qualification. - * So, we have to be able to do ExecInitExpr on a CurrentOfExpr, - * but we shouldn't ever actually execute it. - * ---------------------------------------------------------------- - */ +* ExecEvalCurrentOfExpr +* +* The planner must convert CURRENT OF into a TidScan qualification. +* So, we have to be able to do ExecInitExpr on a CurrentOfExpr, +* but we shouldn't ever actually execute it. +* ---------------------------------------------------------------- +*/ static Datum ExecEvalCurrentOfExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_EXECUTOR), errmsg("CURRENT OF cannot be executed"))); - return 0; /* keep compiler quiet */ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_EXECUTOR), errmsg("CURRENT OF cannot be executed"))); + return 0; /* keep compiler quiet */ } /* ---------------------------------------------------------------- @@ -5419,76 +5431,72 @@ static Datum ExecEvalPrefixBytea(GenericExprState* state, ExprContext* econtext, } /* - * ExecEvalExprSwitchContext - * - * Same as ExecEvalExpr, but get into the right allocation context explicitly. - */ -Datum ExecEvalExprSwitchContext(ExprState* expression, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) -{ - Datum retDatum; - MemoryContext oldContext; - - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - retDatum = ExecEvalExpr(expression, econtext, isNull, isDone); - MemoryContextSwitchTo(oldContext); - return retDatum; +* ExecInitExpr: prepare an expression tree for execution +* +* This function builds and returns an ExprState tree paralleling the given +* Expr node tree. The ExprState tree can then be handed to ExecEvalExpr +* for execution. Because the Expr tree itself is read-only as far as +* ExecInitExpr and ExecEvalExpr are concerned, several different executions +* of the same plan tree can occur concurrently. +* +* This must be called in a memory context that will last as long as repeated +* executions of the expression are needed. Typically the context will be +* the same as the per-query context of the associated ExprContext. +* +* Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to the +* lists of such nodes held by the parent PlanState. Otherwise, we do very +* little initialization here other than building the state-node tree. Any +* nontrivial work associated with initializing runtime info for a node should +* happen during the first actual evaluation of that node. (This policy lets +* us avoid work if the node is never actually evaluated.) +* +* Note: there is no ExecEndExpr function; we assume that any resource +* cleanup needed will be handled by just releasing the memory context +* in which the state tree is built. Functions that require additional +* cleanup work can register a shutdown callback in the ExprContext. +* +* 'node' is the root of the expression tree to examine +* 'parent' is the PlanState node that owns the expression. +* +* 'parent' may be NULL if we are preparing an expression that is not +* associated with a plan tree. (If so, it can't have aggs or subplans.) +* This case should usually come through ExecPrepareExpr, not directly here. +*/ +ExprState* ExecInitExpr(Expr* node, PlanState* parent){ + ExprState* state = NULL; + bool is_flt_frame = (parent != NULL) ? + parent->state->es_is_flt_frame : + (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp ==1); + if(is_flt_frame) { + state = ExecInitExprByFlatten(node, parent); + } else { + state = ExecInitExprByRecursion(node, parent); + } + return state; } - -/* - * ExecInitExpr: prepare an expression tree for execution - * - * This function builds and returns an ExprState tree paralleling the given - * Expr node tree. The ExprState tree can then be handed to ExecEvalExpr - * for execution. Because the Expr tree itself is read-only as far as - * ExecInitExpr and ExecEvalExpr are concerned, several different executions - * of the same plan tree can occur concurrently. - * - * This must be called in a memory context that will last as long as repeated - * executions of the expression are needed. Typically the context will be - * the same as the per-query context of the associated ExprContext. - * - * Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to the - * lists of such nodes held by the parent PlanState. Otherwise, we do very - * little initialization here other than building the state-node tree. Any - * nontrivial work associated with initializing runtime info for a node should - * happen during the first actual evaluation of that node. (This policy lets - * us avoid work if the node is never actually evaluated.) - * - * Note: there is no ExecEndExpr function; we assume that any resource - * cleanup needed will be handled by just releasing the memory context - * in which the state tree is built. Functions that require additional - * cleanup work can register a shutdown callback in the ExprContext. - * - * 'node' is the root of the expression tree to examine - * 'parent' is the PlanState node that owns the expression. - * - * 'parent' may be NULL if we are preparing an expression that is not - * associated with a plan tree. (If so, it can't have aggs or subplans.) - * This case should usually come through ExecPrepareExpr, not directly here. - */ -ExprState* ExecInitExpr(Expr* node, PlanState* parent) +ExprState* ExecInitExprByRecursion(Expr* node, PlanState* parent) { - if (u_sess->hook_cxt.execInitExprHook != NULL) { + if (u_sess->hook_cxt.execInitExprHook != NULL) { ExprState* expr = ((execInitExprFunc)(u_sess->hook_cxt.execInitExprHook))(node, parent); if (expr != NULL) return expr; } ExprState* state = NULL; - gstrace_entry(GS_TRC_ID_ExecInitExpr); - if (node == NULL) { - gstrace_exit(GS_TRC_ID_ExecInitExpr); - return NULL; - } + gstrace_entry(GS_TRC_ID_ExecInitExpr); + if (node == NULL) { + gstrace_exit(GS_TRC_ID_ExecInitExpr); + return NULL; + } - /* Guard against stack overflow due to overly complex expressions */ - check_stack_depth(); + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); - switch (nodeTag(node)) { - case T_Var: - /* varattno == InvalidAttrNumber means it's a whole-row Var */ - if (((Var*)node)->varattno == InvalidAttrNumber) { - WholeRowVarExprState* wstate = makeNode(WholeRowVarExprState); + switch (nodeTag(node)) { + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var*)node)->varattno == InvalidAttrNumber) { + WholeRowVarExprState* wstate = makeNode(WholeRowVarExprState); wstate->parent = parent; wstate->wrv_junkFilter = NULL; @@ -5503,644 +5511,680 @@ ExprState* ExecInitExpr(Expr* node, PlanState* parent) case T_UserVar: case T_SetVariableExpr: state = (ExprState*)makeNode(ExprState); - state->evalfunc = ExecEvalConst; - break; - case T_Param: - state = (ExprState*)makeNode(ExprState); - switch (((Param*)node)->paramkind) { - case PARAM_EXEC: - state->evalfunc = ExecEvalParamExec; - break; - case PARAM_EXTERN: - state->evalfunc = ExecEvalParamExtern; - break; - default: - ereport(ERROR, - (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + state->is_flt_frame = false; + state->evalfunc = ExecEvalConst; + break; + case T_Param: + state = (ExprState*)makeNode(ExprState); + state->is_flt_frame = false; + switch (((Param*)node)->paramkind) { + case PARAM_EXEC: + state->evalfunc = ExecEvalParamExec; + break; + case PARAM_EXTERN: + state->evalfunc = ExecEvalParamExtern; + break; + default: + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized paramkind: %d", (int)((Param*)node)->paramkind))); - break; - } - break; - case T_CoerceToDomainValue: - state = (ExprState*)makeNode(ExprState); - state->evalfunc = ExecEvalCoerceToDomainValue; - break; - case T_CaseTestExpr: - state = (ExprState*)makeNode(ExprState); - state->evalfunc = ExecEvalCaseTestExpr; - break; - case T_Aggref: { - Aggref* aggref = (Aggref*)node; - AggrefExprState* astate = makeNode(AggrefExprState); + break; + } + break; + case T_CoerceToDomainValue: + state = (ExprState*)makeNode(ExprState); + state->evalfunc = ExecEvalCoerceToDomainValue; + break; + case T_CaseTestExpr: + state = (ExprState*)makeNode(ExprState); + state->is_flt_frame = false; + state->evalfunc = ExecEvalCaseTestExpr; + break; + case T_Aggref: { + Aggref* aggref = (Aggref*)node; + AggrefExprState* astate = makeNode(AggrefExprState); - astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalAggref; - if (parent && (IsA(parent, AggState) || IsA(parent, VecAggState))) { - AggState* aggstate = (AggState*)parent; - int naggs; + astate->xprstate.is_flt_frame = false; + astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalAggref; + if (parent && (IsA(parent, AggState) || IsA(parent, VecAggState))) { + AggState* aggstate = (AggState*)parent; + int naggs; - aggstate->aggs = lcons(astate, aggstate->aggs); - naggs = ++aggstate->numaggs; + aggstate->aggs = lcons(astate, aggstate->aggs); + naggs = ++aggstate->numaggs; - astate->aggdirectargs = (List*)ExecInitExpr((Expr*)aggref->aggdirectargs, parent); + astate->aggdirectargs = (List*)ExecInitExprByRecursion((Expr*)aggref->aggdirectargs, parent); - astate->args = (List*)ExecInitExpr((Expr*)aggref->args, parent); + astate->args = (List*)ExecInitExprByRecursion((Expr*)aggref->args, parent); - /* - * Complain if the aggregate's arguments contain any - * aggregates; nested agg functions are semantically - * nonsensical. (This should have been caught earlier, - * but we defend against it here anyway.) - */ - if (naggs != aggstate->numaggs) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("aggregate function calls cannot be nested"))); - } else { - /* planner messed up */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_AGG), errmodule(MOD_OPT), errmsg("Aggref found in non-Agg plan node"))); - } - state = (ExprState*)astate; - } break; - case T_GroupingFunc: { - GroupingFunc* grp_node = (GroupingFunc*)node; - GroupingFuncExprState* grp_state = makeNode(GroupingFuncExprState); - Agg* agg = NULL; + /* + * Complain if the aggregate's arguments contain any + * aggregates; nested agg functions are semantically + * nonsensical. (This should have been caught earlier, + * but we defend against it here anyway.) + */ + if (naggs != aggstate->numaggs) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("aggregate function calls cannot be nested"))); + } else { + /* planner messed up */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_AGG), errmodule(MOD_OPT), errmsg("Aggref found in non-Agg plan node"))); + } + state = (ExprState*)astate; + } break; + case T_GroupingFunc: { + GroupingFunc* grp_node = (GroupingFunc*)node; + GroupingFuncExprState* grp_state = makeNode(GroupingFuncExprState); + grp_state->xprstate.is_flt_frame = false; + Agg* agg = NULL; - if (parent && (IsA(parent, AggState) || IsA(parent, VecAggState))) { - grp_state->aggstate = (AggState*)parent; + if (parent && (IsA(parent, AggState) || IsA(parent, VecAggState))) { + grp_state->aggstate = (AggState*)parent; - agg = (Agg*)(parent->plan); + agg = (Agg*)(parent->plan); - if (agg->groupingSets) - grp_state->clauses = grp_node->cols; - else - grp_state->clauses = NIL; + if (agg->groupingSets) + grp_state->clauses = grp_node->cols; + else + grp_state->clauses = NIL; - state = (ExprState*)grp_state; - state->evalfunc = (ExprStateEvalFunc)ExecEvalGroupingFuncExpr; - } else - ereport(ERROR, - (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), + state = (ExprState*)grp_state; + state->evalfunc = (ExprStateEvalFunc)ExecEvalGroupingFuncExpr; + } else + ereport(ERROR, + (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), errmodule(MOD_OPT), errmsg("parent of GROUPING is not Agg node"))); - } break; - case T_GroupingId: { - GroupingIdExprState* grp_id_state = makeNode(GroupingIdExprState); - if (parent == NULL || !IsA(parent, AggState) || !IsA(parent->plan, Agg)) { - ereport(ERROR, - (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), + } break; + case T_GroupingId: { + GroupingIdExprState* grp_id_state = makeNode(GroupingIdExprState); + grp_id_state->xprstate.is_flt_frame = false; + if (parent == NULL || !IsA(parent, AggState) || !IsA(parent->plan, Agg)) { + ereport(ERROR, + (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), errmodule(MOD_OPT), errmsg("parent of GROUPINGID is not Agg node"))); - } - grp_id_state->aggstate = (AggState*)parent; - state = (ExprState*)grp_id_state; - state->evalfunc = (ExprStateEvalFunc)ExecEvalGroupingIdExpr; - } break; - case T_WindowFunc: { - WindowFunc* wfunc = (WindowFunc*)node; - WindowFuncExprState* wfstate = makeNode(WindowFuncExprState); + } + grp_id_state->aggstate = (AggState*)parent; + state = (ExprState*)grp_id_state; + state->evalfunc = (ExprStateEvalFunc)ExecEvalGroupingIdExpr; + } break; + case T_WindowFunc: { + WindowFunc* wfunc = (WindowFunc*)node; + WindowFuncExprState* wfstate = makeNode(WindowFuncExprState); + wfstate->xprstate.is_flt_frame = false; - wfstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWindowFunc; - if (parent && (IsA(parent, WindowAggState) || IsA(parent, VecWindowAggState))) { - WindowAggState* winstate = (WindowAggState*)parent; - int nfuncs; + wfstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWindowFunc; + if (parent && (IsA(parent, WindowAggState) || IsA(parent, VecWindowAggState))) { + WindowAggState* winstate = (WindowAggState*)parent; + int nfuncs; - winstate->funcs = lcons(wfstate, winstate->funcs); - nfuncs = ++winstate->numfuncs; - if (wfunc->winagg) - winstate->numaggs++; + winstate->funcs = lcons(wfstate, winstate->funcs); + nfuncs = ++winstate->numfuncs; + if (wfunc->winagg) + winstate->numaggs++; - wfstate->args = (List*)ExecInitExpr((Expr*)wfunc->args, parent); + wfstate->args = (List*)ExecInitExprByRecursion((Expr*)wfunc->args, parent); - /* - * Complain if the windowfunc's arguments contain any - * windowfuncs; nested window functions are semantically - * nonsensical. (This should have been caught earlier, - * but we defend against it here anyway.) - */ - if (nfuncs != winstate->numfuncs) - ereport( - ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("window function calls cannot be nested"))); - } else { - /* planner messed up */ - ereport( - ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("WindowFunc found in non-WindowAgg plan node"))); - } - state = (ExprState*)wfstate; - } break; - case T_ArrayRef: { - ArrayRef* aref = (ArrayRef*)node; - ArrayRefExprState* astate = makeNode(ArrayRefExprState); + /* + * Complain if the windowfunc's arguments contain any + * windowfuncs; nested window functions are semantically + * nonsensical. (This should have been caught earlier, + * but we defend against it here anyway.) + */ + if (nfuncs != winstate->numfuncs) + ereport( + ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("window function calls cannot be nested"))); + } else { + /* planner messed up */ + ereport( + ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("WindowFunc found in non-WindowAgg plan node"))); + } + state = (ExprState*)wfstate; + } break; + case T_ArrayRef: { + ArrayRef* aref = (ArrayRef*)node; + ArrayRefExprState* astate = makeNode(ArrayRefExprState); + astate->xprstate.is_flt_frame = false; - astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArrayRef; - astate->refupperindexpr = (List*)ExecInitExpr((Expr*)aref->refupperindexpr, parent); - astate->reflowerindexpr = (List*)ExecInitExpr((Expr*)aref->reflowerindexpr, parent); - astate->refexpr = ExecInitExpr(aref->refexpr, parent); - astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr, parent); - /* do one-time catalog lookups for type info */ - astate->refattrlength = get_typlen(aref->refarraytype); - get_typlenbyvalalign( - aref->refelemtype, &astate->refelemlength, &astate->refelembyval, &astate->refelemalign); - state = (ExprState*)astate; - } break; - case T_FuncExpr: { - FuncExpr* funcexpr = (FuncExpr*)node; - FuncExprState* fstate = makeNode(FuncExprState); - fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFunc; + astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArrayRef; + astate->refupperindexpr = (List*)ExecInitExprByRecursion((Expr*)aref->refupperindexpr, parent); + astate->reflowerindexpr = (List*)ExecInitExprByRecursion((Expr*)aref->reflowerindexpr, parent); + astate->refexpr = ExecInitExprByRecursion(aref->refexpr, parent); + astate->refassgnexpr = ExecInitExprByRecursion(aref->refassgnexpr, parent); + /* do one-time catalog lookups for type info */ + astate->refattrlength = get_typlen(aref->refarraytype); + get_typlenbyvalalign( + aref->refelemtype, &astate->refelemlength, &astate->refelembyval, &astate->refelemalign); + state = (ExprState*)astate; + } break; + case T_FuncExpr: { + FuncExpr* funcexpr = (FuncExpr*)node; + FuncExprState* fstate = makeNode(FuncExprState); + fstate->xprstate.is_flt_frame = false; + fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFunc; - fstate->args = (List*)ExecInitExpr((Expr*)funcexpr->args, parent); - fstate->func.fn_oid = InvalidOid; /* not initialized */ - if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { + + fstate->args = (List*)ExecInitExprByRecursion((Expr*)funcexpr->args, parent); + fstate->func.fn_oid = InvalidOid; /* not initialized */ + if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { fstate->funcReturnsSet = funcexpr->funcretset; } else { fstate->funcReturnsSet = false; } - state = (ExprState*)fstate; - } break; - case T_OpExpr: { - OpExpr* opexpr = (OpExpr*)node; - FuncExprState* fstate = makeNode(FuncExprState); + state = (ExprState*)fstate; + } break; + case T_OpExpr: { + OpExpr* opexpr = (OpExpr*)node; + FuncExprState* fstate = makeNode(FuncExprState); + fstate->xprstate.is_flt_frame = false; - fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalOper; - fstate->args = (List*)ExecInitExpr((Expr*)opexpr->args, parent); - fstate->func.fn_oid = InvalidOid; /* not initialized */ - if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { + fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalOper; + fstate->args = (List*)ExecInitExprByRecursion((Expr*)opexpr->args, parent); + fstate->func.fn_oid = InvalidOid; /* not initialized */ + if (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1) { fstate->funcReturnsSet = opexpr->opretset; } else { fstate->funcReturnsSet = false; } - state = (ExprState*)fstate; - } break; - case T_DistinctExpr: { - DistinctExpr* distinctexpr = (DistinctExpr*)node; - FuncExprState* fstate = makeNode(FuncExprState); + state = (ExprState*)fstate; + } break; + case T_DistinctExpr: { + DistinctExpr* distinctexpr = (DistinctExpr*)node; + FuncExprState* fstate = makeNode(FuncExprState); + fstate->xprstate.is_flt_frame = false; - fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalDistinct; - fstate->args = (List*)ExecInitExpr((Expr*)distinctexpr->args, parent); - fstate->func.fn_oid = InvalidOid; /* not initialized */ - state = (ExprState*)fstate; - } break; - case T_NullIfExpr: { - NullIfExpr* nullifexpr = (NullIfExpr*)node; - FuncExprState* fstate = makeNode(FuncExprState); + fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalDistinct; + fstate->args = (List*)ExecInitExprByRecursion((Expr*)distinctexpr->args, parent); + fstate->func.fn_oid = InvalidOid; /* not initialized */ + state = (ExprState*)fstate; + } break; + case T_NullIfExpr: { + NullIfExpr* nullifexpr = (NullIfExpr*)node; + FuncExprState* fstate = makeNode(FuncExprState); + fstate->xprstate.is_flt_frame = false; - fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNullIf; - fstate->args = (List*)ExecInitExpr((Expr*)nullifexpr->args, parent); - fstate->func.fn_oid = InvalidOid; /* not initialized */ - state = (ExprState*)fstate; - } break; - case T_ScalarArrayOpExpr: { - ScalarArrayOpExpr* opexpr = (ScalarArrayOpExpr*)node; - ScalarArrayOpExprState* sstate = makeNode(ScalarArrayOpExprState); + fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNullIf; + fstate->args = (List*)ExecInitExprByRecursion((Expr*)nullifexpr->args, parent); + fstate->func.fn_oid = InvalidOid; /* not initialized */ + state = (ExprState*)fstate; + } break; + case T_ScalarArrayOpExpr: { + ScalarArrayOpExpr* opexpr = (ScalarArrayOpExpr*)node; + ScalarArrayOpExprState* sstate = makeNode(ScalarArrayOpExprState); + sstate->fxprstate.xprstate.is_flt_frame = false; - sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalScalarArrayOp; - sstate->fxprstate.args = (List*)ExecInitExpr((Expr*)opexpr->args, parent); - sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */ - sstate->element_type = InvalidOid; /* ditto */ - state = (ExprState*)sstate; - } break; - case T_BoolExpr: { - BoolExpr* boolexpr = (BoolExpr*)node; - BoolExprState* bstate = makeNode(BoolExprState); + sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalScalarArrayOp; + sstate->fxprstate.args = (List*)ExecInitExprByRecursion((Expr*)opexpr->args, parent); + sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */ + sstate->element_type = InvalidOid; /* ditto */ + state = (ExprState*)sstate; + } break; + case T_BoolExpr: { + BoolExpr* boolexpr = (BoolExpr*)node; + BoolExprState* bstate = makeNode(BoolExprState); + bstate->xprstate.is_flt_frame = false; - switch (boolexpr->boolop) { - case AND_EXPR: - bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalAnd; - break; - case OR_EXPR: - bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalOr; - break; - case NOT_EXPR: - bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNot; - break; - default: - ereport(ERROR, - (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + switch (boolexpr->boolop) { + case AND_EXPR: + bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalAnd; + break; + case OR_EXPR: + bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalOr; + break; + case NOT_EXPR: + bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNot; + break; + default: + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_OPT), errmsg("unrecognized boolop: %d", (int)boolexpr->boolop))); - break; - } - bstate->args = (List*)ExecInitExpr((Expr*)boolexpr->args, parent); - state = (ExprState*)bstate; - } break; - case T_SubPlan: { - SubPlan* subplan = (SubPlan*)node; - SubPlanState* sstate = NULL; + break; + } + bstate->args = (List*)ExecInitExprByRecursion((Expr*)boolexpr->args, parent); + state = (ExprState*)bstate; + } break; + case T_SubPlan: { + SubPlan* subplan = (SubPlan*)node; + SubPlanState* sstate = NULL; - if (parent == NULL) - ereport(ERROR, - (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), + if (parent == NULL) + ereport(ERROR, + (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), errmodule(MOD_OPT), errmsg("SubPlan found with no parent plan"))); - sstate = ExecInitSubPlan(subplan, parent); + sstate = ExecInitSubPlan(subplan, parent); - /* Add SubPlanState nodes to parent->subPlan */ - parent->subPlan = lappend(parent->subPlan, sstate); + /* Add SubPlanState nodes to parent->subPlan */ + parent->subPlan = lappend(parent->subPlan, sstate); - state = (ExprState*)sstate; - } break; - case T_AlternativeSubPlan: { - AlternativeSubPlan* asplan = (AlternativeSubPlan*)node; - AlternativeSubPlanState* asstate = NULL; + state = (ExprState*)sstate; + } break; + case T_AlternativeSubPlan: { + AlternativeSubPlan* asplan = (AlternativeSubPlan*)node; + AlternativeSubPlanState* asstate = NULL; - if (parent == NULL) - ereport(ERROR, - (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), + if (parent == NULL) + ereport(ERROR, + (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), errmodule(MOD_OPT), errmsg("AlternativeSubPlan found with no parent plan"))); - asstate = ExecInitAlternativeSubPlan(asplan, parent); + asstate = ExecInitAlternativeSubPlan(asplan, parent); - state = (ExprState*)asstate; - } break; - case T_FieldSelect: { - FieldSelect* fselect = (FieldSelect*)node; - FieldSelectState* fstate = makeNode(FieldSelectState); + state = (ExprState*)asstate; + } break; + case T_FieldSelect: { + FieldSelect* fselect = (FieldSelect*)node; + FieldSelectState* fstate = makeNode(FieldSelectState); + fstate->xprstate.is_flt_frame = false; - fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFieldSelect; - fstate->arg = ExecInitExpr(fselect->arg, parent); - fstate->argdesc = NULL; - state = (ExprState*)fstate; - } break; - case T_FieldStore: { - FieldStore* fstore = (FieldStore*)node; - FieldStoreState* fstate = makeNode(FieldStoreState); + fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFieldSelect; + fstate->arg = ExecInitExprByRecursion(fselect->arg, parent); + fstate->argdesc = NULL; + state = (ExprState*)fstate; + } break; + case T_FieldStore: { + FieldStore* fstore = (FieldStore*)node; + FieldStoreState* fstate = makeNode(FieldStoreState); + fstate->xprstate.is_flt_frame = false; - fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFieldStore; - fstate->arg = ExecInitExpr(fstore->arg, parent); - fstate->newvals = (List*)ExecInitExpr((Expr*)fstore->newvals, parent); - fstate->argdesc = NULL; - state = (ExprState*)fstate; - } break; - case T_RelabelType: { - RelabelType* relabel = (RelabelType*)node; - GenericExprState* gstate = makeNode(GenericExprState); + fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFieldStore; + fstate->arg = ExecInitExprByRecursion(fstore->arg, parent); + fstate->newvals = (List*)ExecInitExprByRecursion((Expr*)fstore->newvals, parent); + fstate->argdesc = NULL; + state = (ExprState*)fstate; + } break; + case T_RelabelType: { + RelabelType* relabel = (RelabelType*)node; + GenericExprState* gstate = makeNode(GenericExprState); + gstate->xprstate.is_flt_frame = false; - gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRelabelType; - gstate->arg = ExecInitExpr(relabel->arg, parent); - state = (ExprState*)gstate; - } break; - case T_CoerceViaIO: { - CoerceViaIO* iocoerce = (CoerceViaIO*)node; - CoerceViaIOState* iostate = makeNode(CoerceViaIOState); - Oid iofunc; - bool typisvarlena = false; + gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRelabelType; + gstate->arg = ExecInitExprByRecursion(relabel->arg, parent); + state = (ExprState*)gstate; + } break; + case T_CoerceViaIO: { + CoerceViaIO* iocoerce = (CoerceViaIO*)node; + CoerceViaIOState* iostate = makeNode(CoerceViaIOState); + iostate->xprstate.is_flt_frame = false; + Oid iofunc; + bool typisvarlena = false; - iostate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoerceViaIO; - iostate->arg = ExecInitExpr(iocoerce->arg, parent); - /* lookup the result type's input function */ - getTypeInputInfo(iocoerce->resulttype, &iofunc, &iostate->intypioparam); - fmgr_info(iofunc, &iostate->infunc); - /* lookup the input type's output function */ - getTypeOutputInfo(exprType((Node*)iocoerce->arg), &iofunc, &typisvarlena); - fmgr_info(iofunc, &iostate->outfunc); - state = (ExprState*)iostate; - } break; - case T_ArrayCoerceExpr: { - ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)node; - ArrayCoerceExprState* astate = makeNode(ArrayCoerceExprState); + iostate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoerceViaIO; + iostate->arg = ExecInitExprByRecursion(iocoerce->arg, parent); + /* lookup the result type's input function */ + getTypeInputInfo(iocoerce->resulttype, &iofunc, &iostate->intypioparam); + fmgr_info(iofunc, &iostate->infunc); + /* lookup the input type's output function */ + getTypeOutputInfo(exprType((Node*)iocoerce->arg), &iofunc, &typisvarlena); + fmgr_info(iofunc, &iostate->outfunc); + state = (ExprState*)iostate; + } break; + case T_ArrayCoerceExpr: { + ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)node; + ArrayCoerceExprState* astate = makeNode(ArrayCoerceExprState); + astate->xprstate.is_flt_frame = false; - astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArrayCoerceExpr; - astate->arg = ExecInitExpr(acoerce->arg, parent); - astate->resultelemtype = get_element_type(acoerce->resulttype); - if (astate->resultelemtype == InvalidOid) - ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("target type is not an array"))); - /* Arrays over domains aren't supported yet */ - Assert(getBaseType(astate->resultelemtype) == astate->resultelemtype); - astate->elemfunc.fn_oid = InvalidOid; /* not initialized */ - astate->amstate = (ArrayMapState*)palloc0(sizeof(ArrayMapState)); - state = (ExprState*)astate; - } break; - case T_ConvertRowtypeExpr: { - ConvertRowtypeExpr* convert = (ConvertRowtypeExpr*)node; - ConvertRowtypeExprState* cstate = makeNode(ConvertRowtypeExprState); + astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArrayCoerceExpr; + astate->arg = ExecInitExprByRecursion(acoerce->arg, parent); + astate->resultelemtype = get_element_type(acoerce->resulttype); + if (astate->resultelemtype == InvalidOid) + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("target type is not an array"))); + /* Arrays over domains aren't supported yet */ + Assert(getBaseType(astate->resultelemtype) == astate->resultelemtype); + astate->elemfunc.fn_oid = InvalidOid; /* not initialized */ + astate->amstate = (ArrayMapState*)palloc0(sizeof(ArrayMapState)); + state = (ExprState*)astate; + } break; + case T_ConvertRowtypeExpr: { + ConvertRowtypeExpr* convert = (ConvertRowtypeExpr*)node; + ConvertRowtypeExprState* cstate = makeNode(ConvertRowtypeExprState); + cstate->xprstate.is_flt_frame = false; - cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalConvertRowtype; - cstate->arg = ExecInitExpr(convert->arg, parent); - state = (ExprState*)cstate; - } break; - case T_CaseExpr: { - CaseExpr* caseexpr = (CaseExpr*)node; - CaseExprState* cstate = makeNode(CaseExprState); - List* outlist = NIL; - ListCell* l = NULL; + cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalConvertRowtype; + cstate->arg = ExecInitExprByRecursion(convert->arg, parent); + state = (ExprState*)cstate; + } break; + case T_CaseExpr: { + CaseExpr* caseexpr = (CaseExpr*)node; + CaseExprState* cstate = makeNode(CaseExprState); + cstate->xprstate.is_flt_frame = false; + List* outlist = NIL; + ListCell* l = NULL; - cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCase; - cstate->arg = ExecInitExpr(caseexpr->arg, parent); - foreach (l, caseexpr->args) { - CaseWhen* when = (CaseWhen*)lfirst(l); - CaseWhenState* wstate = makeNode(CaseWhenState); + cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCase; + cstate->arg = ExecInitExprByRecursion(caseexpr->arg, parent); + foreach (l, caseexpr->args) { + CaseWhen* when = (CaseWhen*)lfirst(l); + CaseWhenState* wstate = makeNode(CaseWhenState); - Assert(IsA(when, CaseWhen)); - wstate->xprstate.evalfunc = NULL; /* not used */ - wstate->xprstate.expr = (Expr*)when; - wstate->expr = ExecInitExpr(when->expr, parent); - wstate->result = ExecInitExpr(when->result, parent); - outlist = lappend(outlist, wstate); - } - cstate->args = outlist; - cstate->defresult = ExecInitExpr(caseexpr->defresult, parent); - state = (ExprState*)cstate; - } break; - case T_ArrayExpr: { - ArrayExpr* arrayexpr = (ArrayExpr*)node; - ArrayExprState* astate = makeNode(ArrayExprState); - List* outlist = NIL; - ListCell* l = NULL; + Assert(IsA(when, CaseWhen)); + wstate->xprstate.evalfunc = NULL; /* not used */ + wstate->xprstate.expr = (Expr*)when; + wstate->xprstate.is_flt_frame = false; + wstate->expr = ExecInitExprByRecursion(when->expr, parent); + wstate->result = ExecInitExprByRecursion(when->result, parent); + outlist = lappend(outlist, wstate); + } + cstate->args = outlist; + cstate->defresult = ExecInitExprByRecursion(caseexpr->defresult, parent); + state = (ExprState*)cstate; + } break; + case T_ArrayExpr: { + ArrayExpr* arrayexpr = (ArrayExpr*)node; + ArrayExprState* astate = makeNode(ArrayExprState); + astate->xprstate.is_flt_frame = false; + List* outlist = NIL; + ListCell* l = NULL; - astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArray; - foreach (l, arrayexpr->elements) { - Expr* e = (Expr*)lfirst(l); - ExprState* estate = NULL; + astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArray; + foreach (l, arrayexpr->elements) { + Expr* e = (Expr*)lfirst(l); + ExprState* estate = NULL; - estate = ExecInitExpr(e, parent); - outlist = lappend(outlist, estate); - } - astate->elements = outlist; - /* do one-time catalog lookup for type info */ - get_typlenbyvalalign( - arrayexpr->element_typeid, &astate->elemlength, &astate->elembyval, &astate->elemalign); - state = (ExprState*)astate; - } break; - case T_RowExpr: { - RowExpr* rowexpr = (RowExpr*)node; - RowExprState* rstate = makeNode(RowExprState); - FormData_pg_attribute* attrs = NULL; - List* outlist = NIL; - ListCell* l = NULL; - int i; + estate = ExecInitExprByRecursion(e, parent); + outlist = lappend(outlist, estate); + } + astate->elements = outlist; + /* do one-time catalog lookup for type info */ + get_typlenbyvalalign( + arrayexpr->element_typeid, &astate->elemlength, &astate->elembyval, &astate->elemalign); + state = (ExprState*)astate; + } break; + case T_RowExpr: { + RowExpr* rowexpr = (RowExpr*)node; + RowExprState* rstate = makeNode(RowExprState); + rstate->xprstate.is_flt_frame = false; + FormData_pg_attribute* attrs = NULL; + List* outlist = NIL; + ListCell* l = NULL; + int i; - rstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRow; - /* Build tupdesc to describe result tuples */ - if (rowexpr->row_typeid == RECORDOID) { - /* generic record, use runtime type assignment */ - rstate->tupdesc = ExecTypeFromExprList(rowexpr->args, rowexpr->colnames); - BlessTupleDesc(rstate->tupdesc); - /* we won't need to redo this at runtime */ - } else { - /* it's been cast to a named type, use that */ - rstate->tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1); - } - /* Set up evaluation, skipping any deleted columns */ - Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts); - attrs = rstate->tupdesc->attrs; - i = 0; - foreach (l, rowexpr->args) { - Expr* e = (Expr*)lfirst(l); - ExprState* estate = NULL; + rstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRow; + /* Build tupdesc to describe result tuples */ + if (rowexpr->row_typeid == RECORDOID) { + /* generic record, use runtime type assignment */ + rstate->tupdesc = ExecTypeFromExprList(rowexpr->args, rowexpr->colnames); + BlessTupleDesc(rstate->tupdesc); + /* we won't need to redo this at runtime */ + } else { + /* it's been cast to a named type, use that */ + rstate->tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1); + } + /* Set up evaluation, skipping any deleted columns */ + Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts); + attrs = rstate->tupdesc->attrs; + i = 0; + foreach (l, rowexpr->args) { + Expr* e = (Expr*)lfirst(l); + ExprState* estate = NULL; - if (!attrs[i].attisdropped) { - /* - * Guard against ALTER COLUMN TYPE on rowtype since - * the RowExpr was created. XXX should we check - * typmod too? Not sure we can be sure it'll be the - * same. - */ - if (exprType((Node*)e) != attrs[i].atttypid) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), + if (!attrs[i].attisdropped) { + /* + * Guard against ALTER COLUMN TYPE on rowtype since + * the RowExpr was created. XXX should we check + * typmod too? Not sure we can be sure it'll be the + * same. + */ + if (exprType((Node*)e) != attrs[i].atttypid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("ROW() column has type %s instead of type %s", - format_type_be(exprType((Node*)e)), - format_type_be(attrs[i].atttypid)))); - } else { - /* - * Ignore original expression and insert a NULL. We - * don't really care what type of NULL it is, so - * always make an int4 NULL. - */ - e = (Expr*)makeNullConst(INT4OID, -1, InvalidOid); - } - estate = ExecInitExpr(e, parent); - outlist = lappend(outlist, estate); - i++; - } - rstate->args = outlist; - state = (ExprState*)rstate; - } break; - case T_RowCompareExpr: { - RowCompareExpr* rcexpr = (RowCompareExpr*)node; - RowCompareExprState* rstate = makeNode(RowCompareExprState); - int nopers = list_length(rcexpr->opnos); - List* outlist = NIL; - ListCell* l = NULL; - ListCell* l2 = NULL; - ListCell* l3 = NULL; - int i; + format_type_be(exprType((Node*)e)), + format_type_be(attrs[i].atttypid)))); + } else { + /* + * Ignore original expression and insert a NULL. We + * don't really care what type of NULL it is, so + * always make an int4 NULL. + */ + e = (Expr*)makeNullConst(INT4OID, -1, InvalidOid); + } + estate = ExecInitExprByRecursion(e, parent); + outlist = lappend(outlist, estate); + i++; + } + rstate->args = outlist; + state = (ExprState*)rstate; + } break; + case T_RowCompareExpr: { + RowCompareExpr* rcexpr = (RowCompareExpr*)node; + RowCompareExprState* rstate = makeNode(RowCompareExprState); + rstate->xprstate.is_flt_frame = false; + int nopers = list_length(rcexpr->opnos); + List* outlist = NIL; + ListCell* l = NULL; + ListCell* l2 = NULL; + ListCell* l3 = NULL; + int i; - rstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRowCompare; - Assert(list_length(rcexpr->largs) == nopers); - outlist = NIL; - foreach (l, rcexpr->largs) { - Expr* e = (Expr*)lfirst(l); - ExprState* estate = NULL; + rstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRowCompare; + Assert(list_length(rcexpr->largs) == nopers); + outlist = NIL; + foreach (l, rcexpr->largs) { + Expr* e = (Expr*)lfirst(l); + ExprState* estate = NULL; - estate = ExecInitExpr(e, parent); - outlist = lappend(outlist, estate); - } - rstate->largs = outlist; - Assert(list_length(rcexpr->rargs) == nopers); - outlist = NIL; - foreach (l, rcexpr->rargs) { - Expr* e = (Expr*)lfirst(l); - ExprState* estate = NULL; + estate = ExecInitExprByRecursion(e, parent); + outlist = lappend(outlist, estate); + } + rstate->largs = outlist; + Assert(list_length(rcexpr->rargs) == nopers); + outlist = NIL; + foreach (l, rcexpr->rargs) { + Expr* e = (Expr*)lfirst(l); + ExprState* estate = NULL; - estate = ExecInitExpr(e, parent); - outlist = lappend(outlist, estate); - } - rstate->rargs = outlist; - Assert(list_length(rcexpr->opfamilies) == nopers); - rstate->funcs = (FmgrInfo*)palloc(nopers * sizeof(FmgrInfo)); - rstate->collations = (Oid*)palloc(nopers * sizeof(Oid)); - i = 0; - forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->inputcollids) - { - Oid opno = lfirst_oid(l); - Oid opfamily = lfirst_oid(l2); - Oid inputcollid = lfirst_oid(l3); - int strategy; - Oid lefttype; - Oid righttype; - Oid proc; + estate = ExecInitExprByRecursion(e, parent); + outlist = lappend(outlist, estate); + } + rstate->rargs = outlist; + Assert(list_length(rcexpr->opfamilies) == nopers); + rstate->funcs = (FmgrInfo*)palloc(nopers * sizeof(FmgrInfo)); + rstate->collations = (Oid*)palloc(nopers * sizeof(Oid)); + i = 0; + forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->inputcollids) + { + Oid opno = lfirst_oid(l); + Oid opfamily = lfirst_oid(l2); + Oid inputcollid = lfirst_oid(l3); + int strategy; + Oid lefttype; + Oid righttype; + Oid proc; - get_op_opfamily_properties(opno, opfamily, false, &strategy, &lefttype, &righttype); - proc = get_opfamily_proc(opfamily, lefttype, righttype, BTORDER_PROC); + get_op_opfamily_properties(opno, opfamily, false, &strategy, &lefttype, &righttype); + proc = get_opfamily_proc(opfamily, lefttype, righttype, BTORDER_PROC); - /* - * If we enforced permissions checks on index support - * functions, we'd need to make a check here. But the - * index support machinery doesn't do that, and neither - * does this code. - */ - fmgr_info(proc, &(rstate->funcs[i])); - rstate->collations[i] = inputcollid; - i++; - } - state = (ExprState*)rstate; - } break; - case T_CoalesceExpr: { - CoalesceExpr* coalesceexpr = (CoalesceExpr*)node; - CoalesceExprState* cstate = makeNode(CoalesceExprState); - List* outlist = NIL; - ListCell* l = NULL; + /* + * If we enforced permissions checks on index support + * functions, we'd need to make a check here. But the + * index support machinery doesn't do that, and neither + * does this code. + */ + fmgr_info(proc, &(rstate->funcs[i])); + rstate->collations[i] = inputcollid; + i++; + } + state = (ExprState*)rstate; + } break; + case T_CoalesceExpr: { + CoalesceExpr* coalesceexpr = (CoalesceExpr*)node; + CoalesceExprState* cstate = makeNode(CoalesceExprState); + cstate->xprstate.is_flt_frame = false; + List* outlist = NIL; + ListCell* l = NULL; - cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoalesce; - foreach (l, coalesceexpr->args) { - Expr* e = (Expr*)lfirst(l); - ExprState* estate = NULL; + cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoalesce; + foreach (l, coalesceexpr->args) { + Expr* e = (Expr*)lfirst(l); + ExprState* estate = NULL; - estate = ExecInitExpr(e, parent); - outlist = lappend(outlist, estate); - } - cstate->args = outlist; - state = (ExprState*)cstate; - } break; - case T_MinMaxExpr: { - MinMaxExpr* minmaxexpr = (MinMaxExpr*)node; - MinMaxExprState* mstate = makeNode(MinMaxExprState); - List* outlist = NIL; - ListCell* l = NULL; - TypeCacheEntry* typentry = NULL; + estate = ExecInitExprByRecursion(e, parent); + outlist = lappend(outlist, estate); + } + cstate->args = outlist; + state = (ExprState*)cstate; + } break; + case T_MinMaxExpr: { + MinMaxExpr* minmaxexpr = (MinMaxExpr*)node; + MinMaxExprState* mstate = makeNode(MinMaxExprState); + mstate->xprstate.is_flt_frame = false; + List* outlist = NIL; + ListCell* l = NULL; + TypeCacheEntry* typentry = NULL; - mstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalMinMax; - foreach (l, minmaxexpr->args) { - Expr* e = (Expr*)lfirst(l); - ExprState* estate = NULL; + mstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalMinMax; + foreach (l, minmaxexpr->args) { + Expr* e = (Expr*)lfirst(l); + ExprState* estate = NULL; - estate = ExecInitExpr(e, parent); - outlist = lappend(outlist, estate); - } - mstate->args = outlist; - /* Look up the btree comparison function for the datatype */ - typentry = lookup_type_cache(minmaxexpr->minmaxtype, TYPECACHE_CMP_PROC); - if (!OidIsValid(typentry->cmp_proc)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_FUNCTION), + estate = ExecInitExprByRecursion(e, parent); + outlist = lappend(outlist, estate); + } + mstate->args = outlist; + /* Look up the btree comparison function for the datatype */ + typentry = lookup_type_cache(minmaxexpr->minmaxtype, TYPECACHE_CMP_PROC); + if (!OidIsValid(typentry->cmp_proc)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify a comparison function for type %s", - format_type_be(minmaxexpr->minmaxtype)))); + format_type_be(minmaxexpr->minmaxtype)))); - /* - * If we enforced permissions checks on index support - * functions, we'd need to make a check here. But the index - * support machinery doesn't do that, and neither does this - * code. - */ - fmgr_info(typentry->cmp_proc, &(mstate->cfunc)); - state = (ExprState*)mstate; - } break; - case T_XmlExpr: { - XmlExpr* xexpr = (XmlExpr*)node; - XmlExprState* xstate = makeNode(XmlExprState); - List* outlist = NIL; - ListCell* arg = NULL; + /* + * If we enforced permissions checks on index support + * functions, we'd need to make a check here. But the index + * support machinery doesn't do that, and neither does this + * code. + */ + fmgr_info(typentry->cmp_proc, &(mstate->cfunc)); + state = (ExprState*)mstate; + } break; + case T_XmlExpr: { + XmlExpr* xexpr = (XmlExpr*)node; + XmlExprState* xstate = makeNode(XmlExprState); + xstate->xprstate.is_flt_frame = false; + List* outlist = NIL; + ListCell* arg = NULL; - xstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalXml; - outlist = NIL; - foreach (arg, xexpr->named_args) { - Expr* e = (Expr*)lfirst(arg); - ExprState* estate = NULL; + xstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalXml; + outlist = NIL; + foreach (arg, xexpr->named_args) { + Expr* e = (Expr*)lfirst(arg); + ExprState* estate = NULL; - estate = ExecInitExpr(e, parent); - outlist = lappend(outlist, estate); - } - xstate->named_args = outlist; + estate = ExecInitExprByRecursion(e, parent); + outlist = lappend(outlist, estate); + } + xstate->named_args = outlist; - outlist = NIL; - foreach (arg, xexpr->args) { - Expr* e = (Expr*)lfirst(arg); - ExprState* estate = NULL; + outlist = NIL; + foreach (arg, xexpr->args) { + Expr* e = (Expr*)lfirst(arg); + ExprState* estate = NULL; - estate = ExecInitExpr(e, parent); - outlist = lappend(outlist, estate); - } - xstate->args = outlist; + estate = ExecInitExprByRecursion(e, parent); + outlist = lappend(outlist, estate); + } + xstate->args = outlist; - state = (ExprState*)xstate; - } break; - case T_NullTest: { - NullTest* ntest = (NullTest*)node; - NullTestState* nstate = makeNode(NullTestState); + state = (ExprState*)xstate; + } break; + case T_NullTest: { + NullTest* ntest = (NullTest*)node; + NullTestState* nstate = makeNode(NullTestState); + nstate->xprstate.is_flt_frame = false; - nstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNullTest; - nstate->arg = ExecInitExpr(ntest->arg, parent); - nstate->argdesc = NULL; - state = (ExprState*)nstate; - } break; - case T_HashFilter: { - HashFilter* htest = (HashFilter*)node; - HashFilterState* hstate = makeNode(HashFilterState); - List* outlist = NIL; - ListCell* l = NULL; - int idx = 0; + nstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNullTest; + nstate->arg = ExecInitExprByRecursion(ntest->arg, parent); + nstate->argdesc = NULL; + state = (ExprState*)nstate; + } break; + case T_HashFilter: { + HashFilter* htest = (HashFilter*)node; + HashFilterState* hstate = makeNode(HashFilterState); + hstate->xprstate.is_flt_frame = false; + List* outlist = NIL; + ListCell* l = NULL; + int idx = 0; - hstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalHashFilter; + hstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalHashFilter; - foreach (l, htest->arg) { - Expr* e = (Expr*)lfirst(l); - ExprState* estate = NULL; + foreach (l, htest->arg) { + Expr* e = (Expr*)lfirst(l); + ExprState* estate = NULL; - estate = ExecInitExpr(e, parent); - outlist = lappend(outlist, estate); - } + estate = ExecInitExprByRecursion(e, parent); + outlist = lappend(outlist, estate); + } - hstate->arg = outlist; - hstate->bucketMap = get_bucketmap_by_execnode(parent->plan->exec_nodes, - parent->state->es_plannedstmt, - &hstate->bucketCnt); - hstate->nodelist = (uint2*)palloc(list_length(htest->nodeList) * sizeof(uint2)); - foreach (l, htest->nodeList) - hstate->nodelist[idx++] = lfirst_int(l); + hstate->arg = outlist; + hstate->bucketMap = get_bucketmap_by_execnode(parent->plan->exec_nodes, + parent->state->es_plannedstmt, + &hstate->bucketCnt); + hstate->nodelist = (uint2*)palloc(list_length(htest->nodeList) * sizeof(uint2)); + foreach (l, htest->nodeList) + hstate->nodelist[idx++] = lfirst_int(l); - state = (ExprState*)hstate; - } break; - case T_BooleanTest: { - BooleanTest* btest = (BooleanTest*)node; - GenericExprState* gstate = makeNode(GenericExprState); + state = (ExprState*)hstate; + } break; + case T_BooleanTest: { + BooleanTest* btest = (BooleanTest*)node; + GenericExprState* gstate = makeNode(GenericExprState); + gstate->xprstate.is_flt_frame = false; - gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalBooleanTest; - gstate->arg = ExecInitExpr(btest->arg, parent); - state = (ExprState*)gstate; - } break; - case T_CoerceToDomain: { - CoerceToDomain* ctest = (CoerceToDomain*)node; - CoerceToDomainState* cstate = makeNode(CoerceToDomainState); + gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalBooleanTest; + gstate->arg = ExecInitExprByRecursion(btest->arg, parent); + state = (ExprState*)gstate; + } break; + case T_CoerceToDomain: { + CoerceToDomain* ctest = (CoerceToDomain*)node; + CoerceToDomainState* cstate = makeNode(CoerceToDomainState); + cstate->xprstate.is_flt_frame = false; - cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoerceToDomain; - cstate->arg = ExecInitExpr(ctest->arg, parent); - cstate->constraints = GetDomainConstraints(ctest->resulttype); - state = (ExprState*)cstate; - } break; - case T_CurrentOfExpr: - state = (ExprState*)makeNode(ExprState); - state->evalfunc = ExecEvalCurrentOfExpr; - break; - case T_TargetEntry: { - TargetEntry* tle = (TargetEntry*)node; - GenericExprState* gstate = makeNode(GenericExprState); + cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoerceToDomain; + cstate->arg = ExecInitExprByRecursion(ctest->arg, parent); + cstate->constraints = GetDomainConstraints(ctest->resulttype); + state = (ExprState*)cstate; + } break; + case T_CurrentOfExpr: + state = (ExprState*)makeNode(ExprState); + state->is_flt_frame = false; + state->evalfunc = ExecEvalCurrentOfExpr; + break; + case T_TargetEntry: { + TargetEntry* tle = (TargetEntry*)node; + GenericExprState* gstate = makeNode(GenericExprState); + gstate->xprstate.is_flt_frame = false; - gstate->xprstate.evalfunc = NULL; /* not used */ - gstate->arg = ExecInitExpr(tle->expr, parent); - state = (ExprState*)gstate; - } break; - case T_List: { - List* outlist = NIL; - ListCell* l = NULL; + gstate->xprstate.evalfunc = NULL; /* not used */ + gstate->arg = ExecInitExprByRecursion(tle->expr, parent); + state = (ExprState*)gstate; + } break; + case T_List: { + List* outlist = NIL; + ListCell* l = NULL; - foreach (l, (List*)node) { - outlist = lappend(outlist, ExecInitExpr((Expr*)lfirst(l), parent)); - } - /* Don't fall through to the "common" code below */ - gstrace_exit(GS_TRC_ID_ExecInitExpr); - return (ExprState*)outlist; - } - case T_Rownum: { - RownumState* rnstate = (RownumState*)makeNode(RownumState); - rnstate->ps = parent; - state = (ExprState*)rnstate; - state->evalfunc = (ExprStateEvalFunc)ExecEvalRownum; - } break; - case T_PrefixKey: { + foreach (l, (List*)node) { + outlist = lappend(outlist, ExecInitExprByRecursion((Expr*)lfirst(l), parent)); + } + /* Don't fall through to the "common" code below */ + gstrace_exit(GS_TRC_ID_ExecInitExpr); + return (ExprState*)outlist; + } + case T_Rownum: { + RownumState* rnstate = (RownumState*)makeNode(RownumState); + rnstate->xprstate.is_flt_frame = false; + rnstate->ps = parent; + state = (ExprState*)rnstate; + state->evalfunc = (ExprStateEvalFunc)ExecEvalRownum; + } break; + case T_PrefixKey: { PrefixKey* pkey = (PrefixKey*)node; GenericExprState* gstate = makeNode(GenericExprState); Oid argtype = exprType((Node*)pkey->arg); @@ -6164,302 +6208,351 @@ ExprState* ExecInitExpr(Expr* node, PlanState* parent) ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized node type: %d when initializing expression.", (int)nodeTag(node)))); - state = NULL; /* keep compiler quiet */ - break; - } + state = NULL; /* keep compiler quiet */ + break; + } - /* Common code for all state-node types */ - state->expr = node; + /* Common code for all state-node types */ + state->expr = node; - if (nodeTag(node) != T_TargetEntry) - state->resultType = exprType((Node*)node); + if (nodeTag(node) != T_TargetEntry) + state->resultType = exprType((Node*)node); - gstrace_exit(GS_TRC_ID_ExecInitExpr); - return state; + gstrace_exit(GS_TRC_ID_ExecInitExpr); + return state; } /* - * ExecInitExprList: call ExecInitExpr on a repression list, return a list of ExprStates. + * Call ExecInitExpr() on a list of expressions, return a list of ExprStates. */ -List* ExecInitExprList(List* nodes, PlanState *parent) +List* ExecInitExprList(List *nodes, PlanState *parent) { - List* result = NIL; - ListCell* lc = NULL; + List *result = NIL; + ListCell *lc; - foreach (lc, nodes) { - Expr* experssion = (Expr*)lfirst(lc); - result = lappend(result, ExecInitExpr(experssion, parent)); - } - return result; + foreach(lc, nodes) + { + Expr *e = (Expr*)lfirst(lc); + + result = lappend(result, ExecInitExpr(e, parent)); + } + + return result; } /* - * ExecPrepareExpr --- initialize for expression execution outside a normal - * Plan tree context. - * - * This differs from ExecInitExpr in that we don't assume the caller is - * already running in the EState's per-query context. Also, we run the - * passed expression tree through expression_planner() to prepare it for - * execution. (In ordinary Plan trees the regular planning process will have - * made the appropriate transformations on expressions, but for standalone - * expressions this won't have happened.) - */ +* ExecPrepareExpr --- initialize for expression execution outside a normal +* Plan tree context. +* +* This differs from ExecInitExpr in that we don't assume the caller is +* already running in the EState's per-query context. Also, we run the +* passed expression tree through expression_planner() to prepare it for +* execution. (In ordinary Plan trees the regular planning process will have +* made the appropriate transformations on expressions, but for standalone +* expressions this won't have happened.) +*/ ExprState* ExecPrepareExpr(Expr* node, EState* estate) { - ExprState* result = NULL; - MemoryContext oldcontext; + ExprState* result = NULL; + MemoryContext oldcontext; - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - node = expression_planner(node); + node = expression_planner(node); - result = ExecInitExpr(node, NULL); + result = ExecInitExpr(node, NULL); - MemoryContextSwitchTo(oldcontext); + MemoryContextSwitchTo(oldcontext); - return result; + return result; } /* ---------------------------------------------------------------- - * ExecQual / ExecTargetList / ExecProject - * ---------------------------------------------------------------- - */ +* ExecQual / ExecTargetList / ExecProject +* ---------------------------------------------------------------- +*/ /* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool ExecQual(List* qual, ExprContext* econtext, bool resultForNull) +* ExecQual +* +* Evaluates a conjunctive boolean expression (qual list) and +* returns true iff none of the subexpressions are false. +* (We also return true if the list is empty.) +* +* If some of the subexpressions yield NULL but none yield FALSE, +* then the result of the conjunction is NULL (ie, unknown) +* according to three-valued boolean logic. In this case, +* we return the value specified by the "resultForNull" parameter. +* +* Callers evaluating WHERE clauses should pass resultForNull=FALSE, +* since SQL specifies that tuples with null WHERE results do not +* get selected. On the other hand, callers evaluating constraint +* conditions should pass resultForNull=TRUE, since SQL also specifies +* that NULL constraint conditions are not failures. +* +* NOTE: it would not be correct to use this routine to evaluate an +* AND subclause of a boolean expression; for that purpose, a NULL +* result must be returned as NULL so that it can be properly treated +* in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). +* This routine is only used in contexts where a complete expression +* is being evaluated and we know that NULL can be treated the same +* as one boolean result or the other. +* +* ---------------------------------------------------------------- +*/ +bool ExecQual(List* qual, ExprContext* econtext, bool resultForNull){ + bool is_flt_frame = (qual && IsA(qual, ExprState)) ? + ((ExprState *)qual)->is_flt_frame : false; + if(is_flt_frame) { + return ExecQualByFlatten((ExprState*)qual, econtext); + } else { + return ExecQualByRecursion(qual, econtext, resultForNull); + } +} +bool ExecQualByRecursion(List* qual, ExprContext* econtext, bool resultForNull) { - bool result = false; - MemoryContext oldContext; + bool result = false; + MemoryContext oldContext; + ListCell* l = NULL; + + /* + * debugging stuff + */ + EV_printf("ExecQual: qual is "); + EV_nodeDisplay(qual); + EV_printf("\n"); + + /* + * Run in short-lived per-tuple context while computing expressions. + */ + oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Evaluate the qual conditions one at a time. If we find a FALSE result, + * we can stop evaluating and return FALSE --- the AND result must be + * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we + * can stop and return FALSE --- the AND result must be FALSE or NULL in + * that case, and the caller doesn't care which. + * + * If we get to the end of the list, we can return TRUE. This will happen + * when the AND result is indeed TRUE, or when the AND result is NULL (one + * or more NULL subresult, with all the rest TRUE) and the caller has + * specified resultForNull = TRUE. + */ + result = true; + + foreach (l, qual) { + ExprState* clause = (ExprState*)lfirst(l); + Datum expr_value; + bool isNull = false; + + expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); + + if (isNull) { + if (resultForNull == false) { + result = false; /* treat NULL as FALSE */ + break; + } + } else { + if (!DatumGetBool(expr_value)) { + result = false; /* definitely FALSE */ + break; + } + } + } + + MemoryContextSwitchTo(oldContext); + + return result; +} + +template +static Datum ExecEvalQual(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone){ + List* qual = (List*)exprstate->expr; + bool result = true; ListCell* l = NULL; - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach (l, qual) { + foreach (l, qual){ ExprState* clause = (ExprState*)lfirst(l); Datum expr_value; - bool isNull = false; - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); + expr_value = ExecEvalExpr(clause, econtext, isNull, isDone); - if (isNull) { - if (resultForNull == false) { - result = false; /* treat NULL as FALSE */ - break; + if(*isNull){ + if(resultForNull == false) { + result = false; } } else { if (!DatumGetBool(expr_value)) { - result = false; /* definitely FALSE */ + result =false; break; } } } + return Datum(result); +} - MemoryContextSwitchTo(oldContext); - - return result; +ExprState *ExecInitQualByRecursion(Expr *node, PlanState * parent, bool resultForNull){ + ExprState* qualState = makeNode(ExprState); + qualState->is_flt_frame = false; + if(resultForNull) + qualState->evalfunc = ExecEvalQual; + else + qualState->evalfunc = ExecEvalQual; + qualState->expr =(Expr*)ExecInitExprByRecursion(node, parent); + return qualState; } /* - * Number of items in a tlist (including any resjunk items!) - */ +* Number of items in a tlist (including any resjunk items!) +*/ int ExecTargetListLength(List* targetlist) { - /* This used to be more complex, but fjoins are dead */ - return list_length(targetlist); + /* This used to be more complex, but fjoins are dead */ + return list_length(targetlist); } /* - * Number of items in a tlist, not including any resjunk items - */ +* Number of items in a tlist, not including any resjunk items +*/ int ExecCleanTargetListLength(List* targetlist) { - int len = 0; - ListCell* tl = NULL; + int len = 0; + ListCell* tl = NULL; - foreach (tl, targetlist) { - TargetEntry* curTle = (TargetEntry*)lfirst(tl); + foreach (tl, targetlist) { + TargetEntry* curTle = (TargetEntry*)lfirst(tl); - Assert(IsA(curTle, TargetEntry)); - if (!curTle->resjunk) - len++; - } - return len; + Assert(IsA(curTle, TargetEntry)); + if (!curTle->resjunk) + len++; + } + return len; } static HeapTuple get_tuple(Relation relation, ItemPointer tid) { - Buffer user_buf = InvalidBuffer; - HeapTuple tuple = NULL; - HeapTuple new_tuple = NULL; + Buffer user_buf = InvalidBuffer; + HeapTuple tuple = NULL; + HeapTuple new_tuple = NULL; - /* alloc mem for old tuple and set tuple id */ - tuple = (HeapTupleData *)heaptup_alloc(BLCKSZ); - tuple->t_data = (HeapTupleHeader)((char *)tuple + HEAPTUPLESIZE); - Assert(tid != NULL); - tuple->t_self = *tid; - - if (heap_fetch(relation, SnapshotAny, tuple, &user_buf, false, NULL)) { - new_tuple = heapCopyTuple((HeapTuple)tuple, relation->rd_att, NULL); - ReleaseBuffer(user_buf); - } else { - ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("The tuple is not found"), - errdetail("Another user is getting tuple or the datum is NULL"))); - } + /* alloc mem for old tuple and set tuple id */ + tuple = (HeapTupleData *)heaptup_alloc(BLCKSZ); + tuple->t_data = (HeapTupleHeader)((char *)tuple + HEAPTUPLESIZE); + Assert(tid != NULL); + tuple->t_self = *tid; - heap_freetuple(tuple); - return new_tuple; + if (heap_fetch(relation, SnapshotAny, tuple, &user_buf, false, NULL)) { + new_tuple = heapCopyTuple((HeapTuple)tuple, relation->rd_att, NULL); + ReleaseBuffer(user_buf); + } else { + ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("The tuple is not found"), + errdetail("Another user is getting tuple or the datum is NULL"))); + } + + heap_freetuple(tuple); + return new_tuple; } static void check_huge_clob_paramter(FunctionCallInfoData* fcinfo, bool is_have_huge_clob) { - if (!is_have_huge_clob || IsSystemObjOid(fcinfo->flinfo->fn_oid)) { - return; - } - Oid schema_oid = get_func_namespace(fcinfo->flinfo->fn_oid); - if (IsPackageSchemaOid(schema_oid)) { - return; - } - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + if (!is_have_huge_clob || IsSystemObjOid(fcinfo->flinfo->fn_oid)) { + return; + } + Oid schema_oid = get_func_namespace(fcinfo->flinfo->fn_oid); + if (IsPackageSchemaOid(schema_oid)) { + return; + } + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("huge clob do not support as function in parameter"))); } bool is_external_clob(Oid type_oid, bool is_null, Datum value) { - if (type_oid == CLOBOID && !is_null && VARATT_IS_EXTERNAL_LOB(value)) { - return true; - } - return false; + if (type_oid == CLOBOID && !is_null && VARATT_IS_EXTERNAL_LOB(value)) { + return true; + } + return false; } bool is_huge_clob(Oid type_oid, bool is_null, Datum value) { - if (!is_external_clob(type_oid, is_null, value)) { - return false; - } + if (!is_external_clob(type_oid, is_null, value)) { + return false; + } - struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(value)); - bool is_huge_clob = false; - /* get relation by relid */ - ItemPointerData tuple_ctid; - tuple_ctid.ip_blkid.bi_hi = lob_pointer->bi_hi; - tuple_ctid.ip_blkid.bi_lo = lob_pointer->bi_lo; - tuple_ctid.ip_posid = lob_pointer->ip_posid; - Relation relation = heap_open(lob_pointer->relid, RowExclusiveLock); - HeapTuple origin_tuple = get_tuple(relation, &tuple_ctid); - if (!HeapTupleIsValid(origin_tuple)) { - ereport(ERROR, - (errcode(ERRCODE_CACHE_LOOKUP_FAILED), - errmsg("cache lookup failed for tuple from relation %u", lob_pointer->relid))); - } - bool attr_is_null = false; - Datum attr = fastgetattr(origin_tuple, lob_pointer->columid, relation->rd_att, &attr_is_null); - if (!attr_is_null && VARATT_IS_HUGE_TOAST_POINTER(attr)) { - is_huge_clob = true; - } - heap_close(relation, NoLock); - heap_freetuple(origin_tuple); - return is_huge_clob; + struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(value)); + bool is_huge_clob = false; + /* get relation by relid */ + ItemPointerData tuple_ctid; + tuple_ctid.ip_blkid.bi_hi = lob_pointer->bi_hi; + tuple_ctid.ip_blkid.bi_lo = lob_pointer->bi_lo; + tuple_ctid.ip_posid = lob_pointer->ip_posid; + Relation relation = heap_open(lob_pointer->relid, RowExclusiveLock); + HeapTuple origin_tuple = get_tuple(relation, &tuple_ctid); + if (!HeapTupleIsValid(origin_tuple)) { + ereport(ERROR, + (errcode(ERRCODE_CACHE_LOOKUP_FAILED), + errmsg("cache lookup failed for tuple from relation %u", lob_pointer->relid))); + } + bool attr_is_null = false; + Datum attr = fastgetattr(origin_tuple, lob_pointer->columid, relation->rd_att, &attr_is_null); + if (!attr_is_null && VARATT_IS_HUGE_TOAST_POINTER(attr)) { + is_huge_clob = true; + } + heap_close(relation, NoLock); + heap_freetuple(origin_tuple); + return is_huge_clob; } Datum fetch_lob_value_from_tuple(varatt_lob_pointer* lob_pointer, Oid update_oid, bool* is_null) { - /* get relation by relid */ - ItemPointerData tuple_ctid; - tuple_ctid.ip_blkid.bi_hi = lob_pointer->bi_hi; - tuple_ctid.ip_blkid.bi_lo = lob_pointer->bi_lo; - tuple_ctid.ip_posid = lob_pointer->ip_posid; - Relation relation = heap_open(lob_pointer->relid, RowExclusiveLock); - HeapTuple origin_tuple = get_tuple(relation, &tuple_ctid); - if (!HeapTupleIsValid(origin_tuple)) { - ereport(ERROR, - (errcode(ERRCODE_CACHE_LOOKUP_FAILED), - errmsg("cache lookup failed for tuple from relation %u", lob_pointer->relid))); - } + /* get relation by relid */ + ItemPointerData tuple_ctid; + tuple_ctid.ip_blkid.bi_hi = lob_pointer->bi_hi; + tuple_ctid.ip_blkid.bi_lo = lob_pointer->bi_lo; + tuple_ctid.ip_posid = lob_pointer->ip_posid; + Relation relation = heap_open(lob_pointer->relid, RowExclusiveLock); + HeapTuple origin_tuple = get_tuple(relation, &tuple_ctid); + if (!HeapTupleIsValid(origin_tuple)) { + ereport(ERROR, + (errcode(ERRCODE_CACHE_LOOKUP_FAILED), + errmsg("cache lookup failed for tuple from relation %u", lob_pointer->relid))); + } - Datum attr = fastgetattr(origin_tuple, lob_pointer->columid, relation->rd_att, is_null); + Datum attr = fastgetattr(origin_tuple, lob_pointer->columid, relation->rd_att, is_null); - if (!OidIsValid(update_oid)) { - heap_close(relation, NoLock); - return attr; - } - Datum new_attr = (Datum)0; - if (*is_null) { - new_attr = (Datum)0; - } else { - if (VARATT_IS_HUGE_TOAST_POINTER(attr)) { - if (unlikely(origin_tuple->tupTableType == UHEAP_TUPLE)) { - ereport(ERROR, - (errcode(ERRCODE_INVALID_NAME), + if (!OidIsValid(update_oid)) { + heap_close(relation, NoLock); + return attr; + } + Datum new_attr = (Datum)0; + if (*is_null) { + new_attr = (Datum)0; + } else { + if (VARATT_IS_HUGE_TOAST_POINTER(attr)) { + if (unlikely(origin_tuple->tupTableType == UHEAP_TUPLE)) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), errmsg("UStore cannot update clob column that larger than 1GB"))); - } - Relation update_rel = heap_open(update_oid, RowExclusiveLock); - struct varlena *old_value = (struct varlena *)DatumGetPointer(attr); - struct varlena *new_value = heap_tuple_fetch_and_copy(update_rel, old_value, false); - new_attr = PointerGetDatum(new_value); - heap_close(update_rel, NoLock); - } else if (VARATT_IS_SHORT(attr) || VARATT_IS_EXTERNAL(attr) || VARATT_IS_4B(attr)) { - new_attr = PointerGetDatum(attr); - } else { - ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), - errmsg("lob value which fetch from tuple type is not recognized."), - errdetail("lob type is not one of the existing types"))); - } - } - heap_close(relation, NoLock); - return new_attr; + } + Relation update_rel = heap_open(update_oid, RowExclusiveLock); + struct varlena *old_value = (struct varlena *)DatumGetPointer(attr); + struct varlena *new_value = heap_tuple_fetch_and_copy(update_rel, old_value, false); + new_attr = PointerGetDatum(new_value); + heap_close(update_rel, NoLock); + } else if (VARATT_IS_SHORT(attr) || VARATT_IS_EXTERNAL(attr) || VARATT_IS_4B(attr)) { + new_attr = PointerGetDatum(attr); + } else { + ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), + errmsg("lob value which fetch from tuple type is not recognized."), + errdetail("lob type is not one of the existing types"))); + } + } + heap_close(relation, NoLock); + return new_attr; } /* @@ -6512,76 +6605,76 @@ static bool getRelIdForPartition(Oid objid, Oid *relationid) } /* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - */ +* ExecTargetList +* Evaluates a targetlist with respect to the given +* expression context. Returns TRUE if we were able to create +* a result, FALSE if we have exhausted a set-valued expression. +* +* Results are stored into the passed values and isnull arrays. +* The caller must provide an itemIsDone array that persists across calls. +* +* As with ExecEvalExpr, the caller should pass isDone = NULL if not +* prepared to deal with sets of result tuples. Otherwise, a return +* of *isDone = ExprMultipleResult signifies a set element, and a return +* of *isDone = ExprEndResult signifies end of the set of tuple. +* We assume that *isDone has been initialized to ExprSingleResult by caller. +*/ static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* values, bool* isnull, - ExprDoneCond* itemIsDone, ExprDoneCond* isDone) + ExprDoneCond* itemIsDone, ExprDoneCond* isDone) { MemoryContext oldContext; bool haveDoneSets = false; - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + /* + * Run in short-lived per-tuple context while computing expressions. + */ + oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - RightRefState* refState = econtext->rightRefState; + RightRefState* refState = econtext->rightRefState; int targetCount = list_length(targetlist); GenericExprState* targetArr[targetCount]; - + int colCnt = (IS_ENABLE_RIGHT_REF(refState) && refState->colCnt > 0) ? refState->colCnt : 1; bool hasExecs[colCnt]; SortTargetListAsArray(refState, targetlist, targetArr); InitOutputValues(refState, targetArr, values, isnull, targetCount, hasExecs); - + /* * evaluate all the expressions in the target list */ haveDoneSets = false; /* any exhausted set exprs in tlist? */ - for (GenericExprState* gstate : targetArr) { - TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; + for (GenericExprState* gstate : targetArr) { + TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; + AttrNumber resind = tle->resno - 1; - ELOG_FIELD_NAME_START(tle->resname); + ELOG_FIELD_NAME_START(tle->resname); - values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]); + values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]); if (IS_ENABLE_RIGHT_REF(refState) && resind < refState->colCnt) { hasExecs[resind] = true; } - if (T_Var == nodeTag(tle->expr) && !isnull[resind]) { - Var *var = (Var *)tle->expr; - if (var->vartype == TIDOID) { - Assert(ItemPointerIsValid((ItemPointer)values[resind])); - } - } + if (T_Var == nodeTag(tle->expr) && !isnull[resind]) { + Var *var = (Var *)tle->expr; + if (var->vartype == TIDOID) { + Assert(ItemPointerIsValid((ItemPointer)values[resind])); + } + } bool isClobAndNotNull = false; isClobAndNotNull = (IsA(tle->expr, Param)) && (!isnull[resind]) && (((Param*)tle->expr)->paramtype == CLOBOID || ((Param*)tle->expr)->paramtype == BLOBOID); if (isClobAndNotNull && econtext->ecxt_scantuple != NULL) { - /* if is big lob, fetch and copy from toast */ - if (VARATT_IS_HUGE_TOAST_POINTER(values[resind])) { - Datum new_attr = (Datum)0; - Oid update_oid = ((HeapTuple)(econtext->ecxt_scantuple->tts_tuple))->t_tableOid; + /* if is big lob, fetch and copy from toast */ + if (VARATT_IS_HUGE_TOAST_POINTER(values[resind])) { + Datum new_attr = (Datum)0; + Oid update_oid = ((HeapTuple)(econtext->ecxt_scantuple->tts_tuple))->t_tableOid; Oid parent_oid = InvalidOid; - Relation parent_rel = NULL; + Relation parent_rel = NULL; Relation part_rel = NULL; Partition part = NULL; bool ispartition = getRelIdForPartition(update_oid, &parent_oid); @@ -6591,13 +6684,13 @@ static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* value part_rel = partitionGetRelation(parent_rel, part); } else { parent_rel = heap_open(update_oid, RowExclusiveLock); - } + } struct varlena *old_value = (struct varlena *)DatumGetPointer(values[resind]); struct varlena *new_value = heap_tuple_fetch_and_copy(part_rel == NULL ? parent_rel : part_rel, old_value, false); if (new_value == NULL) { isnull[resind] = true; - } + } new_attr = PointerGetDatum(new_value); if (ispartition) { releaseDummyRelation(&part_rel); @@ -6606,84 +6699,84 @@ static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* value } else { heap_close(parent_rel, NoLock); } - values[resind] = new_attr; - } - } - ELOG_FIELD_NAME_END; + values[resind] = new_attr; + } + } + ELOG_FIELD_NAME_END; - if (itemIsDone[resind] != ExprSingleResult) { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + if (itemIsDone[resind] != ExprSingleResult) { + /* We have a set-valued expression in the tlist */ + if (isDone == NULL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context when calculate targetlist that cannot accept a " "set"))); - if (itemIsDone[resind] == ExprMultipleResult) { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } else { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } + if (itemIsDone[resind] == ExprMultipleResult) { + /* we have undone sets in the tlist, set flag */ + *isDone = ExprMultipleResult; + } else { + /* we have done sets in the tlist, set flag for that */ + haveDoneSets = true; + } + } + } - if (haveDoneSets) { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } else { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - for (GenericExprState* gstate : targetArr) { - TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; + if (haveDoneSets) { + /* + * note: can't get here unless we verified isDone != NULL + */ + if (*isDone == ExprSingleResult) { + /* + * all sets are done, so report that tlist expansion is complete. + */ + *isDone = ExprEndResult; + MemoryContextSwitchTo(oldContext); + return false; + } else { + /* + * We have some done and some undone sets. Restart the done ones + * so that we can deliver a tuple (if possible). + */ + for (GenericExprState* gstate : targetArr) { + TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; + AttrNumber resind = tle->resno - 1; - if (itemIsDone[resind] == ExprEndResult) { - values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]); + if (itemIsDone[resind] == ExprEndResult) { + values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]); - if (itemIsDone[resind] == ExprEndResult) { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } + if (itemIsDone[resind] == ExprEndResult) { + /* + * Oh dear, this item is returning an empty set. Guess + * we can't make a tuple after all. + */ + *isDone = ExprEndResult; + break; + } + } + } - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) { - for (GenericExprState* gstate : targetArr) { - TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; + /* + * If we cannot make a tuple because some sets are empty, we still + * have to cycle the nonempty sets to completion, else resources + * will not be released from subplans etc. + * + * XXX is that still necessary? + */ + if (*isDone == ExprEndResult) { + for (GenericExprState* gstate : targetArr) { + TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; + AttrNumber resind = tle->resno - 1; - while (itemIsDone[resind] == ExprMultipleResult) { - values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]); - } - } + while (itemIsDone[resind] == ExprMultipleResult) { + values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]); + } + } - MemoryContextSwitchTo(oldContext); - return false; - } - } - } + MemoryContextSwitchTo(oldContext); + return false; + } + } + } if (IS_ENABLE_RIGHT_REF(econtext->rightRefState)) { @@ -6691,117 +6784,124 @@ static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* value econtext->rightRefState->isNulls = nullptr; econtext->rightRefState->hasExecs = nullptr; } - + /* Report success */ MemoryContextSwitchTo(oldContext); - return true; + return true; } /* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot* ExecProject(ProjectionInfo* projInfo, ExprDoneCond* isDone) +* ExecProject +* +* projects a tuple based on projection info and stores +* it in the previously specified tuple table slot. +* +* Note: the result is always a virtual tuple; therefore it +* may reference the contents of the exprContext's scan tuples +* and/or temporary results constructed in the exprContext. +* If the caller wishes the result to be valid longer than that +* data will be valid, he must call ExecMaterializeSlot on the +* result slot. +*/ +TupleTableSlot* ExecProject(ProjectionInfo* projInfo, ExprDoneCond* isDone){ + if (projInfo->pi_state.is_flt_frame){ + return ExecProjectByFlatten(projInfo, isDone); + } else { + return ExecProjectByRecursion(projInfo, isDone); + } +} +TupleTableSlot* ExecProjectByRecursion(ProjectionInfo* projInfo, ExprDoneCond* isDone) { - /* - * sanity checks - */ - Assert(projInfo != NULL); + /* + * sanity checks + */ + Assert(projInfo != NULL); - /* - * get the projection info we want - */ - TupleTableSlot *slot = projInfo->pi_slot; - ExprContext *econtext = projInfo->pi_exprContext; + /* + * get the projection info we want + */ + TupleTableSlot *slot = projInfo->pi_slot; + ExprContext *econtext = projInfo->pi_exprContext; - /* Assume single result row until proven otherwise */ - if (isDone != NULL) - *isDone = ExprSingleResult; + /* Assume single result row until proven otherwise */ + if (isDone != NULL) + *isDone = ExprSingleResult; - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - (void)ExecClearTuple(slot); + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + (void)ExecClearTuple(slot); - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) { - tableam_tslot_getsomeattrs(econtext->ecxt_innertuple, projInfo->pi_lastInnerVar); - } + /* + * Force extraction of all input values that we'll need. The + * Var-extraction loops below depend on this, and we are also prefetching + * all attributes that will be referenced in the generic expressions. + */ + if (projInfo->pi_lastInnerVar > 0) { + tableam_tslot_getsomeattrs(econtext->ecxt_innertuple, projInfo->pi_lastInnerVar); + } - if (projInfo->pi_lastOuterVar > 0) { - tableam_tslot_getsomeattrs(econtext->ecxt_outertuple, projInfo->pi_lastOuterVar); - } + if (projInfo->pi_lastOuterVar > 0) { + tableam_tslot_getsomeattrs(econtext->ecxt_outertuple, projInfo->pi_lastOuterVar); + } - if (projInfo->pi_lastScanVar > 0 && econtext->ecxt_scantuple) { - tableam_tslot_getsomeattrs(econtext->ecxt_scantuple, projInfo->pi_lastScanVar); - } + if (projInfo->pi_lastScanVar > 0 && econtext->ecxt_scantuple) { + tableam_tslot_getsomeattrs(econtext->ecxt_scantuple, projInfo->pi_lastScanVar); + } - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - int numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0 && !IS_ENABLE_RIGHT_REF(projInfo->pi_exprContext->rightRefState)) { - Datum* values = slot->tts_values; - bool* isnull = slot->tts_isnull; - int* varSlotOffsets = projInfo->pi_varSlotOffsets; - int* varNumbers = projInfo->pi_varNumbers; - int i; + /* + * Assign simple Vars to result by direct extraction of fields from source + * slots ... a mite ugly, but fast ... + */ + int numSimpleVars = projInfo->pi_numSimpleVars; + if (numSimpleVars > 0 && !IS_ENABLE_RIGHT_REF(projInfo->pi_exprContext->rightRefState)) { + Datum* values = slot->tts_values; + bool* isnull = slot->tts_isnull; + int* varSlotOffsets = projInfo->pi_varSlotOffsets; + int* varNumbers = projInfo->pi_varNumbers; + int i; - if (projInfo->pi_directMap) { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) { - char* slotptr = ((char*)econtext) + varSlotOffsets[i]; - TupleTableSlot* varSlot = *((TupleTableSlot**)slotptr); - int varNumber = varNumbers[i] - 1; + if (projInfo->pi_directMap) { + /* especially simple case where vars go to output in order */ + for (i = 0; i < numSimpleVars; i++) { + char* slotptr = ((char*)econtext) + varSlotOffsets[i]; + TupleTableSlot* varSlot = *((TupleTableSlot**)slotptr); + int varNumber = varNumbers[i] - 1; - Assert (varNumber < varSlot->tts_tupleDescriptor->natts); - Assert (i < slot->tts_tupleDescriptor->natts); - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } else { - /* we have to pay attention to varOutputCols[] */ - int* varOutputCols = projInfo->pi_varOutputCols; + Assert (varNumber < varSlot->tts_tupleDescriptor->natts); + Assert (i < slot->tts_tupleDescriptor->natts); + values[i] = varSlot->tts_values[varNumber]; + isnull[i] = varSlot->tts_isnull[varNumber]; + } + } else { + /* we have to pay attention to varOutputCols[] */ + int* varOutputCols = projInfo->pi_varOutputCols; - for (i = 0; i < numSimpleVars; i++) { - char* slotptr = ((char*)econtext) + varSlotOffsets[i]; - TupleTableSlot* varSlot = *((TupleTableSlot**)slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; + for (i = 0; i < numSimpleVars; i++) { + char* slotptr = ((char*)econtext) + varSlotOffsets[i]; + TupleTableSlot* varSlot = *((TupleTableSlot**)slotptr); + int varNumber = varNumbers[i] - 1; + int varOutputCol = varOutputCols[i] - 1; - Assert (varNumber < varSlot->tts_tupleDescriptor->natts); - Assert (varOutputCol < slot->tts_tupleDescriptor->natts); - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } + Assert (varNumber < varSlot->tts_tupleDescriptor->natts); + Assert (varOutputCol < slot->tts_tupleDescriptor->natts); + values[varOutputCol] = varSlot->tts_values[varNumber]; + isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; + } + } + } - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) { - if (IS_ENABLE_RIGHT_REF(econtext->rightRefState)) { + /* + * If there are any generic expressions, evaluate them. It's possible + * that there are set-returning functions in such expressions; if so and + * we have reached the end of the set, we return the result slot, which we + * already marked empty. + */ + if (projInfo->pi_targetlist) { + if (IS_ENABLE_RIGHT_REF(econtext->rightRefState)) { econtext->rightRefState->isUpsert = projInfo->isUpsertHasRightRef; } bool flag = !ExecTargetList(projInfo->pi_targetlist, econtext, slot->tts_values, @@ -6810,92 +6910,92 @@ TupleTableSlot* ExecProject(ProjectionInfo* projInfo, ExprDoneCond* isDone) if (econtext->rightRefState) { econtext->rightRefState->isUpsert = false; } - + if (flag) { return slot; /* no more result rows, return empty slot */ } } - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + return ExecStoreVirtualTuple(slot); } static Datum ExecEvalGroupingIdExpr( - GroupingIdExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) + GroupingIdExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - int groupingId = 0; + int groupingId = 0; - if (isDone != NULL) { - *isDone = ExprSingleResult; - } + if (isDone != NULL) { + *isDone = ExprSingleResult; + } - *isNull = false; + *isNull = false; - for (int i = 0; i < gstate->aggstate->current_phase; i++) { - groupingId += gstate->aggstate->phases[i].numsets; - } - groupingId += gstate->aggstate->projected_set + 1; + for (int i = 0; i < gstate->aggstate->current_phase; i++) { + groupingId += gstate->aggstate->phases[i].numsets; + } + groupingId += gstate->aggstate->projected_set + 1; - return (Datum)groupingId; + return (Datum)groupingId; } /* - * @Description: copy cursor data from estate->datums to target_cursor - * @in datums - estate->datums - * @in dno - varno in datums - * @in target_cursor - target cursor data - * @return -void - */ +* @Description: copy cursor data from estate->datums to target_cursor +* @in datums - estate->datums +* @in dno - varno in datums +* @in target_cursor - target cursor data +* @return -void +*/ void ExecCopyDataFromDatum(PLpgSQL_datum** datums, int dno, Cursor_Data* target_cursor) { - PLpgSQL_var *cursor_var = (PLpgSQL_var *)(datums[dno]); + PLpgSQL_var *cursor_var = (PLpgSQL_var *)(datums[dno]); - /* only copy cursor option to refcursor */ - if (cursor_var->datatype->typoid != REFCURSOROID) { - return; - } + /* only copy cursor option to refcursor */ + if (cursor_var->datatype->typoid != REFCURSOROID) { + return; + } - cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ISOPEN]); - target_cursor->is_open = DatumGetBool(cursor_var->value); - cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_FOUND]); - target_cursor->found = DatumGetBool(cursor_var->value); - target_cursor->null_fetch = cursor_var->isnull; - cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_NOTFOUND]); - target_cursor->not_found = DatumGetBool(cursor_var->value); - cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ROWCOUNT]); - target_cursor->row_count = DatumGetInt32(cursor_var->value); - target_cursor->null_open = cursor_var->isnull; - target_cursor->cur_dno = dno; + cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ISOPEN]); + target_cursor->is_open = DatumGetBool(cursor_var->value); + cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_FOUND]); + target_cursor->found = DatumGetBool(cursor_var->value); + target_cursor->null_fetch = cursor_var->isnull; + cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_NOTFOUND]); + target_cursor->not_found = DatumGetBool(cursor_var->value); + cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ROWCOUNT]); + target_cursor->row_count = DatumGetInt32(cursor_var->value); + target_cursor->null_open = cursor_var->isnull; + target_cursor->cur_dno = dno; } /* - * @Description: copy cursor data to estate->datums - * @in datums - estate->datums - * @in dno - varno in datums - * @in target_cursor - source cursor data - * @return -void - */ +* @Description: copy cursor data to estate->datums +* @in datums - estate->datums +* @in dno - varno in datums +* @in target_cursor - source cursor data +* @return -void +*/ void ExecCopyDataToDatum(PLpgSQL_datum** datums, int dno, Cursor_Data* source_cursor) { - PLpgSQL_var *cursor_var = (PLpgSQL_var *)(datums[dno]); + PLpgSQL_var *cursor_var = (PLpgSQL_var *)(datums[dno]); - /* only copy cursor option to refcursor */ - if (cursor_var->datatype->typoid != REFCURSOROID) { - return; - } + /* only copy cursor option to refcursor */ + if (cursor_var->datatype->typoid != REFCURSOROID) { + return; + } - cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ISOPEN]); - cursor_var->value = BoolGetDatum(source_cursor->is_open); - cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_FOUND]); - cursor_var->value = BoolGetDatum(source_cursor->found); - cursor_var->isnull = source_cursor->null_fetch; - cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_NOTFOUND]); - cursor_var->value = BoolGetDatum(source_cursor->not_found); - cursor_var->isnull = source_cursor->null_fetch; - cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ROWCOUNT]); - cursor_var->value = Int32GetDatum(source_cursor->row_count); - cursor_var->isnull = source_cursor->null_open; + cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ISOPEN]); + cursor_var->value = BoolGetDatum(source_cursor->is_open); + cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_FOUND]); + cursor_var->value = BoolGetDatum(source_cursor->found); + cursor_var->isnull = source_cursor->null_fetch; + cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_NOTFOUND]); + cursor_var->value = BoolGetDatum(source_cursor->not_found); + cursor_var->isnull = source_cursor->null_fetch; + cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ROWCOUNT]); + cursor_var->value = Int32GetDatum(source_cursor->row_count); + cursor_var->isnull = source_cursor->null_open; } diff --git a/src/gausskernel/runtime/executor/execSRF.cpp b/src/gausskernel/runtime/executor/execSRF.cpp new file mode 100644 index 000000000..049127e14 --- /dev/null +++ b/src/gausskernel/runtime/executor/execSRF.cpp @@ -0,0 +1,337 @@ +/*------------------------------------------------------------------------- + * + * execSRF.c + * Routines implementing the API for set-returning functions + * + * This file serves nodeFunctionscan.c and nodeProjectSet.c, providing + * common code for calling set-returning functions according to the + * ReturnSetInfo API. + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/executor/execSRF.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/objectaccess.h" +#include "executor/exec/execdebug.h" +#include "executor/executor.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/nodeFuncs.h" +#include "parser/parse_coerce.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_proc_fn.h" +#include "access/tableam.h" + +/* static function decls */ +static void init_sexpr(Oid foid, Oid input_collation, FuncExprState *sexpr, MemoryContext sexprCxt, bool allowSRF, + bool needDescForSRF); +static void ShutdownSetExpr(Datum arg); +template +static ExprDoneCond ExecEvalFuncArgs( + FunctionCallInfo fcinfo, List* argList, ExprContext* econtext, int* plpgsql_var_dno = NULL); + +extern bool func_has_refcursor_args(Oid Funcid, FunctionCallInfoData* fcinfo); +extern void check_huge_clob_paramter(FunctionCallInfoData* fcinfo, bool is_have_huge_clob); + +/* + * Prepare function call in FROM (ROWS FROM) for execution. + * + * This is used by nodeFunctionscan.c. + */ +FuncExprState * +ExecInitTableFunctionResult(Expr *expr, + ExprContext *econtext, PlanState *parent) +{ + FuncExprState *state = makeNode(FuncExprState); + + state->funcReturnsSet = false; + state->xprstate.expr = expr; + state->xprstate.is_flt_frame = true; + state->func.fn_oid = InvalidOid; + + /* + * Normally the passed expression tree will be a FuncExpr, since the + * grammar only allows a function call at the top level of a table + * function reference. However, if the function doesn't return set then + * the planner might have replaced the function call via constant-folding + * or inlining. So if we see any other kind of expression node, execute + * it via the general ExecEvalExpr() code. That code path will not + * support set-returning functions buried in the expression, though. + */ + + + FuncExpr *func = (FuncExpr *) expr; + + state->funcReturnsSet = func->funcretset; + state->args = ExecInitExprList(func->args, parent); + + init_fcache(func->funcid, func->inputcollid, state, + econtext->ecxt_per_query_memory, func->funcretset, false); + + + return state; +} + +/* + * Find the real function return type based on the actual func args' types. + * @inPara arg_num: the number of func's args. + * @inPara actual_arg_types: the type array of actual func args'. + * @inPara fcache: the FuncExprState of this functin. + * @return Oid: the real func return type. + */ +static Oid getRealFuncRetype(int arg_num, Oid* actual_arg_types, FuncExprState* fcache) +{ + Oid funcid = fcache->func.fn_oid; + Oid rettype = fcache->func.fn_rettype; + + /* Find the declared arg types in PROCOID by funcid. */ + HeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); + if (!HeapTupleIsValid(proctup)) + ereport(ERROR, + (errcode(ERRCODE_CACHE_LOOKUP_FAILED), + errmodule(MOD_EXECUTOR), + errmsg("cache lookup failed for function %u", funcid))); + + oidvector* proargs = ProcedureGetArgTypes(proctup); + Oid* declared_arg_types = proargs->values; + + /* Find the real return type based on the declared arg types and actual arg types.*/ + rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, arg_num, rettype, false); + + ReleaseSysCache(proctup); + return rettype; +} + +/* + * Check whether the function is a set function supported by the vector engine. + */ +static bool isVectorEngineSupportSetFunc(Oid funcid) +{ + switch (funcid) { + case OID_REGEXP_SPLIT_TO_TABLE: // regexp_split_to_table + case OID_REGEXP_SPLIT_TO_TABLE_NO_FLAG: // regexp_split_to_table + case OID_ARRAY_UNNEST: // unnest + return true; + break; + default: + return false; + break; + } +} + +/* + * Evaluate arguments for a function. + */ +template +static ExprDoneCond ExecEvalFuncArgs( + FunctionCallInfo fcinfo, List* argList, ExprContext* econtext, int* plpgsql_var_dno) +{ + ExprDoneCond argIsDone; + int i; + ListCell *arg; + + argIsDone = ExprSingleResult; /* default assumption */ + + i = 0; + econtext->is_cursor = false; + u_sess->plsql_cxt.func_tableof_index = NIL; + bool is_have_huge_clob = false; + + foreach (arg, argList) { + ExprState *argstate = (ExprState *)lfirst(arg); + + if (has_refcursor && argstate->resultType == REFCURSOROID) + econtext->is_cursor = true; + + fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo->argnull[i]); + ExecTableOfIndexInfo execTableOfIndexInfo; + initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext); + ExecEvalParamExternTableOfIndex((Node*)argstate->expr, &execTableOfIndexInfo); + if (execTableOfIndexInfo.tableOfIndex != NULL) { + MemoryContext oldCxt = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); + PLpgSQL_func_tableof_index* func_tableof = + (PLpgSQL_func_tableof_index*)palloc0(sizeof(PLpgSQL_func_tableof_index)); + func_tableof->varno = i; + func_tableof->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; + func_tableof->tableOfIndex = copyTableOfIndex(execTableOfIndexInfo.tableOfIndex); + u_sess->plsql_cxt.func_tableof_index = lappend(u_sess->plsql_cxt.func_tableof_index, func_tableof); + MemoryContextSwitchTo(oldCxt); + } + + if (has_refcursor && econtext->is_cursor && plpgsql_var_dno != NULL) { + plpgsql_var_dno[i] = econtext->dno; + CopyCursorInfoData(&fcinfo->refcursor_data.argCursor[i], &econtext->cursor_data); + } + fcinfo->argTypes[i] = argstate->resultType; + econtext->is_cursor = false; + if (is_huge_clob(fcinfo->argTypes[i], fcinfo->argnull[i], fcinfo->arg[i])) { + is_have_huge_clob = true; + } + i++; + } + check_huge_clob_paramter(fcinfo, is_have_huge_clob); + + Assert(i == fcinfo->nargs); + + return argIsDone; +} + +/* + * init_sexpr - initialize a FuncExprState node during first use + */ +static void init_sexpr(Oid foid, Oid input_collation, FuncExprState *sexpr, MemoryContext sexprCxt, bool allowSRF, + bool needDescForSRF) +{ + AclResult aclresult; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); + InvokeFunctionExecuteHook(foid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (list_length(sexpr->args) > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", FUNC_MAX_ARGS, FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + fmgr_info_cxt(foid, &(sexpr->func), sexprCxt); + fmgr_info_set_expr((Node *)sexpr->xprstate.expr, &(sexpr->func)); + + /* Initialize the function call parameter struct as well */ + InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func), list_length(sexpr->args), input_collation, NULL, NULL); + + /* If function returns set, check if that's allowed by caller */ + if (sexpr->func.fn_retset && !allowSRF) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + + /* Otherwise, caller should have marked the sexpr correctly */ + Assert(sexpr->func.fn_retset == sexpr->funcReturnsSet); + + /* If function returns set, prepare expected tuple descriptor */ + if (sexpr->func.fn_retset && needDescForSRF) { + TypeFuncClass functypclass; + Oid funcrettype; + TupleDesc tupdesc; + MemoryContext oldcontext; + + functypclass = get_expr_result_type(sexpr->func.fn_expr, &funcrettype, &tupdesc); + + /* Must save tupdesc in sexpr's context */ + oldcontext = MemoryContextSwitchTo(sexprCxt); + + if (functypclass == TYPEFUNC_COMPOSITE) { + /* Composite data type, e.g. a table's row type */ + Assert(tupdesc); + /* Must copy it out of typcache for safety */ + sexpr->funcResultDesc = CreateTupleDescCopy(tupdesc); + sexpr->funcReturnsTuple = true; + } else if (functypclass == TYPEFUNC_SCALAR) { + /* Base data type, i.e. scalar */ + tupdesc = CreateTemplateTupleDesc(1, false); + TupleDescInitEntry(tupdesc, (AttrNumber)1, NULL, funcrettype, -1, 0); + sexpr->funcResultDesc = tupdesc; + sexpr->funcReturnsTuple = false; + } else if (functypclass == TYPEFUNC_RECORD) { + /* This will work if function doesn't need an expectedDesc */ + sexpr->funcResultDesc = NULL; + sexpr->funcReturnsTuple = true; + } else { + /* Else, we will fail if function needs an expectedDesc */ + sexpr->funcResultDesc = NULL; + } + + MemoryContextSwitchTo(oldcontext); + } else + sexpr->funcResultDesc = NULL; + + /* Initialize additional state */ + sexpr->funcResultStore = NULL; + sexpr->funcResultSlot = NULL; + sexpr->shutdown_reg = false; +} + +/* + * callback function in case a FuncExprState needs to be shut down before it + * has been run to completion + */ +static void ShutdownSetExpr(Datum arg) +{ + FuncExprState *sexpr = castNode(FuncExprState, DatumGetPointer(arg)); + + /* If we have a slot, make sure it's let go of any tuplestore pointer */ + if (sexpr->funcResultSlot) + ExecClearTuple(sexpr->funcResultSlot); + + /* Release any open tuplestore */ + if (sexpr->funcResultStore) + tuplestore_end(sexpr->funcResultStore); + sexpr->funcResultStore = NULL; + + /* Clear any active set-argument state */ + sexpr->setArgsValid = false; + + /* execUtils will deregister the callback... */ + sexpr->shutdown_reg = false; +} + +/* + * Prepare targetlist SRF function call for execution. + * + * This is used by nodeProjectSet.c. + */ +FuncExprState *ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent) +{ + FuncExprState *state = makeNode(FuncExprState); + + state->funcReturnsSet = true; + state->xprstate.expr = expr; + state->xprstate.is_flt_frame = true; + state->func.fn_oid = InvalidOid; + + /* + * Initialize metadata. The expression node could be either a FuncExpr or + * an OpExpr. + */ + if (IsA(expr, FuncExpr)) { + FuncExpr *func = (FuncExpr *)expr; + + state->args = ExecInitExprList(func->args, parent); + init_fcache(func->funcid, func->inputcollid, state, econtext->ecxt_per_query_memory, true, true); + } else if (IsA(expr, OpExpr)) { + OpExpr *op = (OpExpr *)expr; + + state->args = ExecInitExprList(op->args, parent); + init_fcache(op->opfuncid, op->inputcollid, state, econtext->ecxt_per_query_memory, true, true); + } else + elog(ERROR, "unrecognized node type: %d", (int)nodeTag(expr)); + + /* shouldn't get here unless the selected function returns set */ + Assert(state->func.fn_retset); + + state->has_refcursor = func_has_refcursor_args(state->func.fn_oid, &state->fcinfo_data); + + return state; +} \ No newline at end of file diff --git a/src/gausskernel/runtime/executor/execScan.cpp b/src/gausskernel/runtime/executor/execScan.cpp index a1f7bb597..a2e36800d 100755 --- a/src/gausskernel/runtime/executor/execScan.cpp +++ b/src/gausskernel/runtime/executor/execScan.cpp @@ -49,7 +49,7 @@ static TupleTableSlot* ExecScanFetch(ScanState* node, ExecScanAccessMtd access_m * join to the remote side. The recheck method is responsible not * only for rechecking the scan/join quals but also for storing * the correct tuple in the slot. - * + * * currently not support. */ Assert(false); @@ -138,13 +138,13 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ - if (node->ps.ps_TupFromTlist) { + if (node->ps.ps_vec_TupFromTlist) { Assert(proj_info); /* can't get here if not projecting */ result_slot = ExecProject(proj_info, &is_done); if (is_done == ExprMultipleResult) return result_slot; /* Done with that source tuple... */ - node->ps.ps_TupFromTlist = false; + node->ps.ps_vec_TupFromTlist = false; } /* @@ -158,8 +158,7 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct /* * Reset per-tuple memory context to free any expression evaluation - * storage allocated in the previous tuple cycle. Note this can't happen - * until we're done projecting out tuples from a scan tuple. + * storage allocated in the previous tuple cycle. */ ResetExprContext(econtext); @@ -181,9 +180,12 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct */ if (TupIsNull(slot) || unlikely(executorEarlyStop())) { if (proj_info != NULL) { - return ExecClearTuple(proj_info->pi_slot); + if (proj_info->pi_state.is_flt_frame) { + return ExecClearTuple(proj_info->pi_state.resultslot); + } else { + return ExecClearTuple(proj_info->pi_slot); + } } else { - /* slot is not used whild early free happen */ return NULL; } } @@ -200,7 +202,7 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (qual == NULL || ExecQual(qual, econtext, false)) { + if (qual == NULL || ExecQual(qual, econtext)) { /* * Found a satisfactory scan tuple. */ @@ -218,8 +220,9 @@ TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd access_mtd, /* funct /* Copy the xcnodeoid if underlying scanned slot has one */ result_slot->tts_xcnodeoid = slot->tts_xcnodeoid; #endif /* PGXC */ + if (is_done != ExprEndResult) { - node->ps.ps_TupFromTlist = (is_done == ExprMultipleResult); + node->ps.ps_vec_TupFromTlist = (is_done == ExprMultipleResult); /* * @hdfs @@ -380,7 +383,7 @@ void ExecScanReScan(ScanState* node) EState* estate = node->ps.state; /* Stop projecting any tuples from SRFs in the targetlist */ - node->ps.ps_TupFromTlist = false; + node->ps.ps_vec_TupFromTlist = false; /* Rescan EvalPlanQual tuple if we're inside an EvalPlanQual recheck */ if (estate->es_epqScanDone != NULL) { diff --git a/src/gausskernel/runtime/executor/execUtils.cpp b/src/gausskernel/runtime/executor/execUtils.cpp index 8a3df3ebd..f804ace9e 100644 --- a/src/gausskernel/runtime/executor/execUtils.cpp +++ b/src/gausskernel/runtime/executor/execUtils.cpp @@ -60,6 +60,7 @@ #include "utils/snapmgr.h" #include "utils/partitionmap.h" #include "utils/partitionmap_gs.h" +#include "utils/typcache.h" #include "optimizer/var.h" #include "utils/resowner.h" #include "miscadmin.h" @@ -72,6 +73,10 @@ #include "utils/xml.h" #include "utils/rangetypes.h" #include "commands/sequence.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_proc_fn.h" +#include "catalog/pg_proc_fn.h" +#include "funcapi.h" static bool get_last_attnums(Node* node, ProjectionInfo* projInfo); static bool index_recheck_constraint( @@ -80,6 +85,7 @@ static bool check_violation(Relation heap, Relation index, IndexInfo *indexInfo, const bool *isnull, EState *estate, bool newIndex, bool errorOK, CheckWaitMode waitMode, ConflictInfoData *conflictInfo, Oid partoid = InvalidOid, int2 bucketid = InvalidBktId, Oid *conflictPartOid = NULL, int2 *conflictBucketid = NULL); +extern struct varlena *heap_tuple_fetch_and_copy(Relation rel, struct varlena *attr, bool needcheck); /* ---------------------------------------------------------------- * Executor state and memory management functions @@ -192,6 +198,7 @@ EState* CreateExecutorState(MemoryContext saveCxt) estate->pruningResult = NULL; estate->first_autoinc = 0; + estate->es_is_flt_frame = (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1); /* * Return the executor state structure */ @@ -726,12 +733,9 @@ ProjectionInfo* ExecBuildVecProjectionInfo( projInfo->pi_exprContext->current_row = 0; } - if (exprlist == NIL) { - projInfo->pi_itemIsDone = NULL; /* not needed */ - } else { - projInfo->pi_itemIsDone = (ExprDoneCond*)palloc0(len * sizeof(ExprDoneCond)); - + if (exprlist != NIL) { if (projInfo->pi_exprContext != NULL && projInfo->pi_exprContext->have_vec_set_fun) { + projInfo->pi_vec_itemIsDone = (ExprDoneCond*)palloc0(len * sizeof(ExprDoneCond)); projInfo->pi_setFuncBatch = New(CurrentMemoryContext) VectorBatch(CurrentMemoryContext, slot->tts_tupleDescriptor); projInfo->pi_exprContext->vec_fun_sel = (bool*)palloc0(BatchMaxSize * sizeof(bool)); @@ -744,12 +748,12 @@ ProjectionInfo* ExecBuildVecProjectionInfo( return projInfo; } -ProjectionInfo* ExecBuildRightRefProjectionInfo(PlanState* planState, TupleDesc inputDesc) +ProjectionInfo* ExecBuildRightRefProjectionInfo(PlanState* planState, TupleDesc inputDesc) { - List* targetList = planState->targetlist; - ExprContext* econtext = planState->ps_ExprContext; + List* targetList = planState->targetlist; + ExprContext* econtext = planState->ps_ExprContext; TupleTableSlot* slot = planState->ps_ResultTupleSlot; - + ProjectionInfo* projInfo = makeNode(ProjectionInfo); int len = ExecTargetListLength(targetList); @@ -759,7 +763,7 @@ ProjectionInfo* ExecBuildRightRefProjectionInfo(PlanState* planState, TupleDesc projInfo->pi_lastInnerVar = 0; projInfo->pi_lastOuterVar = 0; projInfo->pi_lastScanVar = 0; - + projInfo->pi_targetlist = targetList; projInfo->pi_numSimpleVars = 0; projInfo->pi_directMap = false; @@ -768,8 +772,70 @@ ProjectionInfo* ExecBuildRightRefProjectionInfo(PlanState* planState, TupleDesc return projInfo; } +/* + * get_last_attnums: expression walker for ExecBuildProjectionInfo + * + * Update the lastXXXVar counts to be at least as large as the largest + * attribute numbers found in the expression + */ +static bool get_last_attnums(Node* node, ProjectionInfo* projInfo) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) { + Var* variable = (Var*)node; + AttrNumber attnum = variable->varattno; + + switch (variable->varno) { + case INNER_VAR: + if (projInfo->pi_lastInnerVar < attnum) + projInfo->pi_lastInnerVar = attnum; + break; + + case OUTER_VAR: + if (projInfo->pi_lastOuterVar < attnum) + projInfo->pi_lastOuterVar = attnum; + break; + + /* INDEX_VAR is handled by default case */ + default: + if (projInfo->pi_lastScanVar < attnum) + projInfo->pi_lastScanVar = attnum; + break; + } + return false; + } + + /* + * Don't examine the arguments of Aggrefs or WindowFuncs, because those do + * not represent expressions to be evaluated within the overall + * overall targetlist's econtext. GroupingFunc arguments are never + * evaluated at all. + */ + if (IsA(node, Aggref) || IsA(node, GroupingFunc)) + return false; + if (IsA(node, WindowFunc)) + return false; + return expression_tree_walker(node, (bool (*)())get_last_attnums, (void*)projInfo); +} + +ProjectionInfo* ExecBuildProjectionInfo( + List* targetList, ExprContext* econtext, TupleTableSlot* slot, PlanState *parent, TupleDesc inputDesc) +{ + bool is_flt_frame = (parent != NULL) ? parent->state->es_is_flt_frame : false; + + if (is_flt_frame) { + return ExecBuildProjectionInfoByFlatten(targetList, econtext, slot, parent, inputDesc); + } else { + List* targetListState = NULL; + targetListState = (List*)ExecInitExprByRecursion((Expr*)targetList, parent); + return ExecBuildProjectionInfoByRecursion(targetListState, econtext, slot, inputDesc); + } + +} + /* ---------------- - * ExecBuildProjectionInfo + * ExecBuildProjectionInfoByRecursion * * Build a ProjectionInfo node for evaluating the given tlist in the given * econtext, and storing the result into the tuple slot. (Caller must have @@ -783,7 +849,7 @@ ProjectionInfo* ExecBuildRightRefProjectionInfo(PlanState* planState, TupleDesc * there is no need to recheck. * ---------------- */ -ProjectionInfo* ExecBuildProjectionInfo( +ProjectionInfo* ExecBuildProjectionInfoByRecursion( List* targetList, ExprContext* econtext, TupleTableSlot* slot, TupleDesc inputDesc) { ProjectionInfo* projInfo = makeNode(ProjectionInfo); @@ -808,6 +874,7 @@ ProjectionInfo* ExecBuildProjectionInfo( projInfo->pi_lastOuterVar = 0; projInfo->pi_lastScanVar = 0; projInfo->isUpsertHasRightRef = false; + projInfo->pi_state.is_flt_frame = false; /* * We separate the target list elements into simple Var references and @@ -866,7 +933,7 @@ ProjectionInfo* ExecBuildProjectionInfo( break; } numSimpleVars++; - + if (econtext && IS_ENABLE_RIGHT_REF(econtext->rightRefState)) { exprlist = lappend(exprlist, gstate); get_last_attnums((Node*)variable, projInfo); @@ -890,53 +957,6 @@ ProjectionInfo* ExecBuildProjectionInfo( return projInfo; } -/* - * get_last_attnums: expression walker for ExecBuildProjectionInfo - * - * Update the lastXXXVar counts to be at least as large as the largest - * attribute numbers found in the expression - */ -static bool get_last_attnums(Node* node, ProjectionInfo* projInfo) -{ - if (node == NULL) - return false; - if (IsA(node, Var)) { - Var* variable = (Var*)node; - AttrNumber attnum = variable->varattno; - - switch (variable->varno) { - case INNER_VAR: - if (projInfo->pi_lastInnerVar < attnum) - projInfo->pi_lastInnerVar = attnum; - break; - - case OUTER_VAR: - if (projInfo->pi_lastOuterVar < attnum) - projInfo->pi_lastOuterVar = attnum; - break; - - /* INDEX_VAR is handled by default case */ - default: - if (projInfo->pi_lastScanVar < attnum) - projInfo->pi_lastScanVar = attnum; - break; - } - return false; - } - - /* - * Don't examine the arguments of Aggrefs or WindowFuncs, because those do - * not represent expressions to be evaluated within the overall - * overall targetlist's econtext. GroupingFunc arguments are never - * evaluated at all. - */ - if (IsA(node, Aggref) || IsA(node, GroupingFunc)) - return false; - if (IsA(node, WindowFunc)) - return false; - return expression_tree_walker(node, (bool (*)())get_last_attnums, (void*)projInfo); -} - /* ---------------- * ExecAssignProjectionInfo * @@ -948,11 +968,16 @@ static bool get_last_attnums(Node* node, ProjectionInfo* projInfo) */ void ExecAssignProjectionInfo(PlanState* planstate, TupleDesc inputDesc) { - if (planstate->plan && IS_ENABLE_RIGHT_REF(planstate->plan->rightRefState)) { + if (planstate->plan && IS_ENABLE_RIGHT_REF(planstate->plan->rightRefState) && !planstate->state->es_is_flt_frame) { planstate->ps_ProjInfo = ExecBuildRightRefProjectionInfo(planstate, inputDesc); } else { - planstate->ps_ProjInfo = ExecBuildProjectionInfo(planstate->targetlist, planstate->ps_ExprContext, - planstate->ps_ResultTupleSlot, inputDesc); + if (planstate->state->es_is_flt_frame) { + planstate->ps_ProjInfo = ExecBuildProjectionInfoByFlatten(planstate->plan->targetlist, planstate->ps_ExprContext, + planstate->ps_ResultTupleSlot, planstate, inputDesc); + } else { + planstate->ps_ProjInfo = ExecBuildProjectionInfoByRecursion(planstate->targetlist, planstate->ps_ExprContext, + planstate->ps_ResultTupleSlot, inputDesc); + } } } @@ -1419,12 +1444,16 @@ void ExecDeleteIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* es */ predicate = indexInfo->ii_PredicateState; if (predicate == NIL) { - predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + if (estate->es_is_flt_frame) { + predicate = (List*)ExecPrepareQualByFlatten(indexInfo->ii_Predicate, estate); + } else { + predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + } indexInfo->ii_PredicateState = predicate; } /* Skip this index-update if the predicate isn't satisfied */ - if (!ExecQual(predicate, econtext, false)) { + if (!ExecQual(predicate, econtext)) { continue; } } @@ -1574,12 +1603,16 @@ static inline bool CheckForPartialIndex(IndexInfo* indexInfo, EState* estate, Ex * per-query context) */ if (predicate == NIL) { - predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + if (estate->es_is_flt_frame) { + predicate = (List*)ExecPrepareQualByFlatten(indexInfo->ii_Predicate, estate); + } else { + predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + } indexInfo->ii_PredicateState = predicate; } /* Skip this index-update if the predicate isn't satisfied */ - if (!ExecQual(predicate, econtext, false)) { + if (!ExecQual(predicate, econtext)) { return false; } } @@ -1934,12 +1967,16 @@ List* ExecInsertIndexTuples(TupleTableSlot* slot, ItemPointer tupleid, EState* e */ predicate = indexInfo->ii_PredicateState; if (predicate == NIL) { - predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + if (estate->es_is_flt_frame) { + predicate = (List*)ExecPrepareQualByFlatten(indexInfo->ii_Predicate, estate); + } else { + predicate = (List*)ExecPrepareExpr((Expr*)indexInfo->ii_Predicate, estate); + } indexInfo->ii_PredicateState = predicate; } /* Skip this index-update if the predicate isn't satisfied */ - if (!ExecQual(predicate, econtext, false)) { + if (!ExecQual(predicate, econtext)) { continue; } } @@ -2429,6 +2466,10 @@ void ShutdownExprContext(ExprContext* econtext, bool isCommit) MemoryContextSwitchTo(oldcontext); } +/* ---------------------------------------------------------------- + * ExecEvalOper / ExecEvalFunc support routines + * ---------------------------------------------------------------- + */ int PthreadMutexLock(ResourceOwner owner, pthread_mutex_t* mutex, bool trace) { @@ -2760,7 +2801,7 @@ Tuple ReplaceTupleNullCol(TupleDesc tupleDesc, TupleTableSlot *slot) } Tuple oldTup = slot->tts_tuple; Tuple newTup = tableam_tops_modify_tuple(oldTup, tupleDesc, values, nulls, replaces); - + /* revise members of slot */ slot->tts_tuple = newTup; for (attrChk = 1; attrChk <= natts; attrChk++) { @@ -2770,7 +2811,7 @@ Tuple ReplaceTupleNullCol(TupleDesc tupleDesc, TupleTableSlot *slot) slot->tts_isnull[attrChk - 1] = false; slot->tts_values[attrChk - 1] = values[attrChk - 1]; } - + tableam_tops_free_tuple(oldTup); return newTup; } @@ -2857,4 +2898,113 @@ void SortTargetListAsArray(RightRefState* refState, List* targetList, GenericExp targetArr[index++] = (GenericExprState*)lfirst(lc); } } -} \ No newline at end of file +} + +/* this function is only used for getting table of index inout param */ +static bool get_tableofindex_param(Node* node, ExecTableOfIndexInfo* execTableOfIndexInfo) +{ + if (node == NULL) + return false; + if (IsA(node, Param)) { + execTableOfIndexInfo->paramid = ((Param*)node)->paramid; + execTableOfIndexInfo->paramtype = ((Param*)node)->paramtype; + return true; + } + return false; +} + +bool expr_func_has_refcursor_args(Oid Funcid) +{ + HeapTuple proctup = NULL; + Form_pg_proc procStruct; + int allarg; + Oid* p_argtypes = NULL; + char** p_argnames = NULL; + char* p_argmodes = NULL; + bool use_cursor = false; + + proctup = SearchSysCache(PROCOID, ObjectIdGetDatum(Funcid), 0, 0, 0); + + /* + * function may be deleted after clist be searched. + */ + if (!HeapTupleIsValid(proctup)) { + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function doesn't exist "))); + } + + /* get the all args informations, only "in" parameters if p_argmodes is null */ + allarg = get_func_arg_info(proctup, &p_argtypes, &p_argnames, &p_argmodes); + procStruct = (Form_pg_proc)GETSTRUCT(proctup); + + if (procStruct->prorettype == REFCURSOROID) { + use_cursor = true; + } + else { + for (int i = 0; i < allarg; i++) { + if (!(p_argmodes != NULL && (p_argmodes[i] == 'o' || p_argmodes[i] == 'b'))) { + if (p_argtypes[i] == REFCURSOROID) { + use_cursor = true; + break; + } + } + } + } + + ReleaseSysCache(proctup); + return use_cursor; +} + +void check_huge_clob_paramter(FunctionCallInfoData* fcinfo, bool is_have_huge_clob) +{ + if (!is_have_huge_clob || IsSystemObjOid(fcinfo->flinfo->fn_oid)) { + return; + } + Oid schema_oid = get_func_namespace(fcinfo->flinfo->fn_oid); + if (IsPackageSchemaOid(schema_oid)) { + return; + } + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("huge clob do not support as function in parameter"))); +} + +HeapTuple get_tuple(Relation relation, ItemPointer tid) +{ + Buffer user_buf = InvalidBuffer; + HeapTuple tuple = NULL; + HeapTuple new_tuple = NULL; + + /* alloc mem for old tuple and set tuple id */ + tuple = (HeapTupleData *)heaptup_alloc(BLCKSZ); + tuple->t_data = (HeapTupleHeader)((char *)tuple + HEAPTUPLESIZE); + Assert(tid != NULL); + tuple->t_self = *tid; + + if (heap_fetch(relation, SnapshotAny, tuple, &user_buf, false, NULL)) { + new_tuple = heapCopyTuple((HeapTuple)tuple, relation->rd_att, NULL); + ReleaseBuffer(user_buf); + } else { + ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("The tuple is not found"), + errdetail("Another user is getting tuple or the datum is NULL"))); + } + + heap_freetuple(tuple); + return new_tuple; +} + + +void set_result_for_plpgsql_language_function_with_outparam_by_flatten(Datum *result, bool *isNull) +{ + HeapTupleHeader td = DatumGetHeapTupleHeader(*result); + TupleDesc tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); + HeapTupleData tup; + tup.t_len = HeapTupleHeaderGetDatumLength(td); + tup.t_data = td; + Datum *values = (Datum *)palloc(sizeof(Datum) * tupdesc->natts); + bool *nulls = (bool *)palloc(sizeof(bool) * tupdesc->natts); + heap_deform_tuple(&tup, tupdesc, values, nulls); + *result = values[0]; + *isNull = nulls[0]; + pfree(values); + pfree(nulls); +} diff --git a/src/gausskernel/runtime/executor/nodeAgg.cpp b/src/gausskernel/runtime/executor/nodeAgg.cpp index 0bb48f852..d5f469302 100644 --- a/src/gausskernel/runtime/executor/nodeAgg.cpp +++ b/src/gausskernel/runtime/executor/nodeAgg.cpp @@ -924,7 +924,7 @@ static void finalize_aggregate(AggState* aggstate, AggStatePerAgg peraggstate, A */ foreach (lc, peraggstate->aggrefstate->aggdirectargs) { fcinfo.arg[args_pos] = - ExecEvalExpr((ExprState*)lfirst(lc), aggstate->ss.ps.ps_ExprContext, &fcinfo.argnull[args_pos], NULL); + ExecEvalExpr((ExprState*)lfirst(lc), aggstate->ss.ps.ps_ExprContext, &fcinfo.argnull[args_pos]); fcinfo.argTypes[args_pos] = ((ExprState*)lfirst(lc))->resultType; if (anynull == true || fcinfo.argnull[args_pos] == true) anynull = true; @@ -1100,7 +1100,7 @@ static TupleTableSlot* project_aggregates(AggState* aggstate) /* * Check the qual (HAVING clause); if the group does not match, ignore it. */ - if (ExecQual(aggstate->ss.ps.qual, econtext, false)) { + if (ExecQual(aggstate->ss.ps.qual, econtext)) { /* * Form and return or store a projection tuple using the aggregate * results and the representative input tuple. @@ -1111,9 +1111,10 @@ static TupleTableSlot* project_aggregates(AggState* aggstate) result = ExecProject(aggstate->ss.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { - aggstate->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + aggstate->ss.ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); return result; } + } else InstrCountFiltered1(aggstate, 1); @@ -1494,7 +1495,7 @@ static TupleTableSlot* ExecAgg(PlanState* state) * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ - if (node->ss.ps.ps_TupFromTlist) { + if (node->ss.ps.ps_vec_TupFromTlist) { TupleTableSlot* result = NULL; ExprDoneCond isDone; @@ -1502,13 +1503,11 @@ static TupleTableSlot* ExecAgg(PlanState* state) if (isDone == ExprMultipleResult) return result; /* Done with that source tuple... */ - node->ss.ps.ps_TupFromTlist = false; + node->ss.ps.ps_vec_TupFromTlist = false; } /* - * Exit if nothing left to do. (We must do the ps_TupFromTlist check - * first, because in some cases agg_done gets set before we emit the final - * aggregate tuple, and we have to finish running SRFs for it.) + * Exit if nothing left to do. */ if (node->agg_done) return NULL; @@ -2081,8 +2080,12 @@ AggState* ExecInitAgg(Agg* node, EState* estate, int eflags) * that is true, we don't need to worry about evaluating the aggs in any * particular order. */ - aggstate->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->plan.targetlist, (PlanState*)aggstate); - aggstate->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->plan.qual, (PlanState*)aggstate); + if (estate->es_is_flt_frame) { + aggstate->ss.ps.qual = (List*)ExecInitQualByFlatten(node->plan.qual, (PlanState*)aggstate); + } else { + aggstate->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->plan.targetlist, (PlanState*)aggstate); + aggstate->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->plan.qual, (PlanState*)aggstate); + } /* * initialize child nodes @@ -2112,8 +2115,7 @@ AggState* ExecInitAgg(Agg* node, EState* estate, int eflags) ExecAssignResultTypeFromTL(&aggstate->ss.ps); ExecAssignProjectionInfo(&aggstate->ss.ps, NULL); - aggstate->ss.ps.ps_TupFromTlist = false; - + aggstate->ss.ps.ps_vec_TupFromTlist = false; /* * get the count of aggregates in targetlist and quals */ @@ -2470,7 +2472,7 @@ AggState* ExecInitAgg(Agg* node, EState* estate, int eflags) &peraggstate->transfn, peraggstate->numTransInputs + 1, peraggstate->aggCollation, - (Node*)aggstate, + (Node*)aggstate, NULL); /* get info zbout relevant datatypes */ get_typlenbyval(aggref->aggtype, &peraggstate->resulttypeLen, &peraggstate->resulttypeByVal); @@ -2634,8 +2636,12 @@ AggState* ExecInitAgg(Agg* node, EState* estate, int eflags) aggstate->evaldesc = ExecTypeFromTL(combined_inputeval, false); aggstate->evalslot = ExecInitExtraTupleSlot(estate); - combined_inputeval = (List *)ExecInitExpr((Expr *)combined_inputeval, (PlanState *)aggstate); - aggstate->evalproj = ExecBuildProjectionInfo(combined_inputeval, aggstate->tmpcontext, aggstate->evalslot, NULL); + if (estate->es_is_flt_frame) { + aggstate->evalproj = ExecBuildProjectionInfoByFlatten(combined_inputeval, aggstate->tmpcontext, aggstate->evalslot, &aggstate->ss.ps, NULL); + } else {combined_inputeval = (List *)ExecInitExprByRecursion((Expr *)combined_inputeval, (PlanState *)aggstate); + aggstate->evalproj = ExecBuildProjectionInfoByRecursion(combined_inputeval, aggstate->tmpcontext, aggstate->evalslot, NULL); + } + ExecSetSlotDescriptor(aggstate->evalslot, aggstate->evaldesc); AggWriteFileControl* TempFilePara = (AggWriteFileControl*)palloc(sizeof(AggWriteFileControl)); @@ -2758,8 +2764,7 @@ void ExecReScanAgg(AggState* node) } node->agg_done = false; - node->ss.ps.ps_TupFromTlist = false; - + node->ss.ps.ps_vec_TupFromTlist = false; if (aggnode->aggstrategy == AGG_HASHED) { /* * In the hashed case, if we haven't yet built the hash table then we @@ -3143,8 +3148,7 @@ void ExecReSetAgg(AggState* node) errno_t rc; node->agg_done = false; - node->ss.ps.ps_TupFromTlist = false; - + node->ss.ps.ps_vec_TupFromTlist = false; /* Make sure we have closed any open tuplesorts */ for (aggno = 0; aggno < node->numaggs; aggno++) { for (setno = 0; setno < numGroupingSets; setno++) { diff --git a/src/gausskernel/runtime/executor/nodeBitmapHeapscan.cpp b/src/gausskernel/runtime/executor/nodeBitmapHeapscan.cpp index 416e29645..e8c47498f 100644 --- a/src/gausskernel/runtime/executor/nodeBitmapHeapscan.cpp +++ b/src/gausskernel/runtime/executor/nodeBitmapHeapscan.cpp @@ -450,7 +450,7 @@ static TupleTableSlot* BitmapHeapTblNext(BitmapHeapScanState* node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) { + if (!ExecQual(node->bitmapqualorig, econtext)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); (void)ExecClearTuple(slot); @@ -606,7 +606,7 @@ static bool BitmapHeapRecheck(BitmapHeapScanState* node, TupleTableSlot* slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig, econtext); } /* ---------------------------------------------------------------- @@ -787,14 +787,17 @@ BitmapHeapScanState* ExecInitBitmapHeapScan(BitmapHeapScan* node, EState* estate */ ExecAssignExprContext(estate, &scanstate->ss.ps); - scanstate->ss.ps.ps_TupFromTlist = false; - /* * initialize child expressions */ - scanstate->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->scan.plan.targetlist, (PlanState*)scanstate); - scanstate->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->scan.plan.qual, (PlanState*)scanstate); - scanstate->bitmapqualorig = (List*)ExecInitExpr((Expr*)node->bitmapqualorig, (PlanState*)scanstate); + if (estate->es_is_flt_frame) { + scanstate->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)scanstate); + scanstate->bitmapqualorig = (List*)ExecInitQualByFlatten(node->bitmapqualorig, (PlanState*)scanstate); + } else { + scanstate->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)scanstate); + scanstate->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)scanstate); + scanstate->bitmapqualorig = (List*)ExecInitExprByRecursion((Expr*)node->bitmapqualorig, (PlanState*)scanstate); + } /* * open the base relation and acquire appropriate lock on it. diff --git a/src/gausskernel/runtime/executor/nodeCtescan.cpp b/src/gausskernel/runtime/executor/nodeCtescan.cpp index 0ec427375..1b79cb0da 100644 --- a/src/gausskernel/runtime/executor/nodeCtescan.cpp +++ b/src/gausskernel/runtime/executor/nodeCtescan.cpp @@ -240,8 +240,12 @@ CteScanState* ExecInitCteScan(CteScan* node, EState* estate, int eflags) /* * initialize child expressions */ - scanstate->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->scan.plan.targetlist, (PlanState*)scanstate); - scanstate->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->scan.plan.qual, (PlanState*)scanstate); + if (estate->es_is_flt_frame) { + scanstate->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)scanstate); + } else { + scanstate->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)scanstate); + scanstate->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)scanstate); + } /* * tuple table initialization @@ -265,8 +269,7 @@ CteScanState* ExecInitCteScan(CteScan* node, EState* estate, int eflags) Assert(scanstate->ss.ps.ps_ResultTupleSlot->tts_tupleDescriptor->td_tam_ops); - scanstate->ss.ps.ps_TupFromTlist = false; - + scanstate->ss.ps.ps_vec_TupFromTlist = false; return scanstate; } diff --git a/src/gausskernel/runtime/executor/nodeExtensible.cpp b/src/gausskernel/runtime/executor/nodeExtensible.cpp index 709b96a92..cab6b2eda 100644 --- a/src/gausskernel/runtime/executor/nodeExtensible.cpp +++ b/src/gausskernel/runtime/executor/nodeExtensible.cpp @@ -116,12 +116,14 @@ ExtensiblePlanState* ExecInitExtensiblePlan(ExtensiblePlan* eplan, EState* estat /* create expression context for node */ ExecAssignExprContext(estate, &extensionPlanState->ss.ps); - extensionPlanState->ss.ps.ps_TupFromTlist = false; - /* initialize child expressions */ - extensionPlanState->ss.ps.targetlist = - (List*)ExecInitExpr((Expr*)eplan->scan.plan.targetlist, (PlanState*)extensionPlanState); - extensionPlanState->ss.ps.qual = (List*)ExecInitExpr((Expr*)eplan->scan.plan.qual, (PlanState*)extensionPlanState); + if (estate->es_is_flt_frame) { + extensionPlanState->ss.ps.qual = (List*)ExecInitQualByFlatten(eplan->scan.plan.qual, (PlanState*)extensionPlanState); + } else { + extensionPlanState->ss.ps.targetlist = + (List*)ExecInitExpr((Expr*)eplan->scan.plan.targetlist, (PlanState*)extensionPlanState); + extensionPlanState->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)eplan->scan.plan.qual, (PlanState*)extensionPlanState); + } /* tuple table initialization */ ExecInitScanTupleSlot(estate, &extensionPlanState->ss); diff --git a/src/gausskernel/runtime/executor/nodeForeignscan.cpp b/src/gausskernel/runtime/executor/nodeForeignscan.cpp index 113e9c20e..aeba3fe53 100644 --- a/src/gausskernel/runtime/executor/nodeForeignscan.cpp +++ b/src/gausskernel/runtime/executor/nodeForeignscan.cpp @@ -142,9 +142,7 @@ ForeignScanState* ExecInitForeignScan(ForeignScan* node, EState* estate, int efl #ifdef ENABLE_MOT } #endif - - scanstate->ss.ps.ps_TupFromTlist = false; - + scanstate->ss.ps.ps_vec_TupFromTlist = false; /* * This function ExecInitForeignScan will be called by ExecInitVecForeignScan. * If the node is VecForeignScan, do not need initialize here. @@ -153,8 +151,12 @@ ForeignScanState* ExecInitForeignScan(ForeignScan* node, EState* estate, int efl /* * initialize child expressions */ - scanstate->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->scan.plan.targetlist, (PlanState*)scanstate); - scanstate->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->scan.plan.qual, (PlanState*)scanstate); + if (estate->es_is_flt_frame) { + scanstate->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)scanstate); + } else { + scanstate->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)scanstate); + scanstate->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)scanstate); + } } /* * tuple table initialization diff --git a/src/gausskernel/runtime/executor/nodeFunctionscan.cpp b/src/gausskernel/runtime/executor/nodeFunctionscan.cpp index 40fa50a4d..b05c0f87f 100644 --- a/src/gausskernel/runtime/executor/nodeFunctionscan.cpp +++ b/src/gausskernel/runtime/executor/nodeFunctionscan.cpp @@ -141,8 +141,12 @@ FunctionScanState* ExecInitFunctionScan(FunctionScan* node, EState* estate, int /* * initialize child expressions */ - scanstate->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->scan.plan.targetlist, (PlanState*)scanstate); - scanstate->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->scan.plan.qual, (PlanState*)scanstate); + if (estate->es_is_flt_frame) { + scanstate->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)scanstate); + } else { + scanstate->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)scanstate); + scanstate->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)scanstate); + } /* * Now determine if the function returns a simple or composite type, and @@ -195,9 +199,17 @@ FunctionScanState* ExecInitFunctionScan(FunctionScan* node, EState* estate, int * Other node-specific setup */ scanstate->tuplestorestate = NULL; - scanstate->funcexpr = ExecInitExpr((Expr*)node->funcexpr, (PlanState*)scanstate); - - scanstate->ss.ps.ps_TupFromTlist = false; + if (estate->es_is_flt_frame) { + if (IsA((Expr *)node->funcexpr, FuncExpr)) { + scanstate->funcexpr = + (ExprState *)ExecInitTableFunctionResult((Expr *)node->funcexpr, scanstate->ss.ps.ps_ExprContext, &scanstate->ss.ps); + } else { + scanstate->funcexpr = (ExprState *)ExecInitExprByFlatten((Expr *)node->funcexpr, (PlanState *)scanstate); + } + } else { + scanstate->funcexpr = ExecInitExprByRecursion((Expr *)node->funcexpr, (PlanState *)scanstate); + } + scanstate->ss.ps.ps_vec_TupFromTlist = false; /* * Initialize result tuple type and projection info. diff --git a/src/gausskernel/runtime/executor/nodeGroup.cpp b/src/gausskernel/runtime/executor/nodeGroup.cpp index eb7d6c974..df89af9c2 100644 --- a/src/gausskernel/runtime/executor/nodeGroup.cpp +++ b/src/gausskernel/runtime/executor/nodeGroup.cpp @@ -60,7 +60,7 @@ static TupleTableSlot* ExecGroup(PlanState* state) * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ - if (node->ss.ps.ps_TupFromTlist) { + if (node->ss.ps.ps_vec_TupFromTlist) { TupleTableSlot* result = NULL; ExprDoneCond isDone; @@ -68,7 +68,7 @@ static TupleTableSlot* ExecGroup(PlanState* state) if (isDone == ExprMultipleResult) return result; /* Done with that source tuple... */ - node->ss.ps.ps_TupFromTlist = false; + node->ss.ps.ps_vec_TupFromTlist = false; } /* @@ -114,7 +114,7 @@ static TupleTableSlot* ExecGroup(PlanState* state) result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { - node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + node->ss.ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); return result; } } else @@ -170,7 +170,7 @@ static TupleTableSlot* ExecGroup(PlanState* state) result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { - node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + node->ss.ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); return result; } } else @@ -218,8 +218,12 @@ GroupState* ExecInitGroup(Group* node, EState* estate, int eflags) /* * initialize child expressions */ - grpstate->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->plan.targetlist, (PlanState*)grpstate); - grpstate->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->plan.qual, (PlanState*)grpstate); + if (estate->es_is_flt_frame) { + grpstate->ss.ps.qual = (List*)ExecInitQualByFlatten(node->plan.qual, (PlanState*)grpstate); + } else { + grpstate->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->plan.targetlist, (PlanState*)grpstate); + grpstate->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->plan.qual, (PlanState*)grpstate); + } /* * initialize child nodes @@ -240,7 +244,7 @@ GroupState* ExecInitGroup(Group* node, EState* estate, int eflags) ExecAssignProjectionInfo(&grpstate->ss.ps, NULL); - grpstate->ss.ps.ps_TupFromTlist = false; + grpstate->ss.ps.ps_vec_TupFromTlist = false; /* * Precompute fmgr lookup data for inner loop @@ -271,7 +275,7 @@ void ExecEndGroup(GroupState* node) void ExecReScanGroup(GroupState* node) { node->grp_done = FALSE; - node->ss.ps.ps_TupFromTlist = false; + node->ss.ps.ps_vec_TupFromTlist = false; /* must clear first tuple */ (void)ExecClearTuple(node->ss.ss_ScanTupleSlot); diff --git a/src/gausskernel/runtime/executor/nodeHash.cpp b/src/gausskernel/runtime/executor/nodeHash.cpp index 9a1460e14..426c72c3a 100644 --- a/src/gausskernel/runtime/executor/nodeHash.cpp +++ b/src/gausskernel/runtime/executor/nodeHash.cpp @@ -236,8 +236,12 @@ HashState* ExecInitHash(Hash* node, EState* estate, int eflags) /* * initialize child expressions */ - hashstate->ps.targetlist = (List*)ExecInitExpr((Expr*)node->plan.targetlist, (PlanState*)hashstate); - hashstate->ps.qual = (List*)ExecInitExpr((Expr*)node->plan.qual, (PlanState*)hashstate); + if (estate->es_is_flt_frame) { + hashstate->ps.qual = (List*)ExecInitQualByFlatten(node->plan.qual, (PlanState*)hashstate); + } else { + hashstate->ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->plan.targetlist, (PlanState*)hashstate); + hashstate->ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->plan.qual, (PlanState*)hashstate); + } /* * initialize child nodes diff --git a/src/gausskernel/runtime/executor/nodeHashjoin.cpp b/src/gausskernel/runtime/executor/nodeHashjoin.cpp index 5a8c2c317..51e49472e 100755 --- a/src/gausskernel/runtime/executor/nodeHashjoin.cpp +++ b/src/gausskernel/runtime/executor/nodeHashjoin.cpp @@ -88,14 +88,14 @@ static TupleTableSlot* ExecHashJoin(PlanState* state) * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ - if (node->js.ps.ps_TupFromTlist) { + if (node->js.ps.ps_vec_TupFromTlist) { TupleTableSlot* result = NULL; result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone == ExprMultipleResult) return result; /* Done with that source tuple... */ - node->js.ps.ps_TupFromTlist = false; + node->js.ps.ps_vec_TupFromTlist = false; } /* @@ -361,7 +361,7 @@ static TupleTableSlot* ExecHashJoin(PlanState* state) result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { - node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + node->js.ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); return result; } } else @@ -396,7 +396,7 @@ static TupleTableSlot* ExecHashJoin(PlanState* state) result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { - node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + node->js.ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); return result; } } else @@ -429,7 +429,7 @@ static TupleTableSlot* ExecHashJoin(PlanState* state) result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { - node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + node->js.ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); return result; } } else @@ -551,12 +551,20 @@ HashJoinState* ExecInitHashJoin(HashJoin* node, EState* estate, int eflags) /* * initialize child expressions */ - hjstate->js.ps.targetlist = (List*)ExecInitExpr((Expr*)node->join.plan.targetlist, (PlanState*)hjstate); - hjstate->js.ps.qual = (List*)ExecInitExpr((Expr*)node->join.plan.qual, (PlanState*)hjstate); - hjstate->js.jointype = node->join.jointype; - hjstate->js.joinqual = (List*)ExecInitExpr((Expr*)node->join.joinqual, (PlanState*)hjstate); - hjstate->js.nulleqqual = (List*)ExecInitExpr((Expr*)node->join.nulleqqual, (PlanState*)hjstate); - hjstate->hashclauses = (List*)ExecInitExpr((Expr*)node->hashclauses, (PlanState*)hjstate); + if (estate->es_is_flt_frame) { + hjstate->js.ps.qual = (List*)ExecInitQualByFlatten(node->join.plan.qual, (PlanState*)hjstate); + hjstate->js.jointype = node->join.jointype; + hjstate->js.joinqual = (List*)ExecInitQualByFlatten(node->join.joinqual, (PlanState*)hjstate); + hjstate->js.nulleqqual = (List*)ExecInitQualByFlatten(node->join.nulleqqual, (PlanState*)hjstate); + hjstate->hashclauses = (List*)ExecInitQualByFlatten(node->hashclauses, (PlanState*)hjstate); + } else { + hjstate->js.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->join.plan.targetlist, (PlanState*)hjstate); + hjstate->js.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->join.plan.qual, (PlanState*)hjstate); + hjstate->js.jointype = node->join.jointype; + hjstate->js.joinqual = (List*)ExecInitExprByRecursion((Expr*)node->join.joinqual, (PlanState*)hjstate); + hjstate->js.nulleqqual = (List*)ExecInitExprByRecursion((Expr*)node->join.nulleqqual, (PlanState*)hjstate); + hjstate->hashclauses = (List*)ExecInitExprByRecursion((Expr*)node->hashclauses, (PlanState*)hjstate); + } /* * initialize child nodes @@ -650,19 +658,30 @@ HashJoinState* ExecInitHashJoin(HashJoin* node, EState* estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - hcollations = NIL; - foreach (l, hjstate->hashclauses) { - FuncExprState* fstate = (FuncExprState*)lfirst(l); - OpExpr* hclause = NULL; + if (estate->es_is_flt_frame) { + foreach (l, node->hashclauses) { + OpExpr *hclause = (OpExpr *)lfirst(l); - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr*)fstate->xprstate.expr; - Assert(IsA(hclause, OpExpr)); - lclauses = lappend(lclauses, linitial(fstate->args)); - rclauses = lappend(rclauses, lsecond(fstate->args)); - hoperators = lappend_oid(hoperators, hclause->opno); - hcollations = lappend_oid(hcollations, hclause->inputcollid); + lclauses = lappend(lclauses, ExecInitExpr((Expr *)linitial(hclause->args), (PlanState *)hjstate)); + rclauses = lappend(rclauses, ExecInitExpr((Expr *)lsecond(hclause->args), (PlanState *)hjstate)); + hoperators = lappend_oid(hoperators, hclause->opno); + hcollations = lappend_oid(hcollations, hclause->inputcollid); + } + } else { + foreach (l, hjstate->hashclauses) { + FuncExprState *fstate = (FuncExprState *)lfirst(l); + OpExpr *hclause = NULL; + + Assert(IsA(fstate, FuncExprState)); + hclause = (OpExpr *)fstate->xprstate.expr; + Assert(IsA(hclause, OpExpr)); + lclauses = lappend(lclauses, linitial(fstate->args)); + rclauses = lappend(rclauses, lsecond(fstate->args)); + hoperators = lappend_oid(hoperators, hclause->opno); + hcollations = lappend_oid(hcollations, hclause->inputcollid); + } } + hjstate->hj_OuterHashKeys = lclauses; hjstate->hj_InnerHashKeys = rclauses; hjstate->hj_HashOperators = hoperators; @@ -670,7 +689,7 @@ HashJoinState* ExecInitHashJoin(HashJoin* node, EState* estate, int eflags) /* child Hash node needs to evaluate inner hash keys, too */ ((HashState*)innerPlanState(hjstate))->hashkeys = rclauses; - hjstate->js.ps.ps_TupFromTlist = false; + hjstate->js.ps.ps_vec_TupFromTlist = false; hjstate->hj_JoinState = HJ_BUILD_HASHTABLE; hjstate->hj_MatchedOuter = false; hjstate->hj_OuterNotEmpty = false; @@ -1092,7 +1111,6 @@ void ExecReScanHashJoin(HashJoinState* node) node->hj_CurSkewBucketNo = INVALID_SKEW_BUCKET_NO; node->hj_CurTuple = NULL; - node->js.ps.ps_TupFromTlist = false; node->hj_MatchedOuter = false; node->hj_FirstOuterTupleSlot = NULL; @@ -1176,7 +1194,7 @@ void ExecReSetHashJoin(HashJoinState* node) node->hj_CurSkewBucketNo = INVALID_SKEW_BUCKET_NO; node->hj_CurTuple = NULL; - node->js.ps.ps_TupFromTlist = false; + node->js.ps.ps_vec_TupFromTlist = false; node->hj_MatchedOuter = false; node->hj_FirstOuterTupleSlot = NULL; node->js.ps.recursive_reset = true; diff --git a/src/gausskernel/runtime/executor/nodeIndexonlyscan.cpp b/src/gausskernel/runtime/executor/nodeIndexonlyscan.cpp index 56d66f4be..ef90c5c4d 100644 --- a/src/gausskernel/runtime/executor/nodeIndexonlyscan.cpp +++ b/src/gausskernel/runtime/executor/nodeIndexonlyscan.cpp @@ -570,7 +570,7 @@ IndexOnlyScanState* ExecInitIndexOnlyScan(IndexOnlyScan* node, EState* estate, i */ ExecAssignExprContext(estate, &indexstate->ss.ps); - indexstate->ss.ps.ps_TupFromTlist = false; + indexstate->ss.ps.ps_vec_TupFromTlist = false; /* * initialize child expressions @@ -578,9 +578,14 @@ IndexOnlyScanState* ExecInitIndexOnlyScan(IndexOnlyScan* node, EState* estate, i * Note: we don't initialize all of the indexorderby expression, only the * sub-parts corresponding to runtime keys (see below). */ - indexstate->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->scan.plan.targetlist, (PlanState*)indexstate); - indexstate->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->scan.plan.qual, (PlanState*)indexstate); - indexstate->indexqual = (List*)ExecInitExpr((Expr*)node->indexqual, (PlanState*)indexstate); + if (estate->es_is_flt_frame) { + indexstate->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)indexstate); + indexstate->indexqual = (List*)ExecInitQualByFlatten(node->indexqual, (PlanState*)indexstate); + } else { + indexstate->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)indexstate); + indexstate->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)indexstate); + indexstate->indexqual = (List*)ExecInitExprByRecursion((Expr*)node->indexqual, (PlanState*)indexstate); + } /* diff --git a/src/gausskernel/runtime/executor/nodeIndexscan.cpp b/src/gausskernel/runtime/executor/nodeIndexscan.cpp index ff808cccf..2efc3432d 100644 --- a/src/gausskernel/runtime/executor/nodeIndexscan.cpp +++ b/src/gausskernel/runtime/executor/nodeIndexscan.cpp @@ -666,7 +666,7 @@ IndexScanState* ExecInitIndexScan(IndexScan* node, EState* estate, int eflags) */ ExecAssignExprContext(estate, &index_state->ss.ps); - index_state->ss.ps.ps_TupFromTlist = false; + index_state->ss.ps.ps_vec_TupFromTlist = false; /* * initialize child expressions @@ -678,9 +678,14 @@ IndexScanState* ExecInitIndexScan(IndexScan* node, EState* estate, int eflags) * would be nice to improve that. (Problem is that any SubPlans present * in the expression must be found now...) */ - index_state->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->scan.plan.targetlist, (PlanState*)index_state); - index_state->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->scan.plan.qual, (PlanState*)index_state); - index_state->indexqualorig = (List*)ExecInitExpr((Expr*)node->indexqualorig, (PlanState*)index_state); + if (estate->es_is_flt_frame) { + index_state->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)index_state); + index_state->indexqualorig = (List*)ExecInitQualByFlatten(node->indexqualorig, (PlanState*)index_state); + } else { + index_state->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)index_state); + index_state->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)index_state); + index_state->indexqualorig = (List*)ExecInitExprByRecursion((Expr*)node->indexqualorig, (PlanState*)index_state); + } /* * open the base relation and acquire appropriate lock on it. diff --git a/src/gausskernel/runtime/executor/nodeMergejoin.cpp b/src/gausskernel/runtime/executor/nodeMergejoin.cpp index 11be89b57..e7d4714c8 100644 --- a/src/gausskernel/runtime/executor/nodeMergejoin.cpp +++ b/src/gausskernel/runtime/executor/nodeMergejoin.cpp @@ -421,11 +421,11 @@ static TupleTableSlot* MJFillOuter(MergeJoinState* node) ExprDoneCond isDone; MJ_printf("ExecMergeJoin: returning outer fill tuple\n"); - + TupleTableSlot* result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { - node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + node->js.ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); return result; } } else @@ -460,7 +460,7 @@ static TupleTableSlot* MJFillInner(MergeJoinState* node) TupleTableSlot* result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { - node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + node->js.ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); return result; } } else @@ -560,7 +560,7 @@ static TupleTableSlot* ExecMergeJoin(PlanState* state) ExprDoneCond isDone; CHECK_FOR_INTERRUPTS(); - + /* * get information from node */ @@ -577,14 +577,14 @@ static TupleTableSlot* ExecMergeJoin(PlanState* state) * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ - if (node->js.ps.ps_TupFromTlist) { + if (node->js.ps.ps_vec_TupFromTlist) { TupleTableSlot* result = NULL; result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone == ExprMultipleResult) return result; /* Done with that source tuple... */ - node->js.ps.ps_TupFromTlist = false; + node->js.ps.ps_vec_TupFromTlist = false; } /* @@ -771,7 +771,7 @@ static TupleTableSlot* ExecMergeJoin(PlanState* state) inner_tuple_slot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = inner_tuple_slot; - qual_result = (join_qual == NIL || ExecQual(join_qual, econtext, false)); + qual_result = (join_qual == NIL || ExecQual(join_qual, econtext)); MJ_DEBUG_QUAL(join_qual, qual_result); if (qual_result) { @@ -790,7 +790,7 @@ static TupleTableSlot* ExecMergeJoin(PlanState* state) node->mj_JoinState = EXEC_MJ_NEXTOUTER; } - qual_result = (other_qual == NIL || ExecQual(other_qual, econtext, false)); + qual_result = (other_qual == NIL || ExecQual(other_qual, econtext)); MJ_DEBUG_QUAL(other_qual, qual_result); if (qual_result) { @@ -798,13 +798,14 @@ static TupleTableSlot* ExecMergeJoin(PlanState* state) * qualification succeeded. now form the desired * projection tuple and return the slot containing it. */ + ExprDoneCond isDone; MJ_printf("ExecMergeJoin: returning tuple\n"); TupleTableSlot* result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { - node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + node->js.ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); return result; } @@ -1425,12 +1426,20 @@ MergeJoinState* ExecInitMergeJoin(MergeJoin* node, EState* estate, int eflags) /* * initialize child expressions */ - merge_state->js.ps.targetlist = - (List*)ExecInitExpr((Expr*)node->join.plan.targetlist, (PlanState*)merge_state); - merge_state->js.ps.qual = (List*)ExecInitExpr((Expr*)node->join.plan.qual, (PlanState*)merge_state); - merge_state->js.jointype = node->join.jointype; - merge_state->js.joinqual = (List*)ExecInitExpr((Expr*)node->join.joinqual, (PlanState*)merge_state); - merge_state->js.nulleqqual = (List*)ExecInitExpr((Expr*)node->join.nulleqqual, (PlanState*)merge_state); + if (estate->es_is_flt_frame) { + merge_state->js.ps.qual = (List*)ExecInitQualByFlatten(node->join.plan.qual, (PlanState*)merge_state); + merge_state->js.jointype = node->join.jointype; + merge_state->js.joinqual = (List*)ExecInitQualByFlatten(node->join.joinqual, (PlanState*)merge_state); + merge_state->js.nulleqqual = (List*)ExecInitQualByFlatten(node->join.nulleqqual, (PlanState*)merge_state); + merge_state->mj_ConstFalseJoin = false; + } else { + merge_state->js.ps.targetlist = + (List*)ExecInitExprByRecursion((Expr*)node->join.plan.targetlist, (PlanState*)merge_state); + merge_state->js.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->join.plan.qual, (PlanState*)merge_state); + merge_state->js.jointype = node->join.jointype; + merge_state->js.joinqual = (List*)ExecInitExprByRecursion((Expr*)node->join.joinqual, (PlanState*)merge_state); + merge_state->js.nulleqqual = (List*)ExecInitExprByRecursion((Expr*)node->join.nulleqqual, (PlanState*)merge_state); + } merge_state->mj_ConstFalseJoin = false; Assert(node->join.joinqual == NIL || !node->skip_mark_restore); @@ -1533,7 +1542,7 @@ MergeJoinState* ExecInitMergeJoin(MergeJoin* node, EState* estate, int eflags) * initialize join state */ merge_state->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER; - merge_state->js.ps.ps_TupFromTlist = false; + merge_state->js.ps.ps_vec_TupFromTlist = false; merge_state->mj_MatchedOuter = false; merge_state->mj_MatchedInner = false; merge_state->mj_OuterTupleSlot = NULL; @@ -1583,7 +1592,7 @@ void ExecReScanMergeJoin(MergeJoinState* node) (void)ExecClearTuple(node->mj_MarkedTupleSlot); node->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER; - node->js.ps.ps_TupFromTlist = false; + node->js.ps.ps_vec_TupFromTlist = false; node->mj_MatchedOuter = false; node->mj_MatchedInner = false; node->mj_OuterTupleSlot = NULL; diff --git a/src/gausskernel/runtime/executor/nodeModifyTable.cpp b/src/gausskernel/runtime/executor/nodeModifyTable.cpp index 5e36663a1..c9c542598 100644 --- a/src/gausskernel/runtime/executor/nodeModifyTable.cpp +++ b/src/gausskernel/runtime/executor/nodeModifyTable.cpp @@ -155,7 +155,7 @@ static void CheckPlanOutput(Plan* subPlan, Relation resultRel) case T_VecToRow: case T_RowToVec: case T_PartIterator: - case T_VecPartIterator: + case T_VecPartIterator: case T_Limit: case T_VecLimit: case T_Sort: @@ -279,7 +279,7 @@ static TupleTableSlot* ExecProcessReturning( econtext->ecxt_outertuple = planSlot; /* Compute the RETURNING expressions */ - return ExecProject(projectReturning, NULL); + return ExecProject(projectReturning); } static void ExecCheckTIDVisible(Relation targetrel, EState* estate, Relation rel, ItemPointer tid) @@ -498,7 +498,7 @@ bool ExecComputeStoredUpdateExpr(ResultRelInfo *resultRelInfo, EState *estate, T if (resultRelInfo->ri_NumUpdatedNeeded == 0) return true; - /* compare update operator whether the newtuple is equal to the oldtuple, + /* compare update operator whether the newtuple is equal to the oldtuple, * if equal, so update don't fix the default column value */ Datum* oldvalues = (Datum*)palloc(natts * sizeof(Datum)); bool* oldnulls = (bool*)palloc(natts * sizeof(bool)); @@ -653,7 +653,7 @@ checktest: } #endif test = tableam_tuple_lock(relation, tuple, &buffer, - estate->es_output_cid, LockTupleExclusive, LockWaitBlock, &tmfd, + estate->es_output_cid, LockTupleExclusive, LockWaitBlock, &tmfd, true, false, false, estate->es_snapshot, &conflictInfo->conflictTid, false, true, conflictInfo->conflictXid); @@ -2258,7 +2258,7 @@ lreplace: /* * check constraints first if SQL has keyword IGNORE - * + * * Note: we need to exclude the case of UPSERT_UPDATE, so that upsert could be successfully finished. */ if (node->mt_upsert->us_action != UPSERT_UPDATE && estate->es_plannedstmt && @@ -3272,7 +3272,7 @@ static TupleTableSlot* ExecReplace(EState* estate, ModifyTableState* node, Tuple if (OidIsValid(seqOid)) elog(ERROR, "REPLACE can not work on sequence!"); } - + /* set flag to start loop */ node->isConflict = true; @@ -3383,7 +3383,7 @@ static TupleTableSlot* ExecModifyTable(PlanState* state) int resultRelationNum = node->mt_ResultTupleSlots ? list_length(node->mt_ResultTupleSlots) : 1; CHECK_FOR_INTERRUPTS(); - + /* * This should NOT get called during EvalPlanQual; we should have passed a * subplan tree to EvalPlanQual, instead. Use a runtime test not just @@ -3769,9 +3769,13 @@ static void InitMultipleModify(ModifyTableState* node, PlanState* subnode, uint3 if (resultRelationNum == 1) { return; } - + node->mt_ProjInfos = (ProjectionInfo**)palloc0((uint32)resultRelationNum * sizeof(ProjectionInfo*)); - node->targetlists = (List*)ExecInitExpr((Expr*)targetlists, (PlanState*)node); + if (estate->es_is_flt_frame) { + node->targetlists = targetlists; + } else { + node->targetlists = (List*)ExecInitExprByRecursion((Expr*)targetlists, (PlanState*)node); + } int i = 0; forboth (l1, node->targetlists, l2, targetlists) { @@ -3787,8 +3791,13 @@ static void InitMultipleModify(ModifyTableState* node, PlanState* subnode, uint3 TupleDesc tupDesc = ExecTypeFromTL(targetList, hasoid, false); ExecSetSlotDescriptor(slot, tupDesc); node->mt_ResultTupleSlots = lappend(node->mt_ResultTupleSlots, slot); + ProjectionInfo* projInfo = NULL; + if (estate->es_is_flt_frame) { + projInfo = ExecBuildProjectionInfoByFlatten(targetExprList, subnode->ps_ExprContext, slot, (PlanState*)node, NULL); + } else { + projInfo = ExecBuildProjectionInfoByRecursion(targetExprList, subnode->ps_ExprContext, slot, NULL); + } /* Use the targetlist of the subplan to build mt_ProjInfos. It will be use for FetchMultipleModifySlot. */ - ProjectionInfo* projInfo = ExecBuildProjectionInfo(targetExprList, subnode->ps_ExprContext, slot, NULL); node->mt_ProjInfos[i] = projInfo; i++; } @@ -3914,7 +3923,7 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl foreach (l, node->plans) { sub_plan = (Plan*)lfirst(l); sub_plan->rightRefState = node->plan.rightRefState; - + /* * Verify result relation is a valid target for the current operation */ @@ -4057,8 +4066,13 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl foreach(ll, (List*)linitial(node->withCheckOptionLists)) { WithCheckOption* wco = (WithCheckOption*)lfirst(ll); + ExprState* wcoExpr = NULL; if (wco->rtindex == result_rel_info->ri_RangeTableIndex) { - ExprState* wcoExpr = ExecInitExpr((Expr*)wco->qual, mt_state->mt_plans[i]); + if (estate->es_is_flt_frame) { + wcoExpr = ExecInitQualByFlatten((List*)wco->qual, mt_state->mt_plans[i]); + } else { + wcoExpr = ExecInitExprByRecursion((Expr*)wco->qual, mt_state->mt_plans[i]); + } wcoExprs = lappend(wcoExprs, wcoExpr); wcoList = lappend(wcoList, wco); } @@ -4091,18 +4105,15 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl /* Need an econtext too */ econtext = CreateExprContext(estate); mt_state->ps.ps_ExprContext = econtext; - /* * Build a projection for each result rel. */ result_rel_info = mt_state->resultRelInfo; foreach (l, node->returningLists) { List* rlist = (List*)lfirst(l); - List* rliststate = NIL; - rliststate = (List*)ExecInitExpr((Expr*)rlist, &mt_state->ps); result_rel_info->ri_projectReturning = - ExecBuildProjectionInfo(rliststate, econtext, slot, result_rel_info->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mt_state->ps, result_rel_info->ri_RelationDesc->rd_att); } } else { /* @@ -4150,15 +4161,18 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl ExecSetSlotDescriptor(upsertState->us_updateproj, tupDesc); /* build UPDATE SET expression and projection state */ - setexpr = ExecInitExpr((Expr*)node->updateTlist, &mt_state->ps); result_rel_info->ri_updateProj = - ExecBuildProjectionInfo((List*)setexpr, econtext, - upsertState->us_updateproj, result_rel_info->ri_RelationDesc->rd_att); - result_rel_info->ri_updateProj->isUpsertHasRightRef = + ExecBuildProjectionInfo(node->updateTlist, econtext, + upsertState->us_updateproj, &mt_state->ps, result_rel_info->ri_RelationDesc->rd_att); + result_rel_info->ri_updateProj->isUpsertHasRightRef = IS_ENABLE_RIGHT_REF(econtext->rightRefState) && econtext->rightRefState->isUpsertHasRightRef; /* initialize expression state to evaluate update where clause if exists */ if (node->upsertWhere) { - upsertState->us_updateWhere = (List*)ExecInitExpr((Expr*)node->upsertWhere, &mt_state->ps); + if (estate->es_is_flt_frame) { + upsertState->us_updateWhere = (List*)ExecInitQualByFlatten((List*)node->upsertWhere, &mt_state->ps); + } else { + upsertState->us_updateWhere = (List*)ExecInitExprByRecursion((Expr*)node->upsertWhere, &mt_state->ps); + } } } diff --git a/src/gausskernel/runtime/executor/nodeNestloop.cpp b/src/gausskernel/runtime/executor/nodeNestloop.cpp index 70365d860..551d78272 100644 --- a/src/gausskernel/runtime/executor/nodeNestloop.cpp +++ b/src/gausskernel/runtime/executor/nodeNestloop.cpp @@ -108,14 +108,14 @@ static TupleTableSlot* ExecNestLoop(PlanState* state) * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ - if (node->js.ps.ps_TupFromTlist) { + if (node->js.ps.ps_vec_TupFromTlist) { ExprDoneCond is_done; TupleTableSlot* result = ExecProject(node->js.ps.ps_ProjInfo, &is_done); if (is_done == ExprMultipleResult) return result; /* Done with that source tuple... */ - node->js.ps.ps_TupFromTlist = false; + node->js.ps.ps_vec_TupFromTlist = false; } /* @@ -250,7 +250,7 @@ static TupleTableSlot* ExecNestLoop(PlanState* state) TupleTableSlot* result = ExecProject(node->js.ps.ps_ProjInfo, &is_done); if (is_done != ExprEndResult) { - node->js.ps.ps_TupFromTlist = (is_done == ExprMultipleResult); + node->js.ps.ps_vec_TupFromTlist = (is_done == ExprMultipleResult); return result; } } else @@ -298,7 +298,7 @@ static TupleTableSlot* ExecNestLoop(PlanState* state) TupleTableSlot* result = ExecProject(node->js.ps.ps_ProjInfo, &is_done); if (is_done != ExprEndResult) { - node->js.ps.ps_TupFromTlist = (is_done == ExprMultipleResult); + node->js.ps.ps_vec_TupFromTlist = (is_done == ExprMultipleResult); /* * @hdfs * Optimize plan by informational constraint. @@ -353,10 +353,17 @@ NestLoopState* ExecInitNestLoop(NestLoop* node, EState* estate, int eflags) /* * initialize child expressions */ - nlstate->js.ps.targetlist = (List*)ExecInitExpr((Expr*)node->join.plan.targetlist, (PlanState*)nlstate); - nlstate->js.ps.qual = (List*)ExecInitExpr((Expr*)node->join.plan.qual, (PlanState*)nlstate); - nlstate->js.jointype = node->join.jointype; - nlstate->js.joinqual = (List*)ExecInitExpr((Expr*)node->join.joinqual, (PlanState*)nlstate); + if (estate->es_is_flt_frame) { + nlstate->js.ps.qual = (List*)ExecInitQualByFlatten(node->join.plan.qual, (PlanState*)nlstate); + nlstate->js.jointype = node->join.jointype; + nlstate->js.joinqual = (List*)ExecInitQualByFlatten(node->join.joinqual, (PlanState*)nlstate); + Assert(node->join.nulleqqual == NIL); + } else { + nlstate->js.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->join.plan.targetlist, (PlanState*)nlstate); + nlstate->js.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->join.plan.qual, (PlanState*)nlstate); + nlstate->js.jointype = node->join.jointype; + nlstate->js.joinqual = (List*)ExecInitExprByRecursion((Expr*)node->join.joinqual, (PlanState*)nlstate); + } Assert(node->join.nulleqqual == NIL); /* @@ -408,7 +415,7 @@ NestLoopState* ExecInitNestLoop(NestLoop* node, EState* estate, int eflags) /* * finally, wipe the current outer tuple clean. */ - nlstate->js.ps.ps_TupFromTlist = false; + nlstate->js.ps.ps_vec_TupFromTlist = false; nlstate->nl_NeedNewOuter = true; nlstate->nl_MatchedOuter = false; @@ -476,7 +483,7 @@ void ExecReScanNestLoop(NestLoopState* node) * re-scanned from here or you'll get troubles from inner index scans when * outer Vars are used as run-time keys... */ - node->js.ps.ps_TupFromTlist = false; + node->js.ps.ps_vec_TupFromTlist = false; node->nl_NeedNewOuter = true; node->nl_MatchedOuter = false; } diff --git a/src/gausskernel/runtime/executor/nodePartIterator.cpp b/src/gausskernel/runtime/executor/nodePartIterator.cpp index dd33f78ca..50e320ddc 100755 --- a/src/gausskernel/runtime/executor/nodePartIterator.cpp +++ b/src/gausskernel/runtime/executor/nodePartIterator.cpp @@ -55,7 +55,7 @@ PartIteratorState* ExecInitPartIterator(PartIterator* node, EState* estate, int state->ps.qual = NULL; state->ps.righttree = NULL; state->ps.subPlan = NULL; - state->ps.ps_TupFromTlist = false; + state->ps.ps_vec_TupFromTlist = false; state->ps.ps_ProjInfo = NULL; state->currentItr = -1; state->subPartCurrentItr = -1; diff --git a/src/gausskernel/runtime/executor/nodeProjectSet.cpp b/src/gausskernel/runtime/executor/nodeProjectSet.cpp index bb504fc9b..bd8ab7d07 100644 --- a/src/gausskernel/runtime/executor/nodeProjectSet.cpp +++ b/src/gausskernel/runtime/executor/nodeProjectSet.cpp @@ -8,7 +8,9 @@ #include "utils/memutils.h" #include "nodes/execnodes.h" #include "nodes/plannodes.h" +#include "nodes/nodeFuncs.h" #include "vecexecutor/vecnodes.h" +#include "executor/executor.h" static TupleTableSlot *ExecProjectSet(PlanState *state); static TupleTableSlot *ExecProjectSRF(ProjectSetState *node); @@ -17,6 +19,8 @@ ProjectSetState * ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags) { ProjectSetState *state; + ListCell *lc; + int off; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD))); @@ -46,7 +50,11 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags) /* * initialize child expressions */ - state->ps.targetlist = (List *)ExecInitExpr((Expr *)node->plan.targetlist, (PlanState *)state); + if (estate->es_is_flt_frame) { + + } else { + state->ps.targetlist = (List *)ExecInitExpr((Expr *)node->plan.targetlist, (PlanState *)state); + } Assert(node->plan.qual == NIL); /* @@ -64,10 +72,33 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags) */ ExecAssignResultTypeFromTL(&state->ps); - /* Create workspace for per-SRF is-done state */ + /* Create workspace for per-tlist-entry expr state & SRF-is-done state */ state->nelems = list_length(node->plan.targetlist); + state->elems = (Node **)palloc(sizeof(Node *) * state->nelems); state->elemdone = (ExprDoneCond *)palloc(sizeof(ExprDoneCond) * state->nelems); + /* + * Build expressions to evaluate targetlist. We can't use + * ExecBuildProjectionInfo here, since that doesn't deal with SRFs. + * Instead compile each expression separately, using + * ExecInitFunctionResultSet where applicable. + */ + off = 0; + foreach (lc, node->plan.targetlist) { + TargetEntry *te = (TargetEntry *)lfirst(lc); + Expr *expr = te->expr; + + if ((IsA(expr, FuncExpr) && ((FuncExpr *)expr)->funcretset) || + (IsA(expr, OpExpr) && ((OpExpr *)expr)->opretset)) { + state->elems[off] = (Node *)ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext, &state->ps); + } else { + Assert(!expression_returns_set((Node *)expr)); + state->elems[off] = (Node *)ExecInitExprByFlatten(expr, &state->ps); + } + + off++; + } + return state; } @@ -135,20 +166,19 @@ static TupleTableSlot *ExecProjectSet(PlanState *state) } static inline Datum -execMakeExprResult(ExprState *arg, ExprContext *econtext, bool *isnull, +execMakeExprResult(Node *arg, ExprContext *econtext, bool *isnull, ExprDoneCond *isdone, bool *hassrf) { - Datum result; - if (IsA(arg, FuncExprState) && ((FuncExprState *)arg)->funcReturnsSet) { + if (IsA(arg, FuncExprState)) { /* * Evaluate SRF - possibly continuing previously started output. */ - result = ExecMakeFunctionResultSet((FuncExprState *)arg, econtext, isnull, isdone); + result = ExecMakeFunctionResultSet((FuncExprState*)arg, econtext, isnull, isdone); *hassrf = true; } else { /* Non-SRF tlist expression, just evaluate normally. */ - result = ExecEvalExpr(arg, econtext, isnull, NULL); + result = ExecEvalExpr((ExprState *)arg, econtext, isnull); *isdone = ExprSingleResult; } return result; @@ -167,32 +197,44 @@ static TupleTableSlot *ExecProjectSRF(ProjectSetState *node) { TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot; ExprContext *econtext = node->ps.ps_ExprContext; + MemoryContext oldcontext; bool hassrf = false; bool hasresult = false; bool haveDoneSets = false; /* any exhausted set exprs in tlist? */ ExprDoneCond isDone = ExprSingleResult; int argno; - ListCell *lc; + ListCell *lc = NULL; + char* resname = NULL; ExecClearTuple(resultSlot); + /* Call SRFs, as well as plain expressions, in per-tuple context */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + /* * Assume no further tuples are produced unless an ExprMultipleResult is * encountered from a set returning function. */ node->pending_srf_tuples = false; - argno = 0; - foreach (lc, node->ps.targetlist) { - GenericExprState *gstate = (GenericExprState *)lfirst(lc); - TargetEntry *tle = (TargetEntry *)gstate->xprstate.expr; + if (node->ps.plan->targetlist) + lc = list_head(node->ps.plan->targetlist); + for (argno = 0; argno < node->nelems; argno++) { + Node *elem = node->elems[argno]; ExprDoneCond *itemIsDone = &node->elemdone[argno]; Datum *result = &resultSlot->tts_values[argno]; bool *isnull = &resultSlot->tts_isnull[argno]; - ELOG_FIELD_NAME_START(tle->resname); - *result = execMakeExprResult(gstate->arg, econtext, isnull, itemIsDone, &hassrf); + if (lc) { + TargetEntry *te = (TargetEntry *)lfirst(lc); + resname = te->resname; + } + + ELOG_FIELD_NAME_START(resname); + + *result = execMakeExprResult(elem, econtext, isnull, itemIsDone, &hassrf); + ELOG_FIELD_NAME_END; switch (*itemIsDone) { @@ -213,7 +255,9 @@ static TupleTableSlot *ExecProjectSRF(ProjectSetState *node) Assert(false); break; } - argno++; + + lc = lnext(lc); + resname = NULL; } /* ProjectSet should not be used if there's no SRFs */ @@ -224,29 +268,39 @@ static TupleTableSlot *ExecProjectSRF(ProjectSetState *node) /* * all sets are done, so report that tlist expansion is complete. */ + MemoryContextSwitchTo(oldcontext); return NULL; } + if (node->ps.plan->targetlist) + lc = list_head(node->ps.plan->targetlist); + /* * We have some done and some undone sets. Restart the done ones * so that we can deliver a tuple (if possible). */ - argno = 0; - foreach (lc, node->ps.targetlist) { - GenericExprState *gstate = (GenericExprState *)lfirst(lc); - TargetEntry *tle = (TargetEntry *)gstate->xprstate.expr; - + for (argno = 0; argno < node->nelems; argno++) { + Node *elem = node->elems[argno]; ExprDoneCond *itemIsDone = &node->elemdone[argno]; Datum *result = &resultSlot->tts_values[argno]; bool *isnull = &resultSlot->tts_isnull[argno]; + if (*itemIsDone != ExprEndResult) { - argno++; + lc = lnext(lc); + resname = NULL; continue; } + if (lc) { + TargetEntry *te = (TargetEntry *)lfirst(lc); + resname = te->resname; + } + + ELOG_FIELD_NAME_START(resname); + /*restart the done ones*/ - ELOG_FIELD_NAME_START(tle->resname); - *result = execMakeExprResult(gstate->arg, econtext, isnull, itemIsDone, &hassrf); + *result = ExecMakeFunctionResultSet((FuncExprState*)elem, econtext, isnull, itemIsDone); + ELOG_FIELD_NAME_END; Assert(hassrf); @@ -259,6 +313,7 @@ static TupleTableSlot *ExecProjectSRF(ProjectSetState *node) isDone = ExprMultipleResult; node->pending_srf_tuples = true; } + if (*itemIsDone == ExprEndResult) { /* * Oh dear, this item is returning an empty set. Guess @@ -267,7 +322,9 @@ static TupleTableSlot *ExecProjectSRF(ProjectSetState *node) isDone = ExprEndResult; break; } - argno++; + + lc = lnext(lc); + resname = NULL; } /* @@ -279,27 +336,38 @@ static TupleTableSlot *ExecProjectSRF(ProjectSetState *node) */ if (isDone == ExprEndResult) { hasresult = false; - argno = 0; - foreach (lc, node->ps.targetlist) { - GenericExprState *gstate = (GenericExprState *)lfirst(lc); - TargetEntry *tle = (TargetEntry *)gstate->xprstate.expr; + if (node->ps.plan->targetlist) + lc = list_head(node->ps.plan->targetlist); + + for (argno = 0; argno < node->nelems; argno++) { + Node *elem = node->elems[argno]; ExprDoneCond *itemIsDone = &node->elemdone[argno]; Datum *result = &resultSlot->tts_values[argno]; bool *isnull = &resultSlot->tts_isnull[argno]; + if (lc) { + TargetEntry *te = (TargetEntry *)lfirst(lc); + resname = te->resname; + } + + ELOG_FIELD_NAME_START(resname); + while (*itemIsDone == ExprMultipleResult) { - ELOG_FIELD_NAME_START(tle->resname); - *result = execMakeExprResult(gstate->arg, econtext, isnull, itemIsDone, &hassrf); - ELOG_FIELD_NAME_END; + *result = ExecMakeFunctionResultSet((FuncExprState*)elem, econtext, isnull, itemIsDone); /* no need for MakeExpandedObjectReadOnly */ } - argno++; + ELOG_FIELD_NAME_END; + + lc = lnext(lc); + resname = NULL; } } } + MemoryContextSwitchTo(oldcontext); + if (hasresult) { ExecStoreVirtualTuple(resultSlot); return resultSlot; diff --git a/src/gausskernel/runtime/executor/nodeResult.cpp b/src/gausskernel/runtime/executor/nodeResult.cpp index 73cde6049..830b42d79 100644 --- a/src/gausskernel/runtime/executor/nodeResult.cpp +++ b/src/gausskernel/runtime/executor/nodeResult.cpp @@ -113,13 +113,13 @@ static TupleTableSlot* ExecResult(PlanState* state) * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ - if (node->ps.ps_TupFromTlist) { + if (node->ps.ps_vec_TupFromTlist) { result_slot = ExecProject(node->ps.ps_ProjInfo, &is_done); if (is_done == ExprMultipleResult) { return result_slot; } /* Done with that source tuple... */ - node->ps.ps_TupFromTlist = false; + node->ps.ps_vec_TupFromTlist = false; } if (econtext->hasSetResultStore) { @@ -167,7 +167,7 @@ static TupleTableSlot* ExecResult(PlanState* state) result_slot = ExecProject(node->ps.ps_ProjInfo, &is_done); if (is_done != ExprEndResult) { - node->ps.ps_TupFromTlist = (is_done == ExprMultipleResult); + node->ps.ps_vec_TupFromTlist = (is_done == ExprMultipleResult); return result_slot; } } @@ -238,7 +238,7 @@ ResultState* ExecInitResult(BaseResult* node, EState* estate, int eflags) */ ExecAssignExprContext(estate, &resstate->ps); - resstate->ps.ps_TupFromTlist = false; + resstate->ps.ps_vec_TupFromTlist = false; /* * tuple table initialization @@ -248,10 +248,14 @@ ResultState* ExecInitResult(BaseResult* node, EState* estate, int eflags) /* * initialize child expressions */ - resstate->ps.targetlist = (List*)ExecInitExpr((Expr*)node->plan.targetlist, (PlanState*)resstate); - resstate->ps.qual = (List*)ExecInitExpr((Expr*)node->plan.qual, (PlanState*)resstate); - resstate->resconstantqual = ExecInitExpr((Expr*)node->resconstantqual, (PlanState*)resstate); - + if (estate->es_is_flt_frame) { + resstate->ps.qual = (List*)ExecInitQualByFlatten(node->plan.qual, (PlanState*)resstate); + resstate->resconstantqual = ExecInitQualByFlatten((List*)node->resconstantqual, (PlanState*)resstate); + } else { + resstate->ps.targetlist = (List*)ExecInitExpr((Expr*)node->plan.targetlist, (PlanState*)resstate); + resstate->ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->plan.qual, (PlanState*)resstate); + resstate->resconstantqual = ExecInitExprByRecursion((Expr*)node->resconstantqual, (PlanState*)resstate); + } /* * initialize child nodes */ @@ -301,7 +305,7 @@ void ExecEndResult(ResultState* node) void ExecReScanResult(ResultState* node) { node->rs_done = false; - node->ps.ps_TupFromTlist = false; + node->ps.ps_vec_TupFromTlist = false; node->rs_checkqual = (node->resconstantqual == NULL) ? false : true; /* diff --git a/src/gausskernel/runtime/executor/nodeSeqscan.cpp b/src/gausskernel/runtime/executor/nodeSeqscan.cpp index 90a7bd324..276506a85 100644 --- a/src/gausskernel/runtime/executor/nodeSeqscan.cpp +++ b/src/gausskernel/runtime/executor/nodeSeqscan.cpp @@ -448,14 +448,22 @@ RangeScanInRedis reset_scan_qual(Relation curr_heap_rel, ScanState* node, bool i if (u_sess->attr.attr_sql.enable_cluster_resize && RelationInRedistribute(curr_heap_rel)) { List* new_qual = eval_ctid_funcs(curr_heap_rel, node->ps.plan->qual, &node->rangeScanInRedis); if (!node->scanBatchMode) { - node->ps.qual = (List*)ExecInitExpr((Expr*)new_qual, (PlanState*)&node->ps); + if (node->ps.state->es_is_flt_frame) { + node->ps.qual = (List*)ExecInitQualByFlatten(new_qual, (PlanState*)&node->ps); + } else { + node->ps.qual = (List*)ExecInitExprByRecursion((Expr*)new_qual, (PlanState*)&node->ps); + } } else { node->ps.qual = (List*)ExecInitVecExpr((Expr*)new_qual, (PlanState*)&node->ps); } node->ps.qual_is_inited = true; } else if (!node->ps.qual_is_inited) { if (!node->scanBatchMode) { - node->ps.qual = (List*)ExecInitExpr((Expr*)node->ps.plan->qual, (PlanState*)&node->ps); + if (node->ps.state->es_is_flt_frame) { + node->ps.qual = (List*)ExecInitQualByFlatten(node->ps.plan->qual, (PlanState*)&node->ps); + } else { + node->ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->ps.plan->qual, (PlanState*)&node->ps); + } } else { node->ps.qual = (List*)ExecInitVecExpr((Expr*)node->ps.plan->qual, (PlanState*)&node->ps); } @@ -675,7 +683,11 @@ void InitScanRelation(SeqScanState* node, EState* estate, int eflags) current_part_rel = partitionGetRelation(current_relation, currentPart); node->ss_currentPartition = current_part_rel; if (((Scan *)node->ps.plan)->partition_iterator_elimination) { - node->ps.qual = (List*)ExecInitExpr((Expr*)node->ps.plan->qual, (PlanState*)&node->ps); + if (node->ps.state->es_is_flt_frame) { + node->ps.qual = (List*)ExecInitQualByFlatten(node->ps.plan->qual, (PlanState*)&node->ps); + } else { + node->ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->ps.plan->qual, (PlanState*)&node->ps); + } } /* add qual for redis */ @@ -684,7 +696,11 @@ void InitScanRelation(SeqScanState* node, EState* estate, int eflags) current_scan_desc = BeginScanRelation(node, current_part_rel, relfrozenxid64, eflags); } else { node->ss_currentPartition = NULL; - node->ps.qual = (List*)ExecInitExpr((Expr*)node->ps.plan->qual, (PlanState*)&node->ps); + if (node->ps.state->es_is_flt_frame) { + node->ps.qual = (List*)ExecInitQualByFlatten(node->ps.plan->qual, (PlanState*)&node->ps); + } else { + node->ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->ps.plan->qual, (PlanState*)&node->ps); + } } } node->ss_currentRelation = current_relation; @@ -749,7 +765,7 @@ static SeqScanState *ExecInitSeqScanBatchMode(SeqScan *node, SeqScanState* scans * 4. the relaseion is not a hash bucket relation. */ if (node->tablesample || - !CheckColumnsSuportedByBatchMode(scanstate->ps.targetlist, node->plan.qual) || + !CheckColumnsSuportedByBatchMode(scanstate->ps.plan->targetlist, node->plan.qual) || currentRelation->rd_id < FirstNormalObjectId || RELATION_OWN_BUCKET(currentRelation)) { node->scanBatchMode = false; @@ -994,10 +1010,12 @@ SeqScanState* ExecInitSeqScan(SeqScan* node, EState* estate, int eflags) /* * initialize child expressions */ - scanstate->ps.targetlist = (List*)ExecInitExpr((Expr*)node->plan.targetlist, (PlanState*)scanstate); + if(!estate->es_is_flt_frame) { + scanstate->ps.targetlist = (List*)ExecInitExpr((Expr*)node->plan.targetlist, (PlanState*)scanstate); + } if (node->tablesample) { - scanstate->sampleScanInfo.args = (List*)ExecInitExpr((Expr*)tsc->args, (PlanState*)scanstate); + scanstate->sampleScanInfo.args = ExecInitExprList(tsc->args, (PlanState*)scanstate); scanstate->sampleScanInfo.repeatable = ExecInitExpr(tsc->repeatable, (PlanState*)scanstate); scanstate->sampleScanInfo.sampleType = tsc->sampleType; @@ -1028,7 +1046,7 @@ SeqScanState* ExecInitSeqScan(SeqScan* node, EState* estate, int eflags) scanstate->ps.stubType = PST_Scan; } - scanstate->ps.ps_TupFromTlist = false; + scanstate->ps.ps_vec_TupFromTlist = false; /* * Initialize result tuple type and projection info. diff --git a/src/gausskernel/runtime/executor/nodeStartWithOp.cpp b/src/gausskernel/runtime/executor/nodeStartWithOp.cpp index 9988cfe7a..c431151f2 100644 --- a/src/gausskernel/runtime/executor/nodeStartWithOp.cpp +++ b/src/gausskernel/runtime/executor/nodeStartWithOp.cpp @@ -263,8 +263,7 @@ StartWithOpState* ExecInitStartWithOp(StartWithOp* node, EState* estate, int efl */ ExecAssignExprContext(estate, &state->ps); - state->ps.ps_TupFromTlist = false; - + state->ps.ps_vec_TupFromTlist = false; /* * tuple table initialization */ @@ -273,9 +272,13 @@ StartWithOpState* ExecInitStartWithOp(StartWithOp* node, EState* estate, int efl /* * initialize child expressions */ - state->ps.targetlist = (List*)ExecInitExpr((Expr*)node->plan.targetlist, - (PlanState*)state); - state->ps.qual = (List*)ExecInitExpr((Expr*)node->plan.qual, (PlanState*)state); + if (estate->es_is_flt_frame) { + state->ps.qual = (List*)ExecInitQualByFlatten(node->plan.qual, (PlanState*)state); + } else { + state->ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->plan.targetlist, + (PlanState*)state); + state->ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->plan.qual, (PlanState*)state); + } /* * initialize child nodes @@ -1107,6 +1110,7 @@ static List *GetCurrentArrayColArray(const FunctionCallInfo fcinfo, TupleTableSlot *slot = NULL; Var *variable = NULL; *isConstArrayList = false; + List *vars = NIL; ExprContext *econtext = (ExprContext *)fcinfo->swinfo.sw_econtext; ExprState *exprstate = (ExprState *)fcinfo->swinfo.sw_exprstate; @@ -1119,8 +1123,14 @@ static List *GetCurrentArrayColArray(const FunctionCallInfo fcinfo, * specified, so the eval-context's argument only have one argument with *Var* * node ported */ - List *vars = pull_var_clause((Node*)exprstate->expr, - PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); + if (fcinfo->swinfo.sw_is_flt_frame) { + vars = pull_var_clause((Node*)fcinfo->swinfo.sw_exprstate, + PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); + } else { + vars = pull_var_clause((Node*)exprstate->expr, + PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); + } + /* handle case where */ if (vars == NIL) { diff --git a/src/gausskernel/runtime/executor/nodeSubplan.cpp b/src/gausskernel/runtime/executor/nodeSubplan.cpp index 244303671..42e31ee9c 100644 --- a/src/gausskernel/runtime/executor/nodeSubplan.cpp +++ b/src/gausskernel/runtime/executor/nodeSubplan.cpp @@ -30,9 +30,7 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" -static Datum ExecSubPlan(SubPlanState* node, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); -static Datum ExecAlternativeSubPlan( - AlternativeSubPlanState* node, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); +Datum ExecAlternativeSubPlan(AlternativeSubPlanState* node, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecHashSubPlan(SubPlanState* node, ExprContext* econtext, bool* isNull); static Datum ExecScanSubPlan(SubPlanState* node, ExprContext* econtext, bool* isNull); @@ -40,7 +38,7 @@ static Datum ExecScanSubPlan(SubPlanState* node, ExprContext* econtext, bool* is * ExecSubPlan * ---------------------------------------------------------------- */ -static Datum ExecSubPlan(SubPlanState* node, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) +Datum ExecSubPlan(SubPlanState* node, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { SubPlan* sub_plan = (SubPlan*)node->xprstate.expr; EState* estate = node->planstate->state; @@ -550,7 +548,11 @@ void buildSubPlanHash(SubPlanState* node, ExprContext* econtext) * potential for a double free attempt. (XXX possibly no longer needed, * but can't hurt.) */ - (void)ExecClearTuple(node->projRight->pi_slot); + if (node->projRight->pi_state.is_flt_frame) { + (void)ExecClearTuple(node->projRight->pi_state.resultslot); + } else { + (void)ExecClearTuple(node->projRight->pi_slot); + } MemoryContextSwitchTo(oldcontext); } @@ -646,13 +648,14 @@ SubPlanState* ExecInitSubPlan(SubPlan* subplan, PlanState* parent) sstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecSubPlan; sstate->xprstate.expr = (Expr*)subplan; + sstate->xprstate.is_flt_frame = estate->es_is_flt_frame; /* Link the SubPlanState to already-initialized subplan */ sstate->planstate = (PlanState*)list_nth(estate->es_subplanstates, subplan->plan_id - 1); /* Initialize subexpressions */ sstate->testexpr = ExecInitExpr((Expr*)subplan->testexpr, parent); - sstate->args = (List*)ExecInitExpr((Expr*)subplan->args, parent); + sstate->args = ExecInitExprList(subplan->args, parent); /* * initialize my state @@ -744,7 +747,93 @@ SubPlanState* ExecInitSubPlan(SubPlan* subplan, PlanState* parent) * We also extract the combining operators themselves to initialize * the equality and hashing functions for the hash tables. */ - if (IsA(sstate->testexpr->expr, OpExpr)) { + if (estate->es_is_flt_frame) { + if (IsA(subplan->testexpr, OpExpr)) { + /* single combining operator */ + oplist = list_make1(subplan->testexpr); + } else if (and_clause((Node *) subplan->testexpr)) { + /* multiple combining operators */ + Assert(IsA(subplan->testexpr, BoolExpr)); + oplist = castNode(BoolExpr, subplan->testexpr)->args; + } else { + /* shouldn't see anything else in a hashable subplan */ + ereport(ERROR, + (errmodule(MOD_OPT), + errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + errmsg("unrecognized testexpr type: %d in a hash subplan", (int)nodeTag(subplan->testexpr)))); + + oplist = NIL; /* keep compiler quiet */ + } + Assert(list_length(oplist) == ncols); + + lefttlist = righttlist = NIL; + sstate->tab_hash_funcs = (FmgrInfo*)palloc(ncols * sizeof(FmgrInfo)); + sstate->tab_eq_funcs = (FmgrInfo*)palloc(ncols * sizeof(FmgrInfo)); + sstate->lhs_hash_funcs = (FmgrInfo*)palloc(ncols * sizeof(FmgrInfo)); + sstate->cur_eq_funcs = (FmgrInfo*)palloc(ncols * sizeof(FmgrInfo)); + i = 1; + foreach (l, oplist) { + OpExpr* opexpr = (OpExpr*)lfirst(l); + Oid rhs_eq_oper; + Oid left_hashfn; + Oid right_hashfn; + + Assert(IsA(opexpr, OpExpr)); + Assert(list_length(opexpr->args) == 2); + + /* Process lefthand argument */ + Expr* expr = (Expr *) linitial(opexpr->args); + TargetEntry* tle = makeTargetEntry(expr, i, NULL, false); + lefttlist = lappend(lefttlist, tle); + + /* Process righthand argument */ + expr = (Expr *) lsecond(opexpr->args); + tle = makeTargetEntry(expr, i, NULL, false); + righttlist = lappend(righttlist, tle); + + /* Lookup the equality function (potentially cross-type) */ + fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]); + fmgr_info_set_expr((Node*)opexpr, &sstate->cur_eq_funcs[i - 1]); + + /* Look up the equality function for the RHS type */ + if (!get_compatible_hash_operators(opexpr->opno, NULL, &rhs_eq_oper)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not find compatible hash operator for operator %u for subplan", opexpr->opno))); + fmgr_info(get_opcode(rhs_eq_oper), &sstate->tab_eq_funcs[i - 1]); + + /* Lookup the associated hash functions */ + if (!get_op_hash_functions(opexpr->opno, &left_hashfn, &right_hashfn)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not find hash function for hash operator %u for subplan", opexpr->opno))); + fmgr_info(left_hashfn, &sstate->lhs_hash_funcs[i - 1]); + fmgr_info(right_hashfn, &sstate->tab_hash_funcs[i - 1]); + + i++; + } + + /* + * Construct tupdescs, slots and projection nodes for left and right + * sides. The lefthand expressions will be evaluated in the parent + * plan node's exprcontext, which we don't have access to here. + * Fortunately we can just pass NULL for now and fill it in later + * (hack alert!). The righthand expressions will be evaluated in our + * own innerecontext. + */ + // slot contains virtual tuple, so set the default tableAm type to HEAP + tup_desc = ExecTypeFromTL(lefttlist, false, false); + slot = ExecInitExtraTupleSlot(estate); + ExecSetSlotDescriptor(slot, tup_desc); + sstate->projLeft = ExecBuildProjectionInfo(lefttlist, NULL, slot, parent, NULL); + + // slot contains virtual tuple, so set the default tableAm type to HEAP + tup_desc = ExecTypeFromTL(righttlist, false, false); + slot = ExecInitExtraTupleSlot(estate); + ExecSetSlotDescriptor(slot, tup_desc); + sstate->projRight = ExecBuildProjectionInfo(righttlist, sstate->innerecontext, slot, sstate->planstate, NULL); + } else { + if (IsA(sstate->testexpr->expr, OpExpr)) { /* single combining operator */ oplist = list_make1(sstate->testexpr); } else if (and_clause((Node*)sstate->testexpr->expr)) { @@ -837,13 +926,15 @@ SubPlanState* ExecInitSubPlan(SubPlan* subplan, PlanState* parent) tup_desc = ExecTypeFromTL(leftptlist, false, false, TableAmHeap); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tup_desc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, NULL, slot, NULL); + sstate->projLeft = ExecBuildProjectionInfoByRecursion(lefttlist, NULL, slot, NULL); // slot contains virtual tuple, so set the default tableAm type to HEAP tup_desc = ExecTypeFromTL(rightptlist, false, false, TableAmHeap); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tup_desc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, sstate->innerecontext, slot, NULL); + sstate->projRight = ExecBuildProjectionInfoByRecursion(righttlist, sstate->innerecontext, slot, NULL); + } + } return sstate; @@ -1074,15 +1165,34 @@ AlternativeSubPlanState* ExecInitAlternativeSubPlan(AlternativeSubPlan* asplan, SubPlan* subplan2 = NULL; Cost cost1; Cost cost2; + - asstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecAlternativeSubPlan; - asstate->xprstate.expr = (Expr*)asplan; + if (parent->state->es_is_flt_frame) { + ListCell *lc; + asstate->xprstate.expr = (Expr *)asplan; + asstate->xprstate.is_flt_frame = true; + /* + * Initialize subplans. (Can we get away with only initializing the one + * we're going to use?) + */ + foreach (lc, asplan->subplans) { + SubPlan *sp = castNode(SubPlan, lfirst(lc)); + SubPlanState *sps = ExecInitSubPlan(sp, parent); - /* - * Initialize subplans. (Can we get away with only initializing the one - * we're going to use?) - */ - asstate->subplans = (List*)ExecInitExpr((Expr*)asplan->subplans, parent); + asstate->subplans = lappend(asstate->subplans, sps); + parent->subPlan = lappend(parent->subPlan, sps); + } + } else { + asstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecAlternativeSubPlan; + asstate->xprstate.expr = (Expr *)asplan; + asstate->xprstate.is_flt_frame = false; + + /* + * Initialize subplans. (Can we get away with only initializing the one + * we're going to use?) + */ + asstate->subplans = (List *)ExecInitExprByRecursion((Expr *)asplan->subplans, parent); + } /* * Select the one to be used. For this, we need an estimate of the number @@ -1120,7 +1230,7 @@ AlternativeSubPlanState* ExecInitAlternativeSubPlan(AlternativeSubPlan* asplan, * Note: in future we might consider changing to different subplans on the * fly, in case the original rowcount estimate turns out to be way off. */ -static Datum ExecAlternativeSubPlan( +Datum ExecAlternativeSubPlan( AlternativeSubPlanState* node, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { /* Just pass control to the active subplan */ diff --git a/src/gausskernel/runtime/executor/nodeSubqueryscan.cpp b/src/gausskernel/runtime/executor/nodeSubqueryscan.cpp index 05e80403d..a6dedae5d 100644 --- a/src/gausskernel/runtime/executor/nodeSubqueryscan.cpp +++ b/src/gausskernel/runtime/executor/nodeSubqueryscan.cpp @@ -115,8 +115,12 @@ SubqueryScanState* ExecInitSubqueryScan(SubqueryScan* node, EState* estate, int /* * initialize child expressions */ - sub_query_state->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->scan.plan.targetlist, (PlanState*)sub_query_state); - sub_query_state->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->scan.plan.qual, (PlanState*)sub_query_state); + if (estate->es_is_flt_frame) { + sub_query_state->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)sub_query_state); + } else { + sub_query_state->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)sub_query_state); + sub_query_state->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)sub_query_state); + } /* * tuple table initialization @@ -129,7 +133,7 @@ SubqueryScanState* ExecInitSubqueryScan(SubqueryScan* node, EState* estate, int */ sub_query_state->subplan = ExecInitNode(node->subplan, estate, eflags); - sub_query_state->ss.ps.ps_TupFromTlist = false; + sub_query_state->ss.ps.ps_vec_TupFromTlist = false; /* * Initialize scan tuple type (needed by ExecAssignScanProjectionInfo) diff --git a/src/gausskernel/runtime/executor/nodeTidscan.cpp b/src/gausskernel/runtime/executor/nodeTidscan.cpp index 8f8721735..e4db91104 100644 --- a/src/gausskernel/runtime/executor/nodeTidscan.cpp +++ b/src/gausskernel/runtime/executor/nodeTidscan.cpp @@ -45,21 +45,218 @@ ((Var*)(node))->varlevelsup == 0) static TupleTableSlot* ExecTidScan(PlanState* state); -static void TidListCreate(TidScanState* tidstate, bool isBucket); +static void TidListCreateByRecursion(TidScanState* tidstate, bool isBucket); +/* one element in tss_tidexprs */ +typedef struct TidExpr +{ + ExprState *exprstate; /* ExprState for a TID-yielding subexpr */ + bool isarray; /* if true, it yields tid[] not just tid */ + CurrentOfExpr *cexpr; /* alternatively, we can have CURRENT OF */ +} TidExpr; + +static void TidExprListInit(TidScanState *tidstate); +static void TidListCreateByFlatten(TidScanState* tidstate, bool isBucket); static int ItemptrComparator(const void* a, const void* b); static TupleTableSlot* TidNext(TidScanState* node); static void ExecInitNextPartitionForTidScan(TidScanState* node); static void ExecInitPartitionForTidScan(TidScanState* tidstate, EState* estate); +/* + * Extract the qual subexpressions that yield TIDs to search for, + * and compile them into ExprStates if they're ordinary expressions. + * + * CURRENT OF is a special case that we can't compile usefully; + * just drop it into the TidExpr list as-is. + */ +static void +TidExprListInit(TidScanState *tidstate) +{ + TidScan *node = (TidScan *) tidstate->ss.ps.plan; + ListCell *l; + + tidstate->tss_tidexprs = NIL; + tidstate->tss_isCurrentOf = false; + + foreach(l, node->tidquals) + { + Expr *expr = (Expr *) lfirst(l); + TidExpr *tidexpr = (TidExpr *) palloc0(sizeof(TidExpr)); + + if (is_opclause(expr)) + { + Node *arg1; + Node *arg2; + + arg1 = get_leftop(expr); + arg2 = get_rightop(expr); + if (IsCTIDVar(arg1)) + tidexpr->exprstate = ExecInitExpr((Expr *) arg2, + &tidstate->ss.ps); + else if (IsCTIDVar(arg2)) + tidexpr->exprstate = ExecInitExpr((Expr *) arg1, + &tidstate->ss.ps); + else + elog(ERROR, "could not identify CTID variable"); + tidexpr->isarray = false; + } + else if (expr && IsA(expr, ScalarArrayOpExpr)) + { + ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr; + + Assert(IsCTIDVar(linitial(saex->args))); + tidexpr->exprstate = ExecInitExpr((Expr*)lsecond(saex->args), + &tidstate->ss.ps); + tidexpr->isarray = true; + } + else if (expr && IsA(expr, CurrentOfExpr)) + { + CurrentOfExpr *cexpr = (CurrentOfExpr *) expr; + + tidexpr->cexpr = cexpr; + tidstate->tss_isCurrentOf = true; + } + else + elog(ERROR, "could not identify CTID expression"); + + tidstate->tss_tidexprs = lappend(tidstate->tss_tidexprs, tidexpr); + } + + /* CurrentOfExpr could never appear OR'd with something else */ + Assert(list_length(tidstate->tss_tidexprs) == 1 || + !tidstate->tss_isCurrentOf); +} + /* * Compute the list of TIDs to be visited, by evaluating the expressions * for them. * * (The result is actually an array, not a list.) */ -static void TidListCreate(TidScanState* tidstate, bool isBucket) +static void TidListCreateByFlatten(TidScanState* tidstate, bool isBucket) { - List* eval_list = tidstate->tss_tidquals; + ExprContext* econtext = tidstate->ss.ps.ps_ExprContext; + BlockNumber nblocks; + ItemPointerData* tid_list = NULL; + int num_alloc_tids; + int num_tids; + ListCell* l = NULL; + + /* + * We silently discard any TIDs that are out of range at the time of scan + * start. (Since we hold at least AccessShareLock on the table, it won't + * be possible for someone to truncate away the blocks we intend to + * visit.) + */ + if (isBucket) { + nblocks = MaxBlockNumber; + } else { + if (tidstate->ss.isPartTbl) { + nblocks = RelationGetNumberOfBlocks(tidstate->ss.ss_currentPartition); + } else { + nblocks = RelationGetNumberOfBlocks(tidstate->ss.ss_currentRelation); + } + } + + /* + * We initialize the array with enough slots for the case that all quals + * are simple OpExprs or CurrentOfExprs. If there are any + * ScalarArrayOpExprs, we may have to enlarge the array. + */ + num_alloc_tids = list_length(tidstate->tss_tidexprs); + tid_list = (ItemPointerData*)palloc(num_alloc_tids * sizeof(ItemPointerData)); + num_tids = 0; + + foreach(l, tidstate->tss_tidexprs) { + TidExpr *tidexpr = (TidExpr *) lfirst(l); + ItemPointer itemptr; + bool is_null = false; + + if (tidexpr->exprstate && !tidexpr->isarray) { + itemptr = (ItemPointer)DatumGetPointer(ExecEvalExprSwitchContext(tidexpr->exprstate, econtext, &is_null)); + if (!is_null && ItemPointerIsValid(itemptr) && ItemPointerGetBlockNumber(itemptr) < nblocks) { + if (num_tids >= num_alloc_tids) { + num_alloc_tids *= 2; + tid_list = (ItemPointerData*)repalloc(tid_list, num_alloc_tids * sizeof(ItemPointerData)); + } + tid_list[num_tids++] = *itemptr; + } + } else if (tidexpr->exprstate && tidexpr->isarray) { + Datum arraydatum; + ArrayType* itemarray = NULL; + Datum* ipdatums = NULL; + bool* ipnulls = NULL; + int ndatums; + int i; + + arraydatum = ExecEvalExprSwitchContext(tidexpr->exprstate, econtext, &is_null); + if (is_null) + continue; + itemarray = DatumGetArrayTypeP(arraydatum); + deconstruct_array(itemarray, TIDOID, SizeOfIptrData, false, 's', &ipdatums, &ipnulls, &ndatums); + if (num_tids + ndatums > num_alloc_tids) { + num_alloc_tids = num_tids + ndatums; + tid_list = (ItemPointerData*)repalloc(tid_list, num_alloc_tids * sizeof(ItemPointerData)); + } + for (i = 0; i < ndatums; i++) { + if (!ipnulls[i]) { + itemptr = (ItemPointer)DatumGetPointer(ipdatums[i]); + if (ItemPointerIsValid(itemptr) && ItemPointerGetBlockNumber(itemptr) < nblocks) + tid_list[num_tids++] = *itemptr; + } + } + pfree_ext(ipdatums); + pfree_ext(ipnulls); + } else { + ItemPointerData cursor_tid; + + Assert(tidexpr->cexpr); + if (execCurrentOf(tidexpr->cexpr, econtext, tidstate->ss.ss_currentRelation, &cursor_tid, + &(tidstate->tss_CurrentOf_CurrentPartition))) { + if (num_tids >= num_alloc_tids) { + num_alloc_tids *= 2; + tid_list = (ItemPointerData*)repalloc(tid_list, num_alloc_tids * sizeof(ItemPointerData)); + } + tid_list[num_tids++] = cursor_tid; + } + } + } + + /* + * Sort the array of TIDs into order, and eliminate duplicates. + * Eliminating duplicates is necessary since we want OR semantics across + * the list. Sorting makes it easier to detect duplicates, and as a bonus + * ensures that we will visit the heap in the most efficient way. + */ + if (num_tids > 1) { + int last_tid; + int i; + + /* CurrentOfExpr could never appear OR'd with something else */ + Assert(!tidstate->tss_isCurrentOf); + + qsort((void*)tid_list, num_tids, sizeof(ItemPointerData), ItemptrComparator); + last_tid = 0; + for (i = 1; i < num_tids; i++) { + if (!ItemPointerEquals(&tid_list[last_tid], &tid_list[i])) + tid_list[++last_tid] = tid_list[i]; + } + num_tids = last_tid + 1; + } + + tidstate->tss_TidList = tid_list; + tidstate->tss_NumTids = num_tids; + tidstate->tss_TidPtr = -1; +} + +/* + * Compute the list of TIDs to be visited, by evaluating the expressions + * for them. + * + * (The result is actually an array, not a list.) + */ +static void TidListCreateByRecursion(TidScanState* tidstate, bool isBucket) +{ + List* eval_list = tidstate->tss_tidexprs; ExprContext* econtext = tidstate->ss.ps.ps_ExprContext; BlockNumber nblocks; ItemPointerData* tid_list = NULL; @@ -154,7 +351,7 @@ static void TidListCreate(TidScanState* tidstate, bool isBucket) ItemPointerData cursor_tid; if (execCurrentOf(cexpr, econtext, tidstate->ss.ss_currentRelation, &cursor_tid, - &(tidstate->tss_CurrentOf_CurrentPartition))) { + &(tidstate->tss_CurrentOf_CurrentPartition))) { if (num_tids >= num_alloc_tids) { num_alloc_tids *= 2; tid_list = (ItemPointerData*)repalloc(tid_list, num_alloc_tids * sizeof(ItemPointerData)); @@ -164,8 +361,8 @@ static void TidListCreate(TidScanState* tidstate, bool isBucket) } } else ereport(ERROR, - (errcode(ERRCODE_DATA_EXCEPTION), - errmsg("could not identify CTID expression, %s", (expr == NULL) ? "expr is NULL" : ""))); + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("could not identify CTID expression, %s", (expr == NULL) ? "expr is NULL" : ""))); } /* @@ -411,8 +608,13 @@ static TupleTableSlot* TidNext(TidScanState* node) /* * First time through, compute the list of TIDs to be visited */ - if (node->tss_TidList == NULL) - TidListCreate(node, RELATION_CREATE_BUCKET(heap_relation)); + if (node->tss_TidList == NULL) { + if (estate->es_is_flt_frame) { + TidListCreateByFlatten(node, RELATION_CREATE_BUCKET(heap_relation)); + } else { + TidListCreateByRecursion(node, RELATION_CREATE_BUCKET(heap_relation)); + } + } if (node->tss_isCurrentOf && node->ss.isPartTbl) { if (PointerIsValid(node->tss_CurrentOf_CurrentPartition)) { @@ -445,8 +647,8 @@ static bool TidRecheck(TidScanState* node, TupleTableSlot* slot) ExprContext* econtext = node->ss.ps.ps_ExprContext; econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (node->tss_tidquals != NIL) { - return ExecQual(node->tss_tidquals, econtext, false); + if (node->tss_tidexprs != NIL) { + return ExecQual(node->tss_tidexprs, econtext); } else return true; } @@ -588,7 +790,7 @@ TidScanState* ExecInitTidScan(TidScan* node, EState* estate, int eflags) tidstate->ss.currentSlot = 0; tidstate->ss.partScanDirection = node->scan.partScanDirection; tidstate->ss.ps.ExecProcNode = ExecTidScan; - + tidstate->tss_htup.tupTableType = HEAP_TUPLE; /* * Miscellaneous initialization @@ -597,15 +799,17 @@ TidScanState* ExecInitTidScan(TidScan* node, EState* estate, int eflags) */ ExecAssignExprContext(estate, &tidstate->ss.ps); - tidstate->ss.ps.ps_TupFromTlist = false; - /* * initialize child expressions */ - tidstate->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->scan.plan.targetlist, (PlanState*)tidstate); - tidstate->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->scan.plan.qual, (PlanState*)tidstate); - - tidstate->tss_tidquals = (List*)ExecInitExpr((Expr*)node->tidquals, (PlanState*)tidstate); + if (estate->es_is_flt_frame) { + tidstate->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)tidstate); + TidExprListInit(tidstate); + } else { + tidstate->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)tidstate); + tidstate->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)tidstate); + tidstate->tss_tidexprs = (List*)ExecInitExprByRecursion((Expr*)node->tidquals, (PlanState*)tidstate); + } /* * mark tid list as not computed yet diff --git a/src/gausskernel/runtime/executor/nodeTrainModel.cpp b/src/gausskernel/runtime/executor/nodeTrainModel.cpp index af2d72f96..9ff25732a 100644 --- a/src/gausskernel/runtime/executor/nodeTrainModel.cpp +++ b/src/gausskernel/runtime/executor/nodeTrainModel.cpp @@ -102,7 +102,11 @@ TrainModelState* ExecInitTrainModel(TrainModel* pnode, EState* estate, int eflag // initialize child expressions ExecAssignExprContext(estate, &pstate->ss.ps); - pstate->ss.ps.targetlist = (List *)ExecInitExpr((Expr *)pnode->plan.targetlist, (PlanState *)pstate); + if (!estate->es_is_flt_frame) + { + pstate->ss.ps.targetlist = (List *)ExecInitExprByRecursion((Expr *)pnode->plan.targetlist, (PlanState *)pstate); + } + // initialize outer plan PlanState *outer_plan_state = ExecInitNode(outer_plan, estate, eflags); @@ -112,8 +116,8 @@ TrainModelState* ExecInitTrainModel(TrainModel* pnode, EState* estate, int eflag ExecAssignScanTypeFromOuterPlan(&pstate->ss); // input tuples ExecAssignResultTypeFromTL(&pstate->ss.ps); // result tuple ExecAssignProjectionInfo(&pstate->ss.ps, NULL); - pstate->ss.ps.ps_TupFromTlist = false; - + pstate->ss.ps.ps_vec_TupFromTlist = false; + // Input tuple initialization TupleDesc tupdesc = ExecGetResultType(outer_plan_state); pstate->tuple.ncolumns = tupdesc->natts; @@ -137,7 +141,7 @@ TrainModelState* ExecInitTrainModel(TrainModel* pnode, EState* estate, int eflag BlessTupleDesc(tup_desc_out); ExecAssignResultType(&pstate->ss.ps, tup_desc_out); ExecAssignProjectionInfo(&pstate->ss.ps, nullptr); - pstate->ss.ps.ps_TupFromTlist = false; + pstate->ss.ps.ps_vec_TupFromTlist = false; pstate->ss.ps.ps_ProjInfo = nullptr; return pstate; diff --git a/src/gausskernel/runtime/executor/nodeValuesscan.cpp b/src/gausskernel/runtime/executor/nodeValuesscan.cpp index a1676bab4..be3a34e70 100644 --- a/src/gausskernel/runtime/executor/nodeValuesscan.cpp +++ b/src/gausskernel/runtime/executor/nodeValuesscan.cpp @@ -106,7 +106,7 @@ static TupleTableSlot* ValuesNext(ValuesScanState* node) * VALUES list should be InitPlans). */ if (expr_state_list == NIL) { - expr_state_list = (List*)ExecInitExpr((Expr*)exprlist, NULL); + expr_state_list = ExecInitExprList(exprlist, NULL); } /* parser should have checked all sublists are the same length */ @@ -129,12 +129,12 @@ static TupleTableSlot* ValuesNext(ValuesScanState* node) SortTargetListAsArray(refState, expr_state_list, targetArr); InitOutputValues(refState, targetArr, values, is_null, targetCount, hasExecs); - + resind = 0; foreach (lc, expr_state_list) { ExprState* exprState = (ExprState*)lfirst(lc); - values[resind] = ExecEvalExpr(exprState, econtext, &is_null[resind], NULL); + values[resind] = ExecEvalExpr(exprState, econtext, &is_null[resind]); if (IS_ENABLE_RIGHT_REF(refState) && resind < refState->colCnt) { hasExecs[resind] = true; } @@ -232,9 +232,12 @@ ValuesScanState* ExecInitValuesScan(ValuesScan* node, EState* estate, int eflags /* * initialize child expressions */ - scan_state->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->scan.plan.targetlist, (PlanState*)scan_state); - scan_state->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->scan.plan.qual, (PlanState*)scan_state); - + if (estate->es_is_flt_frame) { + scan_state->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)scan_state); + } else { + scan_state->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)scan_state); + scan_state->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)scan_state); + } /* * get info about values list * value lists scan, no relation is involved, default tableAm type is set to HEAP. @@ -282,7 +285,7 @@ ValuesScanState* ExecInitValuesScan(ValuesScan* node, EState* estate, int eflags i++; } - scan_state->ss.ps.ps_TupFromTlist = false; + scan_state->ss.ps.ps_vec_TupFromTlist = false; /* * Initialize result tuple type and projection info. diff --git a/src/gausskernel/runtime/executor/nodeWindowAgg.cpp b/src/gausskernel/runtime/executor/nodeWindowAgg.cpp index d534761cf..8d46ac658 100644 --- a/src/gausskernel/runtime/executor/nodeWindowAgg.cpp +++ b/src/gausskernel/runtime/executor/nodeWindowAgg.cpp @@ -126,7 +126,7 @@ static void advance_windowaggregate( foreach (arg, wfuncstate->args) { ExprState* arg_state = (ExprState*)lfirst(arg); - fcinfo->arg[i] = ExecEvalExpr(arg_state, econtext, &fcinfo->argnull[i], NULL); + fcinfo->arg[i] = ExecEvalExpr(arg_state, econtext, &fcinfo->argnull[i]); fcinfo->argTypes[i] = arg_state->resultType; i++; } @@ -971,12 +971,12 @@ static TupleTableSlot* ExecWindowAgg(PlanState* state) * output tuple (because there is a function-returning-set in the * projection expressions). If so, try to project another one. */ - if (winstate->ss.ps.ps_TupFromTlist) { + if (winstate->ss.ps.ps_vec_TupFromTlist) { TupleTableSlot* res = ExecProject(winstate->ss.ps.ps_ProjInfo, &is_done); if (is_done == ExprMultipleResult) return res; /* Done with that source tuple... */ - winstate->ss.ps.ps_TupFromTlist = false; + winstate->ss.ps.ps_vec_TupFromTlist = false; } /* @@ -1128,7 +1128,7 @@ restart: goto restart; } - winstate->ss.ps.ps_TupFromTlist = (is_done == ExprMultipleResult); + winstate->ss.ps.ps_vec_TupFromTlist = (is_done == ExprMultipleResult); return result; } @@ -1201,7 +1201,10 @@ WindowAggState* ExecInitWindowAgg(WindowAgg* node, EState* estate, int eflags) winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate); winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate); - winstate->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->plan.targetlist, (PlanState*)winstate); + if (!estate->es_is_flt_frame) + { + winstate->ss.ps.targetlist = (List *)ExecInitExprByRecursion((Expr *)node->plan.targetlist, (PlanState *)winstate); + } /* * WindowAgg nodes never have quals, since they can only occur at the @@ -1235,7 +1238,7 @@ WindowAggState* ExecInitWindowAgg(WindowAgg* node, EState* estate, int eflags) ExecAssignProjectionInfo(&winstate->ss.ps, NULL); - winstate->ss.ps.ps_TupFromTlist = false; + winstate->ss.ps.ps_vec_TupFromTlist = false; /* Set up data for comparing tuples */ if (node->partNumCols > 0) @@ -1407,7 +1410,7 @@ void ExecReScanWindowAgg(WindowAggState* node) node->all_done = false; - node->ss.ps.ps_TupFromTlist = false; + node->ss.ps.ps_vec_TupFromTlist = false; node->all_first = true; /* release tuplestore et al */ diff --git a/src/gausskernel/runtime/executor/nodeWorktablescan.cpp b/src/gausskernel/runtime/executor/nodeWorktablescan.cpp index bfee88c8d..a2e528fcc 100755 --- a/src/gausskernel/runtime/executor/nodeWorktablescan.cpp +++ b/src/gausskernel/runtime/executor/nodeWorktablescan.cpp @@ -248,8 +248,12 @@ WorkTableScanState* ExecInitWorkTableScan(WorkTableScan* node, EState* estate, i /* * initialize child expressions */ - scan_state->ss.ps.targetlist = (List*)ExecInitExpr((Expr*)node->scan.plan.targetlist, (PlanState*)scan_state); - scan_state->ss.ps.qual = (List*)ExecInitExpr((Expr*)node->scan.plan.qual, (PlanState*)scan_state); + if (estate->es_is_flt_frame) { + scan_state->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)scan_state); + } else { + scan_state->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)scan_state); + scan_state->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)scan_state); + } /* * tuple table initialization @@ -262,7 +266,7 @@ WorkTableScanState* ExecInitWorkTableScan(WorkTableScan* node, EState* estate, i */ ExecAssignResultTypeFromTL(&scan_state->ss.ps); - scan_state->ss.ps.ps_TupFromTlist = false; + scan_state->ss.ps.ps_vec_TupFromTlist = false; return scan_state; } diff --git a/src/gausskernel/runtime/opfusion/opfusion.cpp b/src/gausskernel/runtime/opfusion/opfusion.cpp index 20a926b30..f1b8c982d 100644 --- a/src/gausskernel/runtime/opfusion/opfusion.cpp +++ b/src/gausskernel/runtime/opfusion/opfusion.cpp @@ -1364,6 +1364,7 @@ EState* CreateExecutorStateForOpfusion(MemoryContext nodeCxt, MemoryContext quer estate->es_query_cxt = queryCxt; estate->es_const_query_cxt = queryCxt; /* set to tmpCxt, will be reset at each query */ estate->es_range_table = NIL; + estate->es_is_flt_frame = (u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp == 1); ResetOpfusionExecutorState(estate); /* diff --git a/src/gausskernel/runtime/opfusion/opfusion_insert.cpp b/src/gausskernel/runtime/opfusion/opfusion_insert.cpp index d72847e18..68feeb00b 100644 --- a/src/gausskernel/runtime/opfusion/opfusion_insert.cpp +++ b/src/gausskernel/runtime/opfusion/opfusion_insert.cpp @@ -439,7 +439,12 @@ bool InsertFusion::execute(long max_rows, char* completionTag) foreach(ll, wcoList) { WithCheckOption* wco = (WithCheckOption*)lfirst(ll); - ExprState* wcoExpr = ExecInitExpr((Expr*)wco->qual, ps); + ExprState* wcoExpr = NULL; + if (ps->state->es_is_flt_frame) { + wcoExpr = ExecInitQualByFlatten((List*)wco->qual, ps); + } else { + wcoExpr = ExecInitExprByRecursion((Expr*)wco->qual, ps); + } wcoExprs = lappend(wcoExprs, wcoExpr); } diff --git a/src/gausskernel/runtime/opfusion/opfusion_update.cpp b/src/gausskernel/runtime/opfusion/opfusion_update.cpp index 0eb260c48..3d23a70f1 100644 --- a/src/gausskernel/runtime/opfusion/opfusion_update.cpp +++ b/src/gausskernel/runtime/opfusion/opfusion_update.cpp @@ -615,7 +615,12 @@ bool UpdateFusion::execute(long max_rows, char *completionTag) foreach(ll, wcoList) { WithCheckOption* wco = (WithCheckOption*)lfirst(ll); - ExprState* wcoExpr = ExecInitExpr((Expr*)wco->qual, ps); + ExprState* wcoExpr = NULL; + if (ps->state->es_is_flt_frame) { + wcoExpr = ExecInitQualByFlatten((List*)wco->qual, ps); + } else { + wcoExpr = ExecInitExprByRecursion((Expr*)wco->qual, ps); + } wcoExprs = lappend(wcoExprs, wcoExpr); } diff --git a/src/gausskernel/runtime/vecexecutor/vecexpression.cpp b/src/gausskernel/runtime/vecexecutor/vecexpression.cpp index e74f03346..46fe14c89 100644 --- a/src/gausskernel/runtime/vecexecutor/vecexpression.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecexpression.cpp @@ -2136,7 +2136,7 @@ static ScalarVector* ExecMakeVecFunctionResultWithSets( hasSetArg = (argDone != ExprSingleResult); } else { /* Re-use callinfo from previous evaluation */ - hasSetArg = fcache->setHasSetArg; + hasSetArg = fcache->vec_setHasSetArg; /* Reset flag (we may set it again below) */ fcache->setArgsValid = false; } @@ -2200,7 +2200,7 @@ static ScalarVector* ExecMakeVecFunctionResultWithSets( * on the next call. */ if (fcache->func.fn_retset && *isDone == ExprMultipleResult) { - fcache->setHasSetArg = hasSetArg; + fcache->vec_setHasSetArg = hasSetArg; fcache->setArgsValid = true; } @@ -3659,7 +3659,7 @@ VectorBatch* ExecVecProject(ProjectionInfo* projInfo, bool selReSet, ExprDoneCon econtext, pProjBatch, projInfo->pi_setFuncBatch, - projInfo->pi_itemIsDone, + projInfo->pi_vec_itemIsDone, isDone); } else { ExecVecTargetList(projInfo->pi_targetlist, econtext, pProjBatch); diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp index 8c3f2600e..ec8524140 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecagg.cpp @@ -224,7 +224,7 @@ VecAggState* ExecInitVecAggregation(VecAgg* node, EState* estate, int eflags) aggstate->ss.ps.ps_ResultTupleSlot, NULL); - aggstate->ss.ps.ps_TupFromTlist = false; + aggstate->ss.ps.ps_vec_TupFromTlist = false; /* * get the count of aggregates in targetlist and quals @@ -763,7 +763,7 @@ VecAggState* ExecInitVecAggregation(VecAgg* node, EState* estate, int eflags) */ VectorBatch* ExecVecAggregation(VecAggState* node) { - Assert(!node->ss.ps.ps_TupFromTlist); + Assert(!node->ss.ps.ps_vec_TupFromTlist); /* * just for cooperation analysis. do nothing if is_dummy is true. @@ -1323,14 +1323,14 @@ VectorBatch* BaseAggRunner::ProducerBatch() /* To AP functio, set column to NULL when group columns has not include it. */ prepare_projection_batch(); - if (list_length(m_runtime->ss.ps.qual) != 0) { + if (list_length((List*)m_runtime->ss.ps.qual) != 0) { ScalarVector* p_vector = NULL; econtext = m_runtime->ss.ps.ps_ExprContext; econtext->ecxt_scanbatch = m_scanBatch; econtext->ecxt_aggbatch = m_scanBatch; econtext->ecxt_outerbatch = m_outerBatch; - p_vector = ExecVecQual(m_runtime->ss.ps.qual, econtext, false); + p_vector = ExecVecQual((List*)m_runtime->ss.ps.qual, econtext, false); if (p_vector == NULL) { return NULL; diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/veccstore.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/veccstore.cpp index 6297612aa..681a49d07 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/veccstore.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/veccstore.cpp @@ -187,7 +187,7 @@ VectorBatch* ApplyProjectionAndFilter(CStoreScanState* node, VectorBatch* p_scan VECCSTORE_SCAN_TRACE_START(node, CSTORE_PROJECT); - qual = node->ps.qual; + qual = (List*)node->ps.qual; econtext = node->ps.ps_ExprContext; p_out_batch = node->m_pCurrentBatch; simple_map = node->m_fSimpleMap; @@ -332,14 +332,14 @@ VectorBatch* ExecCStoreScan(CStoreScanState* node) /* * for function-returning-set. */ - if (node->ps.ps_TupFromTlist) { + if (node->ps.ps_vec_TupFromTlist) { Assert(node->ps.ps_ProjInfo); p_out_batch = ExecVecProject(node->ps.ps_ProjInfo, true, &done); if (p_out_batch->m_rows > 0) { return p_out_batch; } - node->ps.ps_TupFromTlist = false; + node->ps.ps_vec_TupFromTlist = false; } restart: @@ -374,7 +374,7 @@ restart: p_out_batch = ApplyProjectionAndFilter(node, p_scan_batch, &done); if (done != ExprEndResult) { - node->ps.ps_TupFromTlist = (done == ExprMultipleResult); + node->ps.ps_vec_TupFromTlist = (done == ExprMultipleResult); } /* Response to the stop query flag. */ @@ -656,8 +656,8 @@ CStoreScanState* ExecInitCStoreScan( scan_stat->ps.targetlist = (List*)ExecInitVecExpr((Expr*)node->plan.targetlist, (PlanState*)scan_stat); if (node->tablesample) { - scan_stat->sampleScanInfo.args = (List*)ExecInitExpr((Expr*)tsc->args, (PlanState*)scan_stat); - scan_stat->sampleScanInfo.repeatable = ExecInitExpr(tsc->repeatable, (PlanState*)scan_stat); + scan_stat->sampleScanInfo.args = (List*)ExecInitExprByRecursion((Expr*)tsc->args, (PlanState*)scan_stat); + scan_stat->sampleScanInfo.repeatable = ExecInitExprByRecursion(tsc->repeatable, (PlanState*)scan_stat); scan_stat->sampleScanInfo.sampleType = tsc->sampleType; @@ -679,7 +679,7 @@ CStoreScanState* ExecInitCStoreScan( * initialize scan relation */ InitCStoreRelation(scan_stat, estate, idx_flag, parent_heap_rel); - scan_stat->ps.ps_TupFromTlist = false; + scan_stat->ps.ps_vec_TupFromTlist = false; #ifdef ENABLE_LLVM_COMPILE /* @@ -702,7 +702,7 @@ CStoreScanState* ExecInitCStoreScan( CodeGenThreadObjectReady() && CodeGenPassThreshold(((Plan*)node)->plan_rows, estate->es_plannedstmt->num_nodes, ((Plan*)node)->dop); if (consider_codegen) { - jitted_vecqual = dorado::VecExprCodeGen::QualCodeGen(scan_stat->ps.qual, (PlanState*)scan_stat); + jitted_vecqual = dorado::VecExprCodeGen::QualCodeGen((List*)scan_stat->ps.qual, (PlanState*)scan_stat); if (jitted_vecqual != NULL) llvm_code_gen->addFunctionToMCJit(jitted_vecqual, reinterpret_cast(&(scan_stat->jitted_vecqual))); } diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/veccstoreindexheapscan.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/veccstoreindexheapscan.cpp index 33b3b7ef4..5b8bab95e 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/veccstoreindexheapscan.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/veccstoreindexheapscan.cpp @@ -88,7 +88,12 @@ CStoreIndexHeapScanState* ExecInitCstoreIndexHeapScan(CStoreIndexHeapScan* node, CIHSState->ps.state = estate; CIHSState->ps.vectorized = true; CIHSState->ps.type = T_CStoreIndexHeapScanState; - CIHSState->m_deltaQual = (List*)ExecInitExpr((Expr*)node->bitmapqualorig, (PlanState*)&CIHSState->ps); + + if (estate->es_is_flt_frame) { + CIHSState->m_deltaQual = (List*)ExecInitQualByFlatten(node->bitmapqualorig, (PlanState*)&CIHSState->ps); + } else { + CIHSState->m_deltaQual = (List*)ExecInitExprByRecursion((Expr*)node->bitmapqualorig, (PlanState*)&CIHSState->ps); + } outerPlanState(CIHSState) = ExecInitNode(outerPlan(node), estate, eflags); @@ -117,14 +122,14 @@ VectorBatch* ExecCstoreIndexHeapScan(CStoreIndexHeapScanState* state) /* * for function-returning-set. */ - if (state->ps.ps_TupFromTlist) { + if (state->ps.ps_vec_TupFromTlist) { Assert(state->ps.ps_ProjInfo); pOutBatch = ExecVecProject(state->ps.ps_ProjInfo, true, &isDone); if (pOutBatch->m_rows > 0) { return pOutBatch; } - state->ps.ps_TupFromTlist = false; + state->ps.ps_vec_TupFromTlist = false; } state->ps.ps_ProjInfo->pi_exprContext->current_row = 0; @@ -149,7 +154,7 @@ restart: pOutBatch = ApplyProjectionAndFilter(state, pScanBatch, &isDone); if (isDone != ExprEndResult) { - state->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + state->ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); } if (BatchIsNull(pOutBatch)) @@ -166,7 +171,7 @@ restart: pOutBatch = ApplyProjectionAndFilter(state, pScanBatch, &isDone); if (isDone != ExprEndResult) { - state->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + state->ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); } if (BatchIsNull(pOutBatch)) diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/veccstoreindexscan.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/veccstoreindexscan.cpp index 0311777c9..d4a278245 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/veccstoreindexscan.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/veccstoreindexscan.cpp @@ -45,6 +45,7 @@ #include "vecexecutor/vecnodecstoreindexscan.h" /* remove it in future */ #include "nodes/makefuncs.h" +#include "executor/executor.h" extern bool CodeGenThreadObjectReady(); extern bool CodeGenPassThreshold(double rows, int dn_num, int dop); @@ -182,14 +183,14 @@ VectorBatch* ExecCstoreIndexScan(CStoreIndexScanState* state) /* * for function-returning-set. */ - if (state->ps.ps_TupFromTlist) { + if (state->ps.ps_vec_TupFromTlist) { Assert(state->ps.ps_ProjInfo); pOutBatch = ExecVecProject(state->ps.ps_ProjInfo, true, &isDone); if (pOutBatch->m_rows > 0) { return pOutBatch; } - state->ps.ps_TupFromTlist = false; + state->ps.ps_vec_TupFromTlist = false; } state->ps.ps_ProjInfo->pi_exprContext->current_row = 0; @@ -212,7 +213,7 @@ restart: pOutBatch = ApplyProjectionAndFilter(state, pScanBatch, &isDone); if (isDone != ExprEndResult) { - state->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + state->ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); } if (BatchIsNull(pOutBatch)) @@ -229,7 +230,7 @@ restart: pOutBatch = ApplyProjectionAndFilter(state, pScanBatch, &isDone); if (isDone != ExprEndResult) { - state->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + state->ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); } if (BatchIsNull(pOutBatch)) @@ -324,8 +325,11 @@ CStoreIndexScanState* ExecInitCstoreIndexScan(CStoreIndexScan* node, EState* est indexstate->ps.vectorized = true; indexstate->ps.type = T_CStoreIndexScanState; indexstate->index_only_scan = node->indexonly; - indexstate->m_deltaQual = (List*)ExecInitExpr((Expr*)node->indexqualorig, (PlanState*)&indexstate->ps); - + if (estate->es_is_flt_frame) { + indexstate->m_deltaQual = (List*)ExecInitQualByFlatten(node->indexqualorig, (PlanState*)&indexstate->ps); + } else { + indexstate->m_deltaQual = (List*)ExecInitExprByRecursion((Expr*)node->indexqualorig, (PlanState*)&indexstate->ps); + } // If we are just doing EXPLAIN (ie, aren't going to run the plan), stop // here. This allows an index-advisor plugin to EXPLAIN a plan containing // references to nonexistent indexes. diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecgroup.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecgroup.cpp index d032dcd3a..3ac931cb5 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecgroup.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecgroup.cpp @@ -119,7 +119,7 @@ static VectorBatch* ProduceBatch(VecGroupState* node) expr_context->ecxt_scanbatch = batch; expr_context->ecxt_outerbatch = batch; - if (list_length(node->ss.ps.qual) != 0) { + if (list_length((List*)node->ss.ps.qual) != 0) { ScalarVector* p_vector = NULL; /* @@ -181,7 +181,7 @@ VecGroupState* ExecInitVecGroup(VecGroup* node, EState* estate, int eflags) CodeGenThreadObjectReady() && CodeGenPassThreshold(((Plan*)node)->plan_rows, estate->es_plannedstmt->num_nodes, ((Plan*)node)->dop); if (consider_codegen) { - grp_vecqual = dorado::VecExprCodeGen::QualCodeGen(grp_state->ss.ps.qual, (PlanState*)grp_state); + grp_vecqual = dorado::VecExprCodeGen::QualCodeGen((List*)grp_state->ss.ps.qual, (PlanState*)grp_state); if (grp_vecqual != NULL) llvm_code_gen->addFunctionToMCJit(grp_vecqual, reinterpret_cast(&(grp_state->jitted_vecqual))); } @@ -222,7 +222,7 @@ void ExecEndVecGroup(VecGroupState* node) void ExecReScanVecGroup(VecGroupState* node) { - node->ss.ps.ps_TupFromTlist = false; + node->ss.ps.ps_vec_TupFromTlist = false; node->grp_done = false; ReScanGrpUniq(node); diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vechashjoin.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vechashjoin.cpp index 0a2b1a78b..4aec0d42b 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vechashjoin.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vechashjoin.cpp @@ -213,7 +213,7 @@ VecHashJoinState* ExecInitVecHashJoin(VecHashJoin* node, EState* estate, int efl hash_state->hj_HashOperators = hoperators; hash_state->hj_hashCollations = NIL; hash_state->eqfunctions = eqfunctions; - hash_state->js.ps.ps_TupFromTlist = false; + hash_state->js.ps.ps_vec_TupFromTlist = false; /* Initialize runtime bloomfilter. */ hash_state->bf_runtime.bf_var_list = hash_state->js.ps.plan->var_list; @@ -3438,7 +3438,7 @@ VectorBatch* HashJoinTbl::buildResult(VectorBatch* in_batch, VectorBatch* out_ba if (m_runtime->js.ps.qual != NULL) { has_qual = true; econtext->ecxt_scanbatch = m_result; - p_vector = ExecVecQual(m_runtime->js.ps.qual, econtext, false); + p_vector = ExecVecQual((List*)m_runtime->js.ps.qual, econtext, false); if (p_vector == NULL) { in_batch->Reset(); diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/veclimit.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/veclimit.cpp index 1ef518a0b..3c69118e3 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/veclimit.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/veclimit.cpp @@ -289,8 +289,8 @@ VecLimitState* ExecInitVecLimit(VecLimit* node, EState* estate, int eflags) /* * initialize child expressions */ - limit_state->limitOffset = ExecInitExpr((Expr*)node->limitOffset, (PlanState*)limit_state); - limit_state->limitCount = ExecInitExpr((Expr*)node->limitCount, (PlanState*)limit_state); + limit_state->limitOffset = ExecInitExprByRecursion((Expr*)node->limitOffset, (PlanState*)limit_state); + limit_state->limitCount = ExecInitExprByRecursion((Expr*)node->limitCount, (PlanState*)limit_state); /* * Tuple table initialization (XXX not actually used...) diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecmergejoin.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecmergejoin.cpp index cd08e1a81..b3c20ea87 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecmergejoin.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecmergejoin.cpp @@ -1048,7 +1048,7 @@ VectorBatch* ExecVecMergeJoinT(VecMergeJoinState* node) VectorBatch* resultBatch = NULL; /* prevent excessive calls */ - DBG_ASSERT(!node->js.ps.ps_TupFromTlist); + DBG_ASSERT(!node->js.ps.ps_vec_TupFromTlist); econtext = node->js.ps.ps_ExprContext; doFillOuter = node->mj_FillOuter; @@ -1941,7 +1941,7 @@ VecMergeJoinState* ExecInitVecMergeJoin(VecMergeJoin* node, EState* estate, int */ mergestate->m_fDone = false; mergestate->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER; - mergestate->js.ps.ps_TupFromTlist = false; + mergestate->js.ps.ps_vec_TupFromTlist = false; mergestate->mj_MatchedOuter = false; mergestate->mj_MatchedInner = false; mergestate->mj_OuterOffset = NullBatchTuple; @@ -2014,7 +2014,7 @@ void ExecReScanVecMergeJoin(VecMergeJoinState* node) { BatchAccessor* pbaccessor = NULL; node->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER; - node->js.ps.ps_TupFromTlist = false; + node->js.ps.ps_vec_TupFromTlist = false; node->mj_MatchedOuter = false; node->mj_MatchedInner = false; node->mj_OuterOffset = NullBatchTuple; diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecnestloop.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecnestloop.cpp index 13e88afa5..83c75b96b 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecnestloop.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecnestloop.cpp @@ -526,7 +526,7 @@ VecNestLoopState* ExecInitVecNestLoop(VecNestLoop* node, EState* estate, int efl /* * finally, wipe the current outer tuple clean. */ - nlstate->js.ps.ps_TupFromTlist = false; + nlstate->js.ps.ps_vec_TupFromTlist = false; nlstate->nl_NeedNewOuter = true; nlstate->nl_MatchedOuter = false; @@ -670,7 +670,7 @@ void ExecReScanVecNestLoop(VecNestLoopState* node) * re-scanned from here or you'll get troubles from inner index scans when * outer Vars are used as run-time keys... */ - node->js.ps.ps_TupFromTlist = false; + node->js.ps.ps_vec_TupFromTlist = false; VecNestLoopRuntime* runtime = (VecNestLoopRuntime*)node->vecNestLoopRuntime; runtime->Rescan(); diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecpartiterator.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecpartiterator.cpp index 93b29e0fc..7c32d462d 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecpartiterator.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecpartiterator.cpp @@ -49,7 +49,7 @@ VecPartIteratorState* ExecInitVecPartIterator(VecPartIterator* node, EState* est state->ps.subPlan = NULL; state->currentItr = -1; state->subPartCurrentItr = -1; - state->ps.ps_TupFromTlist = false; + state->ps.ps_vec_TupFromTlist = false; state->ps.ps_ProjInfo = NULL; state->ps.vectorized = true; state->ps.ps_ResultTupleSlot = state->ps.lefttree->ps_ResultTupleSlot; diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecresult.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecresult.cpp index 74a5a07b4..6196f61e8 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecresult.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecresult.cpp @@ -41,7 +41,7 @@ VectorBatch* ExecVecResult(VecResultState* node) ExprContext* expr_context = NULL; VectorBatch* batch = NULL; VectorBatch* res_batch = NULL; - List* qual = node->ps.qual; + List* qual = (List*)node->ps.qual; expr_context = node->ps.ps_ExprContext; @@ -49,7 +49,7 @@ VectorBatch* ExecVecResult(VecResultState* node) * check constant qualifications like (2 > 1), if not already done */ if (node->rs_checkqual) { - bool qual_result = ExecQual((List*)node->resconstantqual, expr_context, false); + bool qual_result = ExecQual((List*)node->resconstantqual, expr_context); node->rs_checkqual = false; if (!qual_result) { node->rs_done = true; @@ -62,7 +62,7 @@ VectorBatch* ExecVecResult(VecResultState* node) } } - Assert(node->ps.ps_TupFromTlist == false); + Assert(node->ps.ps_vec_TupFromTlist == false); /* * Reset per-tuple memory context to free any expression evaluation @@ -164,7 +164,7 @@ VecResultState* ExecInitVecResult(VecResult* node, EState* estate, int eflags) */ ExecAssignExprContext(estate, &res_state->ps); - res_state->ps.ps_TupFromTlist = false; + res_state->ps.ps_vec_TupFromTlist = false; /* * tuple table initialization @@ -176,8 +176,11 @@ VecResultState* ExecInitVecResult(VecResult* node, EState* estate, int eflags) */ res_state->ps.targetlist = (List*)ExecInitVecExpr((Expr*)node->plan.targetlist, (PlanState*)res_state); res_state->ps.qual = (List*)ExecInitVecExpr((Expr*)node->plan.qual, (PlanState*)res_state); - - res_state->resconstantqual = ExecInitExpr((Expr*)node->resconstantqual, (PlanState*)res_state); + if (estate->es_is_flt_frame) { + res_state->resconstantqual = ExecInitQualByFlatten((List*)node->resconstantqual, (PlanState*)res_state); + } else { + res_state->resconstantqual = ExecInitExprByRecursion((Expr*)node->resconstantqual, (PlanState*)res_state); + } /* * initialize child nodes @@ -226,7 +229,7 @@ void ExecEndVecResult(VecResultState* node) void ExecReScanVecResult(VecResultState* node) { node->rs_done = false; - node->ps.ps_TupFromTlist = false; + node->ps.ps_vec_TupFromTlist = false; node->rs_checkqual = (node->resconstantqual == NULL) ? false : true; /* diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecrowtovector.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecrowtovector.cpp index 5a56deb4e..b2b1789d3 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecrowtovector.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecrowtovector.cpp @@ -319,7 +319,7 @@ static VectorBatch *ApplyProjectionAndFilterBatch(VectorBatch *pScanBatch, VectorBatch *pOutBatch = NULL; bool fSimpleMap = false; uint64 inputRows = pScanBatch->m_rows; - List* qual = node->ps.qual; + List* qual = (List*)node->ps.qual; econtext = node->ps.ps_ExprContext; pOutBatch = node->scanBatchState->pCurrentBatch; diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecscan.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecscan.cpp index 1f53ca704..654e58285 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecscan.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecscan.cpp @@ -81,7 +81,7 @@ VectorBatch* ExecVecScan(ScanState* node, ExecVecScanAccessMtd accessMtd, /* fun /* * Fetch data from node */ - List* qual = node->ps.qual; + List* qual = (List*)node->ps.qual; proj_info = node->ps.ps_ProjInfo; econtext = node->ps.ps_ExprContext; @@ -106,7 +106,7 @@ VectorBatch* ExecVecScan(ScanState* node, ExecVecScanAccessMtd accessMtd, /* fun // We don't support function returning set // - DBG_ASSERT(!node->ps.ps_TupFromTlist); + DBG_ASSERT(!node->ps.ps_vec_TupFromTlist); // Reset per-tuple memory context to free any expression evaluation // storage allocated in the previous batch cycle. Note this can't happen diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecsubplan.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecsubplan.cpp index 4c810a9fe..e42779788 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecsubplan.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecsubplan.cpp @@ -26,6 +26,7 @@ #include "utils/lsyscache.h" #include "optimizer/clauses.h" #include "executor/node/nodeSubplan.h" +#include "executor/executor.h" static TupleTableSlot* GetSlotfromBatchRow(TupleTableSlot* slot, VectorBatch* batch, int n_row); @@ -1197,47 +1198,81 @@ SubPlanState* ExecInitVecSubPlan(SubPlan* subplan, PlanState* parent) i++; } + if (estate->es_is_flt_frame) { + if (IsA(subplan->testexpr, OpExpr)) { + /* single combining operator */ + oplist = list_make1(subplan->testexpr); + } else if (and_clause((Node*)subplan->testexpr)) { + /* multiple combining operators */ + Assert(IsA(subplan->testexpr, BoolExpr)); + oplist = ((BoolExpr*)subplan->testexpr)->args; + } else { + /* shouldn't see anything else in a hashable subplan */ + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + errmsg("unrecognized testexpr type: %d", (int)nodeTag(subplan->testexpr)))); + oplist = NIL; /* keep compiler quiet */ + } + Assert(list_length(oplist) == ncols); - if (IsA(sstate->row_testexpr->expr, OpExpr)) { - /* single combining operator */ - oplist = list_make1(sstate->row_testexpr); - } else if (and_clause((Node*)sstate->row_testexpr->expr)) { - /* multiple combining operators */ - Assert(IsA(sstate->row_testexpr, BoolExprState)); - oplist = ((BoolExprState*)sstate->row_testexpr)->args; + i = 1; + foreach (l, oplist) { + OpExpr* opexpr = (OpExpr*)lfirst(l); + Expr* expr = NULL; + TargetEntry* tle = NULL; + + Assert(IsA(opexpr, OpExpr)); + Assert(list_length(opexpr->args) == 2); + + /* Process righthand argument */ + expr = (Expr*)lsecond(opexpr->args); + tle = makeTargetEntry(expr, i, NULL, false); + righttlist = lappend(righttlist, tle); + + i++; + } } else { - /* shouldn't see anything else in a hashable subplan */ - ereport(ERROR, - (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), - errmsg("unrecognized testexpr type: %d", (int)nodeTag(sstate->row_testexpr->expr)))); - oplist = NIL; /* keep compiler quiet */ - } - Assert(list_length(oplist) == ncols); + if (IsA(sstate->row_testexpr->expr, OpExpr)) { + /* single combining operator */ + oplist = list_make1(sstate->row_testexpr); + } else if (and_clause((Node*)sstate->row_testexpr->expr)) { + /* multiple combining operators */ + Assert(IsA(sstate->row_testexpr, BoolExprState)); + oplist = ((BoolExprState*)sstate->row_testexpr)->args; + } else { + /* shouldn't see anything else in a hashable subplan */ + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + errmsg("unrecognized testexpr type: %d", (int)nodeTag(sstate->row_testexpr->expr)))); + oplist = NIL; /* keep compiler quiet */ + } + Assert(list_length(oplist) == ncols); - i = 1; - foreach (l, oplist) { - FuncExprState* fstate = (FuncExprState*)lfirst(l); - ExprState* exstate = NULL; - Expr* expr = NULL; - TargetEntry* tle = NULL; - GenericExprState* tlestate = NULL; + i = 1; + foreach (l, oplist) { + FuncExprState* fstate = (FuncExprState*)lfirst(l); + ExprState* exstate = NULL; + Expr* expr = NULL; + TargetEntry* tle = NULL; + GenericExprState* tlestate = NULL; - Assert(IsA(fstate, FuncExprState)); - Assert(IsA((OpExpr*)fstate->xprstate.expr, OpExpr)); - Assert(list_length(fstate->args) == 2); + Assert(IsA(fstate, FuncExprState)); + Assert(IsA((OpExpr*)fstate->xprstate.expr, OpExpr)); + Assert(list_length(fstate->args) == 2); - /* Process righthand argument */ - exstate = (ExprState*)lsecond(fstate->args); - expr = exstate->expr; - tle = makeTargetEntry(expr, i, NULL, false); - tlestate = makeNode(GenericExprState); - tlestate->xprstate.expr = (Expr*)tle; - tlestate->xprstate.evalfunc = NULL; - tlestate->arg = exstate; - righttlist = lappend(righttlist, tlestate); - rightptlist = lappend(rightptlist, tle); + /* Process righthand argument */ + exstate = (ExprState*)lsecond(fstate->args); + expr = exstate->expr; + tle = makeTargetEntry(expr, i, NULL, false); + tlestate = makeNode(GenericExprState); + tlestate->xprstate.expr = (Expr*)tle; + tlestate->xprstate.evalfunc = NULL; + tlestate->arg = exstate; + righttlist = lappend(righttlist, tlestate); + rightptlist = lappend(rightptlist, tle); - i++; + i++; + } } /* @@ -1253,10 +1288,17 @@ SubPlanState* ExecInitVecSubPlan(SubPlan* subplan, PlanState* parent) ExecSetSlotDescriptor(slot, tup_desc); sstate->projLeft = ExecBuildVecProjectionInfo(lefttlist, NULL, NULL, slot, NULL); - tup_desc = ExecTypeFromTL(rightptlist, false); - slot = ExecInitExtraTupleSlot(estate); - ExecSetSlotDescriptor(slot, tup_desc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, sstate->innerecontext, slot, NULL); + if (estate->es_is_flt_frame) { + tup_desc = ExecTypeFromTL(righttlist, false); + slot = ExecInitExtraTupleSlot(estate); + ExecSetSlotDescriptor(slot, tup_desc); + sstate->projRight = ExecBuildProjectionInfoByFlatten(righttlist, sstate->innerecontext, slot, sstate->planstate, NULL); + } else { + tup_desc = ExecTypeFromTL(rightptlist, false); + slot = ExecInitExtraTupleSlot(estate); + ExecSetSlotDescriptor(slot, tup_desc); + sstate->projRight = ExecBuildProjectionInfoByRecursion(righttlist, sstate->innerecontext, slot, NULL); + } } return sstate; diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecsubqueryscan.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecsubqueryscan.cpp index 50aae598b..857048085 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecsubqueryscan.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecsubqueryscan.cpp @@ -133,7 +133,7 @@ VecSubqueryScanState* ExecInitVecSubqueryScan(VecSubqueryScan* node, EState* est */ vecsubquerystate->subplan = ExecInitNode(node->subplan, estate, eflags); - vecsubquerystate->ss.ps.ps_TupFromTlist = false; + vecsubquerystate->ss.ps.ps_vec_TupFromTlist = false; /* * Initialize scan tuple type (needed by ExecAssignScanProjectionInfo) diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vectsstorescan.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vectsstorescan.cpp index b8237e98f..151726bcc 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vectsstorescan.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vectsstorescan.cpp @@ -414,7 +414,7 @@ TsStoreScanState* ExecInitTsStoreScan(TsStoreScan* node, Relation parentHeapRel, } scanstate->time_range = get_time_range(scanstate); - scanstate->ps.ps_TupFromTlist = false; + scanstate->ps.ps_vec_TupFromTlist = false; if (scanstate->limit > 0 && scanstate->ps.qual == NULL) { /* if is simple sort by timestamp limit n , then earlier stop */ @@ -655,7 +655,7 @@ rescan: pScanBatch->FixRowCount(); pOutBatch = ts_apply_projection_and_filter(node, pScanBatch, &isDone); if (isDone != ExprEndResult) { - node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); + node->ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult); } if (node->early_stop) { node->scaned_tuples += pScanBatch->m_rows; diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp index e11ca1c35..0e1001e96 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecwindowagg.cpp @@ -160,7 +160,7 @@ VecWindowAggState* ExecInitVecWindowAgg(VecWindowAgg* node, EState* estate, int ExecAssignVectorForExprEval(planstate->ps_ProjInfo->pi_exprContext); } - winstate->ss.ps.ps_TupFromTlist = false; + winstate->ss.ps.ps_vec_TupFromTlist = false; /* Set up data for comparing tuples */ if (node->partNumCols > 0) diff --git a/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonichashagg.cpp b/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonichashagg.cpp index 35777c299..7b0bd1d15 100644 --- a/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonichashagg.cpp +++ b/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonichashagg.cpp @@ -2343,7 +2343,7 @@ VectorBatch* SonicHashAgg::ProducerBatch() } m_outBatch->m_rows = m_scanBatch->m_rows; - if (list_length(m_runtime->ss.ps.qual) != 0) { + if (list_length((List*)m_runtime->ss.ps.qual) != 0) { ScalarVector* pVector = NULL; expr_context = m_runtime->ss.ps.ps_ExprContext; @@ -2351,7 +2351,7 @@ VectorBatch* SonicHashAgg::ProducerBatch() expr_context->ecxt_aggbatch = m_scanBatch; expr_context->ecxt_outerbatch = m_outBatch; - pVector = ExecVecQual(m_runtime->ss.ps.qual, expr_context, false); + pVector = ExecVecQual((List*)m_runtime->ss.ps.qual, expr_context, false); if (pVector == NULL) { return NULL; diff --git a/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonichashjoin.cpp b/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonichashjoin.cpp index 1e1f239f3..7b2c3641e 100644 --- a/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonichashjoin.cpp +++ b/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonichashjoin.cpp @@ -2051,7 +2051,7 @@ VectorBatch* SonicHashJoin::buildRes(VectorBatch* inBatch, VectorBatch* outBatch if (m_runtime->js.ps.qual != NULL) { has_qual = true; econtext->ecxt_scanbatch = inBatch; - pvector = ExecVecQual(m_runtime->js.ps.qual, econtext, false); + pvector = ExecVecQual((List*)m_runtime->js.ps.qual, econtext, false); if (pvector == NULL) { inBatch->Reset(); diff --git a/src/gausskernel/storage/cstore/cstore_am.cpp b/src/gausskernel/storage/cstore/cstore_am.cpp index 27f819f41..01d7be607 100644 --- a/src/gausskernel/storage/cstore/cstore_am.cpp +++ b/src/gausskernel/storage/cstore/cstore_am.cpp @@ -67,6 +67,7 @@ #include "securec_check.h" #include "commands/tablespace.h" #include "workload/workload.h" +#include "executor/executor.h" #ifdef PGXC #include "pgxc/pgxc.h" @@ -4481,7 +4482,7 @@ void ScanDeltaStore(CStoreScanState* node, VectorBatch* outBatch, List* indexqua /* If there is index qual, use it to filter the delta rows before */ if (hasIndexFilter) { econtext->ecxt_scantuple = slot; - if (!ExecQual(indexqual, econtext, false)) { + if (!ExecQual(indexqual, econtext)) { (void)ExecClearTuple(slot); continue; } diff --git a/src/gausskernel/storage/cstore/cstore_rewrite.cpp b/src/gausskernel/storage/cstore/cstore_rewrite.cpp index 3f7378183..13ab7b9bd 100644 --- a/src/gausskernel/storage/cstore/cstore_rewrite.cpp +++ b/src/gausskernel/storage/cstore/cstore_rewrite.cpp @@ -739,7 +739,7 @@ void CStoreRewriter::AddColumns(_in_ uint32 cuId, _in_ int rowsCntInCu, _in_ boo if (newColInfo->newValue) { // compute the new value by expression m_econtext->ecxt_scantuple = fakeSlot; - newColVal = ExecEvalExpr(newColInfo->newValue->exprstate, m_econtext, &newColValIsNull, NULL); + newColVal = ExecEvalExpr(newColInfo->newValue->exprstate, m_econtext, &newColValIsNull); } else if (newColInfo->notNull) { // DEFAULT must be defined if NOT NULL exists and // the table is not empty now. @@ -1141,7 +1141,7 @@ void CStoreRewriter::SetDataTypeHandleSameValCu( // compute the new value only once if it's cu with the same value. Assert(setDataTypeColInfo->newValue); bool attNewIsNull = false; - Datum attNewValue = ExecEvalExpr(setDataTypeColInfo->newValue->exprstate, m_econtext, &attNewIsNull, NULL); + Datum attNewValue = ExecEvalExpr(setDataTypeColInfo->newValue->exprstate, m_econtext, &attNewIsNull); if (attNewIsNull && setDataTypeColInfo->notNull) { ereport(ERROR, (errcode(ERRCODE_NOT_NULL_VIOLATION), @@ -1253,7 +1253,7 @@ void CStoreRewriter::SetDataTypeHandleNormalCu( m_SDTColIsNull[cnt] = false; m_SDTColValues[cnt] = - ExecEvalExpr(setDataTypeColInfo->newValue->exprstate, m_econtext, (m_SDTColIsNull + cnt), NULL); + ExecEvalExpr(setDataTypeColInfo->newValue->exprstate, m_econtext, (m_SDTColIsNull + cnt)); if (!m_SDTColIsNull[cnt]) { fullNull = false; diff --git a/src/gausskernel/storage/mot/fdw_adapter/mot_fdw.cpp b/src/gausskernel/storage/mot/fdw_adapter/mot_fdw.cpp index 295d7ff44..5bdf0a0c5 100644 --- a/src/gausskernel/storage/mot/fdw_adapter/mot_fdw.cpp +++ b/src/gausskernel/storage/mot/fdw_adapter/mot_fdw.cpp @@ -1037,7 +1037,7 @@ static TupleTableSlot* IterateForeignScanStopAtFirst( { MOT::RC rc = MOT::RC_OK; ForeignScan* fscan = (ForeignScan*)node->ss.ps.plan; - festate->m_execExprs = (List*)ExecInitExpr((Expr*)fscan->fdw_exprs, (PlanState*)node); + festate->m_execExprs = ExecInitExprList(fscan->fdw_exprs, (PlanState*)node); festate->m_econtext = node->ss.ps.ps_ExprContext; MOTAdaptor::CreateKeyBuffer(node->ss.ss_currentRelation, festate, 0); MOT::Sentinel* sentinel = @@ -1107,7 +1107,7 @@ static TupleTableSlot* MOTIterateForeignScan(ForeignScanState* node) if (!festate->m_cursorOpened) { ForeignScan* fscan = (ForeignScan*)node->ss.ps.plan; - festate->m_execExprs = (List*)ExecInitExpr((Expr*)fscan->fdw_exprs, (PlanState*)node); + festate->m_execExprs = ExecInitExprList(fscan->fdw_exprs, (PlanState*)node); festate->m_econtext = node->ss.ps.ps_ExprContext; CleanCursors(festate); MOTAdaptor::OpenCursor(node->ss.ss_currentRelation, festate); @@ -1211,7 +1211,7 @@ static void MOTReScanForeignScan(ForeignScanState* node) if (!stopAtFirst) { if (festate->m_execExprs == NULL) { ForeignScan* fscan = (ForeignScan*)node->ss.ps.plan; - festate->m_execExprs = (List*)ExecInitExpr((Expr*)fscan->fdw_exprs, (PlanState*)node); + festate->m_execExprs = ExecInitExprList(fscan->fdw_exprs, (PlanState*)node); festate->m_econtext = node->ss.ps.ps_ExprContext; } MOTAdaptor::OpenCursor(node->ss.ss_currentRelation, festate); diff --git a/src/gausskernel/storage/mot/fdw_adapter/mot_internal.cpp b/src/gausskernel/storage/mot/fdw_adapter/mot_internal.cpp index dbcd5a529..28005612a 100644 --- a/src/gausskernel/storage/mot/fdw_adapter/mot_internal.cpp +++ b/src/gausskernel/storage/mot/fdw_adapter/mot_internal.cpp @@ -1823,7 +1823,7 @@ void MOTAdaptor::CreateKeyBuffer(Relation rel, MOTFdwStateSt* festate, int start if (opers[i] < KEY_OPER::READ_INVALID) { bool is_null = false; ExprState* expr = (ExprState*)list_nth(festate->m_execExprs, exprs[i] - 1); - Datum val = ExecEvalExpr((ExprState*)(expr), festate->m_econtext, &is_null, nullptr); + Datum val = ExecEvalExpr((ExprState*)(expr), festate->m_econtext, &is_null); if (is_null) { MOT_ASSERT((offset + fieldLengths[i]) <= keyLength); errno_t erc = memset_s(buf + offset, fieldLengths[i], 0x00, fieldLengths[i]); diff --git a/src/gausskernel/storage/replication/logical/worker.cpp b/src/gausskernel/storage/replication/logical/worker.cpp index 85118c51b..f8fdc985d 100644 --- a/src/gausskernel/storage/replication/logical/worker.cpp +++ b/src/gausskernel/storage/replication/logical/worker.cpp @@ -257,7 +257,7 @@ static void slot_fill_defaults(LogicalRepRelMapEntry *rel, EState *estate, Tuple } for (i = 0; i < num_defaults; i++) - slot->tts_values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext, &slot->tts_isnull[defmap[i]], NULL); + slot->tts_values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext, &slot->tts_isnull[defmap[i]]); } /* diff --git a/src/gausskernel/storage/tcap/tcap_version.cpp b/src/gausskernel/storage/tcap/tcap_version.cpp index 6fc9099ec..6c648561a 100644 --- a/src/gausskernel/storage/tcap/tcap_version.cpp +++ b/src/gausskernel/storage/tcap/tcap_version.cpp @@ -54,6 +54,7 @@ #include "storage/tcap.h" #include "catalog/pg_constraint.h" +#include "executor/executor.h" static bool TvIsContainsForeignKey(Oid relid) { @@ -477,7 +478,7 @@ Snapshot TvChooseScanSnap(Relation relation, Scan *scan, ScanState *ss) econtext = CreateExprContext(estate); val = ExecEvalExprSwitchContext(ExecInitExpr((Expr *)tcc->tvver, &ss->ps), - econtext, &isnull, NULL); + econtext, &isnull); con = makeConst((tcc->tvtype == TV_VERSION_TIMESTAMP) ? TIMESTAMPTZOID : INT8OID, -1, InvalidOid, 8, val, isnull, true); diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h index 348a37d9f..3b104010f 100644 --- a/src/include/catalog/objectaccess.h +++ b/src/include/catalog/objectaccess.h @@ -60,4 +60,6 @@ extern THR_LOCAL PGDLLIMPORT object_access_hook_type object_access_hook; (objectId),(subId),(arg)); \ } while(0) +#define InvokeFunctionExecuteHook(objectId) + #endif /* OBJECTACCESS_H */ diff --git a/src/include/distributelayer/streamMain.h b/src/include/distributelayer/streamMain.h index 0e09a4efa..c0a50a636 100644 --- a/src/include/distributelayer/streamMain.h +++ b/src/include/distributelayer/streamMain.h @@ -30,6 +30,7 @@ extern void SetStreamWorkerInfo(class StreamProducer* proObj); extern void ResetStreamEnv(); extern void ResetSessionEnv(); extern void ExtractProduerInfo(); +extern void ExtractProduerSkewInfo(); extern ThreadId ApplyStreamThread(StreamProducer *producer); extern void RestoreStream(); extern void StreamExit(); diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 00c435c6d..6f9bf4fb1 100755 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -16,6 +16,7 @@ #define EXECUTOR_H #include "executor/exec/execdesc.h" +#include "parser/parse_coerce.h" #include "nodes/parsenodes.h" #include "nodes/params.h" #include "pgxc/pgxc.h" @@ -120,13 +121,6 @@ extern inline bool is_errmodule_enable(int elevel, ModuleId mod_id); #define HAS_INSTR(node, dnonly) \ (!dnonly || IS_PGXC_DATANODE) && u_sess->instr_cxt.global_instr != NULL && (node)->ps.instrument != NULL -/* - * ExecEvalExpr was formerly a function containing a switch statement; - * now it's just a macro invoking the function pointed to by an ExprState - * node. Beware of double evaluation of the ExprState argument! - */ -#define ExecEvalExpr(expr, econtext, isNull, isDone) ((*(expr)->evalfunc)(expr, econtext, isNull, isDone)) - /* * recursive union macro */ @@ -194,7 +188,7 @@ extern TupleHashEntry FindTupleHashEntry( * prototypes from functions in execJunk.c */ extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot, - const TableAmRoutine *tam_ops); + const TableAmRoutine *tam_ops = TableAmHeap); extern void ExecInitJunkAttr(EState *estate, CmdType operation, List *targetlist, ResultRelInfo *result_rel_info); extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, TupleDesc cleanTupType, TupleTableSlot *slot); extern AttrNumber ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName); @@ -304,31 +298,193 @@ static inline TupleTableSlot *ExecProcNode(PlanState *node) /* - * prototypes from functions in execQual.c + * prototypes from functions in execExpr.c */ -extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno, bool* isNull); -extern Datum GetAttributeByName(HeapTupleHeader tuple, const char* attname, bool* isNull); -extern Tuplestorestate* ExecMakeTableFunctionResult( - ExprState* funcexpr, ExprContext* econtext, TupleDesc expectedDesc, bool randomAccess, FunctionScanState* node); -extern Datum ExecEvalExprSwitchContext( - ExprState* expression, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); +extern bool ExecQual(List* qual, ExprContext* econtext, bool resultForNull = false); +extern TupleTableSlot* ExecProject(ProjectionInfo* projInfo, ExprDoneCond* isDone = NULL); extern ExprState* ExecInitExpr(Expr* node, PlanState* parent); -extern List* ExecInitExprList(List* nodes, PlanState *parent); +extern ExprState *ExecInitQual(List *qual, PlanState *parent); +extern ExprState *ExecInitCheck(List *qual, PlanState *parent); +extern List *ExecInitExprList(List *nodes, PlanState *parent); +extern ProjectionInfo* ExecBuildProjectionInfo(List *targetList, + ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc); extern ExprState* ExecPrepareExpr(Expr* node, EState* estate); -extern bool ExecQual(List* qual, ExprContext* econtext, bool resultForNull); -extern int ExecTargetListLength(List* targetlist); -extern int ExecCleanTargetListLength(List* targetlist); -extern TupleTableSlot* ExecProject(ProjectionInfo* projInfo, ExprDoneCond* isDone); -extern Datum ExecMakeFunctionResultSet(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +extern Datum ExecMakeFunctionResultSet(FuncExprState *fcache, + ExprContext *econtext, + bool *isNull, + ExprDoneCond *isDone); +extern ExprState *ExecPrepareCheck(List *qual, EState *estate); +extern List *ExecPrepareExprList(List *nodes, EState *estate); +extern bool ExecCheck(ExprState *state, ExprContext *context); +/** + * new expr + */ +template +extern void init_fcache( + Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF); +extern ExprState* ExecInitExprByRecursion(Expr* node, PlanState* parent); +extern bool ExecQualByRecursion(List* qual, ExprContext* econtext, bool resultForNull); +extern ExprState* ExecInitExprByFlatten(Expr* node, PlanState* parent); +extern ExprState *ExecInitQualByFlatten(List *qual, PlanState *parent); +extern ExprState *ExecInitQualByRecursion(Expr *qual, PlanState *parent, bool resultForNull = false); +extern ExprState *ExecInitCheckByFlatten(List *qual, PlanState *parent); +extern bool ExecCheckByFlatten(ExprState *state, ExprContext *context); +extern ProjectionInfo* ExecBuildProjectionInfoByFlatten(List *targetList, + ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc); +extern ProjectionInfo* ExecBuildProjectionInfoByRecursion(List *targetList, ExprContext *econtext, TupleTableSlot *slot, TupleDesc inputDesc); +extern ExprState *ExecPrepareQualByFlatten(List *qual, EState *estate); +extern TupleTableSlot* ExecProjectByRecursion(ProjectionInfo* projInfo, ExprDoneCond* isDone); +/* + * ExecEvalExpr + * + * Evaluate expression identified by "state" in the execution context + * given by "econtext". *isNull is set to the is-null flag for the result, + * and the Datum value is the function result. + * + * The caller should already have switched into the temporary memory + * context econtext->ecxt_per_tuple_memory. The convenience entry point + * ExecEvalExprSwitchContext() is provided for callers who don't prefer to + * do the switch in an outer loop. + */ +#ifndef FRONTEND +static inline Datum +ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone= NULL) +{ + return state->evalfunc(state, econtext, isNull, isDone); +} +#endif + +/* + * ExecEvalExprSwitchContext + * + * Same as ExecEvalExpr, but get into the right allocation context explicitly. + */ +#ifndef FRONTEND +Datum +static inline ExecEvalExprSwitchContext(ExprState *state, + ExprContext *econtext, + bool *isNull, + ExprDoneCond *isDone = NULL) +{ + Datum retDatum; + MemoryContext oldContext; + + oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + retDatum = state->evalfunc(state, econtext, isNull, isDone); + MemoryContextSwitchTo(oldContext); + return retDatum; +} +#endif + +/* + * ExecProject + * + * Projects a tuple based on projection info and stores it in the slot passed + * to ExecBuildProjectInfo(). + * + * Note: the result is always a virtual tuple; therefore it may reference + * the contents of the exprContext's scan tuples and/or temporary results + * constructed in the exprContext. If the caller wishes the result to be + * valid longer than that data will be valid, he must call ExecMaterializeSlot + * on the result slot. + */ +#ifndef FRONTEND +static inline TupleTableSlot * +ExecProjectByFlatten(ProjectionInfo *projInfo, ExprDoneCond* isDone = NULL) +{ + ExprContext *econtext = projInfo->pi_exprContext; + ExprState *state = &projInfo->pi_state; + TupleTableSlot *slot = state->resultslot; + bool isnull; + ListCell *lc; + char* resname = NULL; + + if (isDone) { + *isDone = ExprSingleResult; + } + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. + */ + ExecClearTuple(slot); + + if (state->expr) { + lc = list_head((List*)(state->expr)); + TargetEntry *te = (TargetEntry*)lfirst(lc); + state->current_targetentry = lc; + resname = te->resname; + } + + ELOG_FIELD_NAME_START(resname); + + /* Run the expression, discarding scalar result from the last column. */ + (void) ExecEvalExprSwitchContext(state, econtext, &isnull); + + ELOG_FIELD_NAME_END; + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple (inlined version of ExecStoreVirtualTuple()). + */ + slot->tts_flags &= ~TTS_FLAG_EMPTY; + slot->tts_nvalid = slot->tts_tupleDescriptor->natts; + + return slot; +} +#endif + +/* + * ExecQual - evaluate a qual prepared with ExecInitQual (possibly via + * ExecPrepareQualByFlatten). Returns true if qual is satisfied, else false. + * + * Note: ExecQual used to have a third argument "resultForNull". The + * behavior of this function now corresponds to resultForNull == false. + * If you want the resultForNull == true behavior, see ExecCheck. + */ +#ifndef FRONTEND +static inline bool +ExecQualByFlatten(ExprState *state, ExprContext *econtext) +{ + Datum ret; + bool isnull; + ExprDoneCond isDone = ExprSingleResult; + + /* short-circuit (here and in ExecInitQual) for empty restriction list */ + if (state == NULL) + return true; + + /* verify that expression was compiled using ExecInitQual */ + Assert(state->flags & EEO_FLAG_IS_QUAL); + + ret = ExecEvalExprSwitchContext(state, econtext, &isnull, &isDone); + + /* EEOP_QUAL should never return NULL */ + Assert(!isnull); + + return DatumGetBool(ret); +} +#endif + +/* + * prototypes from functions in execSRF.c + */ +extern FuncExprState *ExecInitTableFunctionResult(Expr *expr, + ExprContext *econtext, PlanState *parent); +extern FuncExprState *ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent); +Tuplestorestate* ExecMakeTableFunctionResult( + ExprState* setexpr, ExprContext* econtext, TupleDesc expectedDesc, bool randomAccess, FunctionScanState* node); + +/* + * prototypes from functions in execScan.c + */ extern TupleTableSlot* ExecScan(ScanState* node, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd); extern void ExecAssignScanProjectionInfo(ScanState* node); extern void ExecScanReScan(ScanState* node); extern void initExecTableOfIndexInfo(ExecTableOfIndexInfo* execTableOfIndexInfo, ExprContext* econtext); extern bool ExecEvalParamExternTableOfIndexById(ExecTableOfIndexInfo* execTableOfIndexInfo); extern void ExecEvalParamExternTableOfIndex(Node* node, ExecTableOfIndexInfo* execTableOfIndexInfo); -extern bool is_external_clob(Oid type_oid, bool is_null, Datum value); -extern bool is_huge_clob(Oid type_oid, bool is_null, Datum value); /* * prototypes from functions in execTuples.c @@ -395,6 +551,17 @@ extern ExprContext* CreateStandaloneExprContext(void); extern void FreeExprContext(ExprContext* econtext, bool isCommit); extern void ReScanExprContext(ExprContext* econtext); extern void ShutdownExprContext(ExprContext* econtext, bool isCommit); +extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno, bool* isNull); +extern Datum GetAttributeByName(HeapTupleHeader tuple, const char* attname, bool* isNull); +extern int ExecTargetListLength(List* targetlist); +extern int ExecCleanTargetListLength(List* targetlist); +extern bool is_external_clob(Oid type_oid, bool is_null, Datum value); +extern bool is_huge_clob(Oid type_oid, bool is_null, Datum value); +extern bool func_has_refcursor_args(Oid Funcid, FunctionCallInfoData* fcinfo); +extern void set_result_for_plpgsql_language_function_with_outparam(FuncExprState *fcache, Datum *result, bool *isNull); +extern void set_result_for_plpgsql_language_function_with_outparam_by_flatten(Datum *result, bool *isNull); +extern bool expr_func_has_refcursor_args(Oid Funcid); +extern Datum fetch_lob_value_from_tuple(varatt_lob_pointer *lob_pointer, Oid update_oid, bool *is_null); #define ResetExprContext(econtext) MemoryContextReset((econtext)->ecxt_per_tuple_memory) @@ -428,8 +595,7 @@ extern void ExecAssignResultType(PlanState* planstate, TupleDesc tupDesc); extern void ExecAssignResultTypeFromTL(PlanState* planstate, const TableAmRoutine* tam_ops = TableAmHeap); extern TupleDesc ExecGetResultType(PlanState* planstate); extern void ExecAssignVectorForExprEval(ExprContext* econtext); -extern ProjectionInfo* ExecBuildProjectionInfo( - List* targetList, ExprContext* econtext, TupleTableSlot* slot, TupleDesc inputDesc); + extern void ExecAssignProjectionInfo(PlanState* planstate, TupleDesc inputDesc); extern void ExecAssignScanProjectionInfoWithVarno(ScanState* node, Index varno); extern void ExecFreeExprContext(PlanState* planstate); diff --git a/src/include/executor/node/nodeSubplan.h b/src/include/executor/node/nodeSubplan.h index b8d29cadd..2d97a0d7f 100644 --- a/src/include/executor/node/nodeSubplan.h +++ b/src/include/executor/node/nodeSubplan.h @@ -17,9 +17,9 @@ #include "nodes/execnodes.h" extern SubPlanState* ExecInitSubPlan(SubPlan* subplan, PlanState* parent); - +extern Datum ExecSubPlan(SubPlanState* node, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone = NULL); extern AlternativeSubPlanState* ExecInitAlternativeSubPlan(AlternativeSubPlan* asplan, PlanState* parent); - +extern Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node, ExprContext *econtext, bool *isNull, ExprDoneCond* isDone = NULL); extern void ExecReScanSetParamPlan(SubPlanState* node, PlanState* parent); extern void ExecSetParamPlan(SubPlanState* node, ExprContext* econtext); diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index 170e97506..2837be3f4 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -163,6 +163,7 @@ typedef struct TupleTableSlot { Oid tts_xcnodeoid; /* Oid of node from where the datarow is fetched */ MemoryContext tts_per_tuple_mcxt; #endif + TableAmType tts_tupslotTableAm; /* slots's tuple table type */ } TupleTableSlot; #define TTS_HAS_PHYSICAL_TUPLE(slot) ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr)) diff --git a/src/include/fmgr.h b/src/include/fmgr.h index 85aaf7a3c..62f2cab4f 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -144,6 +144,7 @@ typedef struct StartWithFuncEvalInfo { Node *sw_econtext; Node *sw_exprstate; + bool sw_is_flt_frame; } StartWithFuncEvalInfo; /* diff --git a/src/include/knl/knl_guc/knl_instance_attr_common.h b/src/include/knl/knl_guc/knl_instance_attr_common.h index d733fb379..1967769b8 100644 --- a/src/include/knl/knl_guc/knl_instance_attr_common.h +++ b/src/include/knl/knl_guc/knl_instance_attr_common.h @@ -104,6 +104,7 @@ typedef struct knl_instance_attr_common { int cluster_run_mode; int stream_cluster_run_mode; bool light_comm; + bool enable_expr_fusion; } knl_instance_attr_common; #endif /* SRC_INCLUDE_KNL_KNL_INSTANCE_ATTR_COMMON_H_ */ diff --git a/src/include/nodes/execExpr.h b/src/include/nodes/execExpr.h new file mode 100644 index 000000000..5d6560d43 --- /dev/null +++ b/src/include/nodes/execExpr.h @@ -0,0 +1,710 @@ +/*------------------------------------------------------------------------- + * + * execExpr.h + * Low level infrastructure related to expression evaluation + * + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/executor/execExpr.h + * + *------------------------------------------------------------------------- + */ +#ifndef EXEC_EXPR_H +#define EXEC_EXPR_H + +#include "executor/node/nodeAgg.h" +#include "nodes/execnodes.h" + + +/* forward references to avoid circularity */ +struct ArrayRefState; + +/* Bits in ExprState->flags (see also execnodes.h for public flag bits): */ +/* expression's interpreter has been initialized */ +#define EEO_FLAG_INTERPRETER_INITIALIZED (1 << 1) +/* jump-threading is in use */ +#define EEO_FLAG_DIRECT_THREADED (1 << 2) + +#define FUNC_EXPR_FLAG_HAS_REFCURSOR 0x01 +#define FUNC_EXPR_FLAG_HAS_CURSOR_RETURN 0x02 +#define FUNC_EXPR_FLAG_STRICT 0x04 +#define FUNC_EXPR_FLAG_FUSAGE 0x08 + +/* + * Discriminator for ExprEvalSteps. + * + * Identifies the operation to be executed and which member in the + * ExprEvalStep->d union is valid. + * + * The order of entries needs to be kept in sync with the dispatch_table[] + * array in execExprInterp.c:ExecInterpExpr(). + */ +typedef enum ExprEvalOp +{ + /* entire expression has been evaluated completely, return */ + EEOP_DONE, + + /* apply slot_getsomeattrs on corresponding tuple slot */ + EEOP_INNER_FETCHSOME, + EEOP_OUTER_FETCHSOME, + EEOP_SCAN_FETCHSOME, + + /* compute non-system Var value */ + EEOP_INNER_VAR, + EEOP_OUTER_VAR, + EEOP_SCAN_VAR, + + /* compute system Var value */ + EEOP_INNER_SYSVAR, + EEOP_OUTER_SYSVAR, + EEOP_SCAN_SYSVAR, + + /* compute wholerow Var */ + EEOP_WHOLEROW, + + /* + * Compute non-system Var value, assign it into ExprState's resultslot. + * These are not used if a CheckVarSlotCompatibility() check would be + * needed. + */ + EEOP_ASSIGN_INNER_VAR, + EEOP_ASSIGN_OUTER_VAR, + EEOP_ASSIGN_SCAN_VAR, + + /* assign ExprState's resvalue/resnull to a column of its resultslot */ + EEOP_ASSIGN_TMP, + /* ditto, applying MakeExpandedObjectReadOnly() */ + EEOP_ASSIGN_TMP_MAKE_RO, + + /* evaluate Const value */ + EEOP_CONST, + + /* + * Evaluate function call (including OpExprs etc). For speed, we + * distinguish in the opcode whether the function is strict and/or + * requires usage stats tracking. + */ + EEOP_FUNCEXPR, + + /* + * Evaluate boolean AND expression, one step per subexpression. FIRST/LAST + * subexpressions are special-cased for performance. Since AND always has + * at least two subexpressions, FIRST and LAST never apply to the same + * subexpression. + */ + EEOP_BOOL_AND_STEP_FIRST, + EEOP_BOOL_AND_STEP, + EEOP_BOOL_AND_STEP_LAST, + + /* similarly for boolean OR expression */ + EEOP_BOOL_OR_STEP_FIRST, + EEOP_BOOL_OR_STEP, + EEOP_BOOL_OR_STEP_LAST, + + /* evaluate boolean NOT expression */ + EEOP_BOOL_NOT_STEP, + + /* simplified version of BOOL_AND_STEP for use by ExecQual() */ + EEOP_QUAL, + + /* unconditional jump to another step */ + EEOP_JUMP, + + /* conditional jumps based on current result value */ + EEOP_JUMP_IF_NULL, + EEOP_JUMP_IF_NOT_NULL, + EEOP_JUMP_IF_NOT_TRUE, + + /* perform NULL tests for scalar values */ + EEOP_NULLTEST_ISNULL, + EEOP_NULLTEST_ISNOTNULL, + + /* perform NULL tests for row values */ + EEOP_NULLTEST_ROWISNULL, + EEOP_NULLTEST_ROWISNOTNULL, + + /* evaluate a BooleanTest expression */ + EEOP_BOOLTEST_IS_TRUE, + EEOP_BOOLTEST_IS_NOT_TRUE, + EEOP_BOOLTEST_IS_FALSE, + EEOP_BOOLTEST_IS_NOT_FALSE, + + /* evaluate PARAM_EXEC/EXTERN parameters */ + EEOP_PARAM_EXEC, + EEOP_PARAM_EXTERN, + + /* return CaseTestExpr value */ + EEOP_CASE_TESTVAL, + + /* apply MakeExpandedObjectReadOnly() to target value */ + EEOP_MAKE_READONLY, + + /* evaluate assorted special-purpose expression types */ + EEOP_IOCOERCE, + EEOP_DISTINCT, + EEOP_NULLIF, + EEOP_CURRENTOFEXPR, + EEOP_ARRAYEXPR, + EEOP_ARRAYCOERCE, + EEOP_ROW, + + /* + * Compare two individual elements of each of two compared ROW() + * expressions. Skip to ROWCOMPARE_FINAL if elements are not equal. + */ + EEOP_ROWCOMPARE_STEP, + + /* evaluate boolean value based on previous ROWCOMPARE_STEP operations */ + EEOP_ROWCOMPARE_FINAL, + + /* evaluate GREATEST() or LEAST() */ + EEOP_MINMAX, + + /* evaluate FieldSelect expression */ + EEOP_FIELDSELECT, + + /* + * Deform tuple before evaluating new values for individual fields in a + * FieldStore expression. + */ + EEOP_FIELDSTORE_DEFORM, + + /* + * Form the new tuple for a FieldStore expression. Individual fields will + * have been evaluated into columns of the tuple deformed by the preceding + * DEFORM step. + */ + EEOP_FIELDSTORE_FORM, + + /* Process an array subscript; short-circuit expression to NULL if NULL */ + EEOP_ARRAYREF_SUBSCRIPT, + + /* + * Compute old array element/slice when an ArrayRef assignment expression + * contains ArrayRef/FieldStore subexpressions. Value is accessed using + * the CaseTest mechanism. + */ + EEOP_ARRAYREF_OLD, + + /* compute new value for ArrayRef assignment expression */ + EEOP_ARRAYREF_ASSIGN, + + /* compute element/slice for ArrayRef fetch expression */ + EEOP_ARRAYREF_FETCH, + + /* evaluate value for CoerceToDomainValue */ + EEOP_DOMAIN_TESTVAL, + + /* evaluate a domain's NOT NULL constraint */ + EEOP_DOMAIN_NOTNULL, + + /* evaluate a single domain CHECK constraint */ + EEOP_DOMAIN_CHECK, + + /* evaluate assorted special-purpose expression types */ + EEOP_CONVERT_ROWTYPE, + EEOP_SCALARARRAYOP, + EEOP_XMLEXPR, + EEOP_AGGREF, + EEOP_GROUPING_FUNC, + EEOP_WINDOW_FUNC, + EEOP_SUBPLAN, + EEOP_ALTERNATIVE_SUBPLAN, + + /* rownum */ + EEOP_ROWNUM, + + /* grouping_id */ + EEOP_GROUPING_ID, + + /* hash_filter */ + EEOP_HASH_FILTER, + + EEOP_USERVAR_OR_SETVARIABLE, + EEOP_USERSET_ELEM, + /* prefix_key */ + EEOP_PREFIX_BTYEA, + EEOP_PREFIX_TEXT, + + /* non-existent operation, used e.g. to check array lengths */ + EEOP_LAST +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + /* + * Instruction to be executed. During instruction preparation this is an + * enum ExprEvalOp, but later it can be changed to some other type, e.g. a + * pointer for computed goto (that's why it's an intptr_t). + */ + intptr_t opcode; + + /* where to store the result of this step */ + Datum *resvalue; + bool *resnull; + + /* + * Inline data for the operation. Inline data is faster to access, but + * also bloats the size of all instructions. The union should be kept to + * no more than 40 bytes on 64-bit systems (so that the entire struct is + * no more than 64 bytes, a single cacheline on common systems). + */ + union + { + /* for EEOP_INNER/OUTER/SCAN_FETCHSOME */ + struct + { + /* attribute number up to which to fetch (inclusive) */ + int last_var; + } fetch; + + /* for EEOP_INNER/OUTER/SCAN_[SYS]VAR[_FIRST] */ + struct + { + /* attnum is attr number - 1 for regular VAR ... */ + /* but it's just the normal (negative) attr number for SYSVAR */ + int attnum; + Oid vartype; /* type OID of variable */ + } var; + + /* for EEOP_WHOLEROW */ + struct + { + Var *var; /* original Var node in plan tree */ + bool first; /* first time through, need to initialize? */ + bool slow; /* need runtime check for nulls? */ + TupleDesc tupdesc; /* descriptor for resulting tuples */ + JunkFilter *junkFilter; /* JunkFilter to remove resjunk cols */ + } wholerow; + + /* for EEOP_ASSIGN_*_VAR */ + struct + { + /* target index in ExprState->resultslot->tts_values/nulls */ + int resultnum; + /* source attribute number - 1 */ + int attnum; + } assign_var; + + /* for EEOP_ASSIGN_TMP[_MAKE_RO] */ + struct + { + /* target index in ExprState->resultslot->tts_values/nulls */ + int resultnum; + } assign_tmp; + + /* for EEOP_CONST */ + struct + { + /* constant's value */ + Datum value; + bool isnull; + bool is_cursor; + Const* con; + } constval; + + /* for EEOP_FUNCEXPR_* / NULLIF / DISTINCT */ + struct + { + char prokind; + bool needResetErrMsg; + int flag; + FmgrInfo *finfo; /* function's lookup data */ + FunctionCallInfo fcinfo_data; /* arguments etc */ + /* faster to access without additional indirection: */ + PGFunction fn_addr; /* actual call address */ + int nargs; /* number of arguments */ + List* args; /* states of argument expressions */ + int* var_dno; + bool is_plpgsql_func_with_outparam; + } func; + + /* for EEOP_BOOL_*_STEP */ + struct + { + bool *anynull; /* track if any input was NULL */ + int jumpdone; /* jump here if result determined */ + } boolexpr; + + /* for EEOP_QUAL */ + struct + { + int jumpdone; /* jump here on false or null */ + } qualexpr; + + /* for EEOP_JUMP[_CONDITION] */ + struct + { + int jumpdone; /* target instruction's index */ + } jump; + + /* for EEOP_NULLTEST_ROWIS[NOT]NULL */ + struct + { + /* cached tupdesc pointer - filled at runtime */ + TupleDesc argdesc; + } nulltest_row; + + /* for EEOP_PARAM_EXEC/EXTERN */ + struct + { + int paramid; /* numeric ID for parameter */ + Oid paramtype; /* OID of parameter's datatype */ + bool is_cursor; + } param; + + /* for EEOP_CASE_TESTVAL/DOMAIN_TESTVAL */ + struct + { + Datum *value; /* value to return */ + bool *isnull; + } casetest; + + /* for EEOP_MAKE_READONLY */ + struct + { + Datum *value; /* value to coerce to read-only */ + bool *isnull; + } make_readonly; + + /* for EEOP_IOCOERCE */ + struct + { + /* lookup and call info for source type's output function */ + FmgrInfo *finfo_out; + FunctionCallInfo fcinfo_data_out; + /* lookup and call info for result type's input function */ + FmgrInfo *finfo_in; + FunctionCallInfo fcinfo_data_in; + } iocoerce; + + /* for EEOP_ARRAYEXPR */ + struct + { + Datum *elemvalues; /* element values get stored here */ + bool *elemnulls; + int nelems; /* length of the above arrays */ + Oid elemtype; /* array element type */ + int16 elemlength; /* typlen of the array element type */ + bool elembyval; /* is the element type pass-by-value? */ + char elemalign; /* typalign of the element type */ + bool multidims; /* is array expression multi-D? */ + } arrayexpr; + + /* for EEOP_ARRAYCOERCE */ + struct + { + ArrayCoerceExpr *coerceexpr; + Oid resultelemtype; /* element type of result array */ + FmgrInfo *elemfunc; /* lookup info for element coercion + * function */ + struct ArrayMapState *amstate; /* workspace for array_map */ + } arraycoerce; + + /* for EEOP_ROW */ + struct + { + TupleDesc tupdesc; /* descriptor for result tuples */ + /* workspace for the values constituting the row: */ + Datum *elemvalues; + bool *elemnulls; + } row; + + /* for EEOP_ROWCOMPARE_STEP */ + struct + { + /* lookup and call data for column comparison function */ + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + /* target for comparison resulting in NULL */ + int jumpnull; + /* target for comparison yielding inequality */ + int jumpdone; + } rowcompare_step; + + /* for EEOP_ROWCOMPARE_FINAL */ + struct + { + RowCompareType rctype; + } rowcompare_final; + + /* for EEOP_MINMAX */ + struct + { + /* workspace for argument values */ + Datum *values; + bool *nulls; + int nelems; + /* is it GREATEST or LEAST? */ + MinMaxOp op; + /* lookup and call data for comparison function */ + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + } minmax; + + /* for EEOP_FIELDSELECT */ + struct + { + AttrNumber fieldnum; /* field number to extract */ + Oid resulttype; /* field's type */ + /* cached tupdesc pointer - filled at runtime */ + TupleDesc argdesc; + } fieldselect; + + /* for EEOP_FIELDSTORE_DEFORM / FIELDSTORE_FORM */ + struct + { + /* original expression node */ + FieldStore *fstore; + + /* cached tupdesc pointer - filled at runtime */ + /* note that a DEFORM and FORM pair share the same tupdesc */ + TupleDesc *argdesc; + + /* workspace for column values */ + Datum *values; + bool *nulls; + int ncolumns; + } fieldstore; + + /* for EEOP_ARRAYREF_SUBSCRIPT */ + struct + { + /* too big to have inline */ + struct ArrayRefState *state; + int off; /* 0-based index of this subscript */ + bool isupper; /* is it upper or lower subscript? */ + int jumpdone; /* jump here on null */ + } arrayref_subscript; + + /* for EEOP_ARRAYREF_OLD / ASSIGN / FETCH */ + struct + { + /* too big to have inline */ + struct ArrayRefState *state; + } arrayref; + + /* for EEOP_DOMAIN_NOTNULL / DOMAIN_CHECK */ + struct + { + /* name of constraint */ + char *constraintname; + /* where the result of a CHECK constraint will be stored */ + Datum *checkvalue; + bool *checknull; + /* OID of domain type */ + Oid resulttype; + } domaincheck; + + /* for EEOP_CONVERT_ROWTYPE */ + struct + { + ConvertRowtypeExpr *convert; /* original expression */ + /* these three fields are filled at runtime: */ + TupleDesc indesc; /* tupdesc for input type */ + TupleDesc outdesc; /* tupdesc for output type */ + TupleConversionMap *map; /* column mapping */ + bool initialized; /* initialized for current types? */ + } convert_rowtype; + + /* for EEOP_SCALARARRAYOP */ + struct + { + /* element_type/typlen/typbyval/typalign are filled at runtime */ + Oid element_type; /* InvalidOid if not yet filled */ + bool useOr; /* use OR or AND semantics? */ + int16 typlen; /* array element type storage info */ + bool typbyval; + char typalign; + FmgrInfo *finfo; /* function's lookup data */ + FunctionCallInfo fcinfo_data; /* arguments etc */ + /* faster to access without additional indirection: */ + PGFunction fn_addr; /* actual call address */ + } scalararrayop; + + /* for EEOP_XMLEXPR */ + struct + { + XmlExpr *xexpr; /* original expression node */ + /* workspace for evaluating named args, if any */ + Datum *named_argvalue; + bool *named_argnull; + /* workspace for evaluating unnamed args, if any */ + Datum *argvalue; + bool *argnull; + } xmlexpr; + + /* for EEOP_AGGREF */ + struct + { + /* out-of-line state, modified by nodeAgg.c */ + AggrefExprState *astate; + } aggref; + + /* for EEOP_GROUPING_FUNC */ + struct + { + AggState *parent; /* parent Agg */ + List *clauses; /* integer list of column numbers */ + } grouping_func; + + /* for EEOP_WINDOW_FUNC */ + struct + { + /* out-of-line state, modified by nodeWindowFunc.c */ + WindowFuncExprState *wfstate; + } window_func; + + /* for EEOP_SUBPLAN */ + struct + { + /* out-of-line state, created by nodeSubplan.c */ + SubPlanState *sstate; + } subplan; + + /* for EEOP_ALTERNATIVE_SUBPLAN */ + struct + { + /* out-of-line state, created by nodeSubplan.c */ + AlternativeSubPlanState *asstate; + } alternative_subplan; + + /* for EEOP_ROWNUM* */ + struct + { + bool typeCompat; + PlanState* RownumState; + } rownum; + + /* for EEOP_USERVAR_OR_SETVARIABLE */ + struct + { + Const* con; + } uservar; + + /* for EEOP_USERSET_ELEM */ + struct + { + UserSetElem* useexpr; + } userset; + + /* for EEOP_PrefixKey */ + struct + { + PrefixKey* pkey; + } prefix_key; + + /* for EEOP_GROUPING_ID* */ + struct + { + AggState* GroupingIdState; + } grouping_id; + + /* for EEOP_HASH_FILTER* */ + struct + { + List* arg; /* input expression */ + uint2* nodelist; /* Node indices where data is located */ + uint2* bucketMap; + int bucketCnt; + Datum* argvalue; + bool* argnull; + List* typeOids; /* type oid list of var for filter */ + } hash_filter; + + } d; +} ExprEvalStep; + + +/* Non-inline data for array operations */ +typedef struct ArrayRefState +{ + bool isassignment; /* is it assignment, or just fetch? */ + + Oid refelemtype; /* OID of the array element type */ + int16 refattrlength; /* typlen of array type */ + int16 refelemlength; /* typlen of the array element type */ + bool refelembyval; /* is the element type pass-by-value? */ + char refelemalign; /* typalign of the element type */ + + /* numupper and upperprovided[] are filled at compile time */ + /* at runtime, extracted subscript datums get stored in upperindex[] */ + int numupper; + bool upperprovided[MAXDIM]; + int upperindex[MAXDIM]; + + /* similarly for lower indexes, if any */ + int numlower; + bool lowerprovided[MAXDIM]; + int lowerindex[MAXDIM]; + + /* subscript expressions get evaluated into here */ + Datum subscriptvalue; + bool subscriptnull; + + /* for assignment, new value to assign is evaluated into here */ + Datum replacevalue; + bool replacenull; + + /* if we have a nested assignment, ARRAYREF_OLD puts old value here */ + Datum prevvalue; + bool prevnull; + + Expr *refexpr; + int refupperindexpr_count; + int plpgsql_index; + Oid typOid; +} ArrayRefState; + +/* functions in execExprInterp.c */ +extern void ExecReadyInterpretedExpr(ExprState *state); +extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op); + +/* + * Non fast-path execution functions. These are externs instead of statics in + * execExprInterp.c, because that allows them to be used by other methods of + * expression evaluation, reducing code duplication. + */ +extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); +extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); +extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op); +extern void ExecEvalRowNull(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); +extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); +extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op); +extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op); +extern void ExecEvalRow(ExprState *state, ExprEvalStep *op); +extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op); +extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); +extern void ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); +extern void ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); +extern bool ExecEvalArrayRefSubscript(ExprState *state, ExprEvalStep *op, ExprContext *econtext); +extern void ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op); +extern void ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op); +extern void ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op); +extern void ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); +extern void ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op); +extern void ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op); +extern void ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op); +extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op); +extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op); +extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); +extern void ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); +extern void ExecEvalHashFilter(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); +extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, + ExprContext *econtext); + +#endif /* EXEC_EXPR_H */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index fb1d9311f..5954bcdb9 100755 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -224,7 +224,8 @@ typedef struct ExprContext { } ExprContext; /* - * Set-result status returned by ExecEvalExpr() + * Set-result status used when evaluating functions potentially returning a + * set. */ typedef enum { ExprSingleResult, /* expression does not return a set */ @@ -265,76 +266,6 @@ typedef struct ReturnSetInfo { TupleDesc setDesc; /* actual descriptor for returned tuples */ } ReturnSetInfo; -/* ---------------- - * ProjectionInfo node information - * - * This is all the information needed to perform projections --- - * that is, form new tuples by evaluation of targetlist expressions. - * Nodes which need to do projections create one of these. - * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) - * exprContext expression context in which to evaluate targetlist - * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) - * pi_maxOrmin column table optimize, indicate if get this column's max or min. - * ---------------- - */ -typedef bool (*vectarget_func)(ExprContext* econtext, VectorBatch* pBatch); -typedef struct ProjectionInfo { - NodeTag type; - List* pi_targetlist; - ExprContext* pi_exprContext; - TupleTableSlot* pi_slot; - ExprDoneCond* pi_itemIsDone; - bool pi_directMap; - bool pi_topPlan; /* Whether the outermost layer query */ - int pi_numSimpleVars; - int* pi_varSlotOffsets; - int* pi_varNumbers; - int* pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; - List* pi_acessedVarNumbers; - List* pi_sysAttrList; - List* pi_lateAceessVarNumbers; - List* pi_maxOrmin; - List* pi_PackTCopyVars; /* VarList to record those columns what we need to move */ - List* pi_PackLateAccessVarNumbers; /*VarList to record those columns what we need to move in late read cstore - scan.*/ - bool pi_const; - VectorBatch* pi_batch; - vectarget_func jitted_vectarget; /* LLVM function pointer to point to the codegened targetlist expr function */ - VectorBatch* pi_setFuncBatch; - bool isUpsertHasRightRef; -} ProjectionInfo; - /* * Function pointer which will be used by LLVM assemble. The created IR functions * will be added to the actual machine code. @@ -424,17 +355,66 @@ typedef struct MergeState { * pointer to the routine to execute to evaluate the node. * ---------------- */ -typedef struct ExprState ExprState; +struct ExprState; typedef Datum (*ExprStateEvalFunc)(ExprState* expression, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); typedef ScalarVector* (*VectorExprFun)( ExprState* expression, ExprContext* econtext, bool* selVector, ScalarVector* inputVector, ExprDoneCond* isDone); typedef void* (*exprFakeCodeGenSig)(void*); + +/* Bits in ExprState->flags (see also execExpr.h for private flag bits): */ +/* expression is for use with ExecQual() */ +#define EEO_FLAG_IS_QUAL (1 << 0) + struct ExprState { - NodeTag type; - Expr* expr; /* associated Expr node */ - ExprStateEvalFunc evalfunc; /* routine to run to execute node */ + NodeTag type; + + uint8 flags; /* bitmask of EEO_FLAG_* bits, see above */ + + /* + * Storage for result value of a scalar expression, or for individual + * column results within expressions built by ExecBuildProjectionInfo(). + */ + bool resnull; + Datum resvalue; + + /* + * If projecting a tuple result, this slot holds the result; else NULL. + */ + TupleTableSlot *resultslot; + + /* + * Instructions to compute expression's return value. + */ + struct ExprEvalStep *steps; + + /* + * Function that actually evaluates the expression. This can be set to + * different values depending on the complexity of the expression. + */ + ExprStateEvalFunc evalfunc; + + /* original expression tree, for debugging only */ + Expr *expr; + + /* + * XXX: following fields only needed during "compilation" (ExecInitExpr); + * could be thrown away afterwards. + */ + + int steps_len; /* number of steps currently */ + int steps_alloc; /* allocated length of steps array */ + + struct PlanState *parent; /* parent PlanState node, if any */ + + Datum *innermost_caseval; + bool *innermost_casenull; + + Datum *innermost_domainval; + bool *innermost_domainnull; + + ListCell *current_targetentry; // vectorized evaluator // @@ -442,11 +422,86 @@ struct ExprState { exprFakeCodeGenSig exprCodeGen; /* routine to run llvm assembler function */ + bool is_flt_frame; /*Indicates whether it is a flattened expr frame */ ScalarVector tmpVector; Oid resultType; }; +/* ---------------- + * ProjectionInfo node information + * + * This is all the information needed to perform projections --- + * that is, form new tuples by evaluation of targetlist expressions. + * Nodes which need to do projections create one of these. + * + * ExecProject() evaluates the tlist, forms a tuple, and stores it + * in the given slot. Note that the result will be a "virtual" tuple + * unless ExecMaterializeSlot() is then called to force it to be + * converted to a physical tuple. The slot must have a tupledesc + * that matches the output of the tlist! + * + * The planner very often produces tlists that consist entirely of + * simple Var references (lower levels of a plan tree almost always + * look like that). And top-level tlists are often mostly Vars too. + * We therefore optimize execution of simple-Var tlist entries. + * The pi_targetlist list actually contains only the tlist entries that + * aren't simple Vars, while those that are Vars are processed using the + * varSlotOffsets/varNumbers/varOutputCols arrays. + * + * The lastXXXVar fields are used to optimize fetching of fields from + * input tuples: they let us do a slot_getsomeattrs() call to ensure + * that all needed attributes are extracted in one pass. + * + * targetlist target list for projection (non-Var expressions only) + * exprContext expression context in which to evaluate targetlist + * slot slot to place projection result in + * itemIsDone workspace array for ExecProject + * directMap true if varOutputCols[] is an identity map + * numSimpleVars number of simple Vars found in original tlist + * varSlotOffsets array indicating which slot each simple Var is from + * varNumbers array containing input attr numbers of simple Vars + * varOutputCols array containing output attr numbers of simple Vars + * lastInnerVar highest attnum from inner tuple slot (0 if none) + * lastOuterVar highest attnum from outer tuple slot (0 if none) + * lastScanVar highest attnum from scan tuple slot (0 if none) + * pi_maxOrmin column table optimize, indicate if get this column's max or min. + * ---------------- + */ +typedef bool (*vectarget_func)(ExprContext* econtext, VectorBatch* pBatch); +typedef struct ProjectionInfo { + NodeTag type; + List* pi_targetlist; + /* instructions to evaluate projection */ + ExprState pi_state; + /* expression context in which to evaluate expression */ + ExprContext* pi_exprContext; + TupleTableSlot* pi_slot; + ExprDoneCond* pi_itemIsDone; + bool pi_directMap; + bool pi_topPlan; /* Whether the outermost layer query */ + int pi_numSimpleVars; + int* pi_varSlotOffsets; + int* pi_varNumbers; + int* pi_varOutputCols; + int pi_lastInnerVar; + int pi_lastOuterVar; + int pi_lastScanVar; + List* pi_acessedVarNumbers; + List* pi_sysAttrList; + List* pi_lateAceessVarNumbers; + List* pi_maxOrmin; + List* pi_PackTCopyVars; /* VarList to record those columns what we need to move */ + List* pi_PackLateAccessVarNumbers; /*VarList to record those columns what we need to move in late read cstore + scan.*/ + bool pi_const; + ExprDoneCond* pi_vec_itemIsDone; + VectorBatch* pi_batch; + vectarget_func jitted_vectarget; /* LLVM function pointer to point to the codegened targetlist expr function */ + VectorBatch* pi_setFuncBatch; + bool isUpsertHasRightRef; +} ProjectionInfo; + /* ---------------- * ResultRelInfo information * @@ -557,6 +612,8 @@ typedef struct EState { /* If query can insert/delete tuples, the command ID to mark them with */ CommandId es_output_cid; + bool es_is_flt_frame; /*Indicates whether it is a flattened expr frame */ + /* Info about target table(s) for insert/update/delete queries: */ ResultRelInfo* es_result_relations; /* array of ResultRelInfos */ int es_num_result_relations; /* length of array */ @@ -805,6 +862,7 @@ typedef struct WholeRowVarExprState { */ typedef struct AggrefExprState { ExprState xprstate; + Aggref *aggref; /* expression plan node */ List* aggdirectargs; /* states of direct-argument expressions */ List* args; /* states of argument expressions */ ExprState* aggfilter; /* state of FILTER expression, if any */ @@ -821,6 +879,7 @@ typedef struct AggrefExprState { */ typedef struct WindowFuncExprState { ExprState xprstate; + WindowFunc *wfunc; /* expression plan node */ List* args; /* states of argument expressions */ int wfuncno; /* ID number for wfunc within its plan node */ @@ -829,6 +888,7 @@ typedef struct WindowFuncExprState { ScalarVector* m_resultVector; } WindowFuncExprState; + /* ---------------- * ArrayRefExprState node * @@ -899,12 +959,11 @@ typedef struct FuncExprState { */ bool setArgsValid; - /* - * Flag to remember whether we found a set-valued argument to the - * function. This causes the function result to be a set as well. Valid - * only when setArgsValid is true or funcResultStore isn't NULL. - */ - bool setHasSetArg; /* some argument returns a set */ + bool setHasSetArg; + + bool is_plpgsql_func_with_outparam; + + bool has_refcursor; /* * Flag to remember whether we have registered a shutdown callback for @@ -922,6 +981,7 @@ typedef struct FuncExprState { FunctionCallInfoData fcinfo_data; ScalarVector* tmpVec; + bool vec_setHasSetArg; /* some argument returns a set */ } FuncExprState; /* ---------------- @@ -1002,6 +1062,7 @@ typedef struct SubPlanState { */ typedef struct AlternativeSubPlanState { ExprState xprstate; + AlternativeSubPlan* subplan; /* expression plan node */ List* subplans; /* states of alternative subplans */ int active; /* list index of the one we're using */ } AlternativeSubPlanState; @@ -1217,7 +1278,8 @@ typedef struct DomainConstraintState { NodeTag type; DomainConstraintType constrainttype; /* constraint type */ char* name; /* name of constraint (for error msgs) */ - ExprState* check_expr; /* for CHECK, a boolean expression */ + Expr *check_node; /* for check, expr node,for flatten*/ + ExprState *check_expr; /* for CHECK, a boolean expression */ } DomainConstraintState; typedef struct HbktScanSlot { @@ -1288,14 +1350,13 @@ typedef struct PlanState { * State for management of parameter-change-driven rescanning */ Bitmapset* chgParam; /* set of IDs of changed Params */ - + /* * Other run-time state needed by most if not all node types. */ TupleTableSlot* ps_ResultTupleSlot; /* slot for my result tuples */ ExprContext* ps_ExprContext; /* node's expression-evaluation context */ ProjectionInfo* ps_ProjInfo; /* info for doing tuple projection */ - bool ps_TupFromTlist; /* state flag for processing set-valued functions in targetlist */ int64 ps_rownum; /* store current rownum */ List* targetlist; /* target list to be computed at this node */ @@ -1410,6 +1471,7 @@ typedef struct UpsertState */ typedef struct ProjectSetState { PlanState ps; /* its first field is NodeTag */ + Node **elems; /* array of expression states */ ExprDoneCond *elemdone; /* array of per-SRF is-done states */ int nelems; /* length of elemdone[] array */ bool pending_srf_tuples; /* still evaluating srfs in tlist? */ @@ -1736,7 +1798,7 @@ typedef struct ScanState { bool isSampleScan; /* identify is it table sample scan or not. */ bool runTimePredicatesReady; bool is_scan_end; /* @hdfs Mark whether iterator is over or not, if the scan uses informational constraint. */ - + int currentSlot; /* current iteration position */ int part_id; int startPartitionId; /* start partition id for parallel threads. */ @@ -1927,7 +1989,7 @@ typedef struct BitmapHeapScanState { */ typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List* tss_tidquals; /* list of ExprState nodes */ + List* tss_tidexprs; bool tss_isCurrentOf; Relation tss_CurrentOf_CurrentPartition; int tss_NumTids; @@ -2378,6 +2440,7 @@ typedef struct GroupState { */ /* these structs are private in nodeAgg.c: */ typedef struct AggStatePerAggData* AggStatePerAgg; +typedef struct AggStatePerTransData *AggStatePerTrans; typedef struct AggStatePerGroupData* AggStatePerGroup; typedef struct AggStatePerPhaseData* AggStatePerPhase; diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 4376b8c88..9136dbb9a 100755 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -272,6 +272,7 @@ typedef enum NodeTag { T_FuncExprState, T_ScalarArrayOpExprState, T_BoolExprState, + T_SetExprState, T_SubPlanState, T_AlternativeSubPlanState, T_FieldSelectState, diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 65a80bd29..737cc06be 100755 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -157,7 +157,6 @@ typedef struct OpMemInfo { /* PSORT_SPREAD_MAXMEM_RATIO can increase 20% for partition table's one part on extended limit. */ #define PSORT_SPREAD_MAXMEM_RATIO 1.2 - /* ---------- * PlannerGlobal * Global information for planning/optimization @@ -361,6 +360,12 @@ typedef struct PlannerInfo { List* distinct_pathkeys; /* distinctClause pathkeys, if any */ List* sort_pathkeys; /* sortClause pathkeys, if any */ + /* Use fetch_upper_rel() to get any particular upper rel */ + List *upper_rels[UPPERREL_FINAL + 1]; /* upper-rel RelOptInfos */ + + /* Result tlists chosen by grouping_planner for upper-stage processing */ + struct PathTarget *upper_targets[UPPERREL_FINAL + 1]; + List* minmax_aggs; /* List of MinMaxAggInfos */ List* initial_rels; /* RelOptInfos we are now trying to join */ diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h index 91a4f96b5..e92cc7bc6 100644 --- a/src/include/optimizer/tlist.h +++ b/src/include/optimizer/tlist.h @@ -62,7 +62,7 @@ extern void add_new_column_to_pathtarget(PathTarget *target, Expr *expr); extern void add_new_columns_to_pathtarget(PathTarget *target, List *exprs); extern bool split_pathtarget_at_srfs(PlannerInfo *root, PathTarget *target, PathTarget *input_target, List **targets, List **targets_contain_srfs); - + /* Convenience macro to get a PathTarget with valid cost/width fields */ #define create_pathtarget(root, tlist) \ set_pathtarget_cost_width(root, make_pathtarget_from_tlist(tlist)) diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index 1c76dc0d8..a4733f7bd 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -25,6 +25,8 @@ errcontext.previous = t_thrd.log_cxt.error_context_stack; \ t_thrd.log_cxt.error_context_stack = &errcontext; +#define ELOG_FIELD_NAME_UPDATE(fieldname) t_thrd.log_cxt.error_context_stack->arg = (void*)(fieldname); + #define ELOG_FIELD_NAME_END t_thrd.log_cxt.error_context_stack = errcontext.previous; /* Type categories (see TYPCATEGORY_xxx symbols in catalog/pg_type.h) */ diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 71598f793..9a398936e 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -119,6 +119,9 @@ /* Define to 1 if you have the `class' function. */ #undef HAVE_CLASS +/* Define to 1 if your compiler handles computed gotos. */ +#undef HAVE_COMPUTED_GOTO + /* Define to 1 if you have the header file. */ #undef HAVE_CRTDEFS_H diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32 index 8751cec7b..c44f9280d 100644 --- a/src/include/pg_config.h.win32 +++ b/src/include/pg_config.h.win32 @@ -78,6 +78,9 @@ /* Define to 1 if you have the `class' function. */ /* #undef HAVE_CLASS */ +/* Define to 1 if your compiler handles computed gotos. */ +/* #undef HAVE_COMPUTED_GOTO */ + /* Define to 1 if you have the `crypt' function. */ /* #undef HAVE_CRYPT */ diff --git a/src/include/utils/expandeddatum.h b/src/include/utils/expandeddatum.h new file mode 100644 index 000000000..2d50082ef --- /dev/null +++ b/src/include/utils/expandeddatum.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * expandeddatum.h + * Declarations for access to "expanded" value representations. + * + * Complex data types, particularly container types such as arrays and + * records, usually have on-disk representations that are compact but not + * especially convenient to modify. What's more, when we do modify them, + * having to recopy all the rest of the value can be extremely inefficient. + * Therefore, we provide a notion of an "expanded" representation that is used + * only in memory and is optimized more for computation than storage. + * The format appearing on disk is called the data type's "flattened" + * representation, since it is required to be a contiguous blob of bytes -- + * but the type can have an expanded representation that is not. Data types + * must provide means to translate an expanded representation back to + * flattened form. + * + * An expanded object is meant to survive across multiple operations, but + * not to be enormously long-lived; for example it might be a local variable + * in a PL/pgSQL procedure. So its extra bulk compared to the on-disk format + * is a worthwhile trade-off. + * + * References to expanded objects are a type of TOAST pointer. + * Because of longstanding conventions in Postgres, this means that the + * flattened form of such an object must always be a varlena object. + * Fortunately that's no restriction in practice. + * + * There are actually two kinds of TOAST pointers for expanded objects: + * read-only and read-write pointers. Possession of one of the latter + * authorizes a function to modify the value in-place rather than copying it + * as would normally be required. Functions should always return a read-write + * pointer to any new expanded object they create. Functions that modify an + * argument value in-place must take care that they do not corrupt the old + * value if they fail partway through. + * + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/utils/expandeddatum.h + * + *------------------------------------------------------------------------- + */ +#ifndef EXPANDEDDATUM_H +#define EXPANDEDDATUM_H + + +extern Datum MakeExpandedObjectReadOnlyInternal(Datum d); + + +#endif /* EXPANDEDDATUM_H */ \ No newline at end of file diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h index a8be6f789..f85c97a90 100644 --- a/src/include/utils/xml.h +++ b/src/include/utils/xml.h @@ -66,7 +66,10 @@ extern bool pg_xml_error_occurred(PgXmlErrorContext* errcxt); extern void xml_ereport(PgXmlErrorContext* errcxt, int level, int sqlcode, const char* msg); extern xmltype* xmlconcat(List* args); -extern xmltype* xmlelement(XmlExprState* xmlExpr, ExprContext* econtext); +extern xmltype* xmlelement(XmlExprState *xexpr, ExprContext* econtext); +extern xmltype* xmlelementByFlatten(XmlExpr *xexpr, + Datum *named_argvalue, bool *named_argnull, + Datum *argvalue, bool *argnull); extern xmltype* xmlparse(text* data, XmlOptionType xmloption, bool preserve_whitespace); extern xmltype* xmlpi(char* target, text* arg, bool arg_is_null, bool* result_is_null); extern xmltype* xmlroot(xmltype* data, text* version, int standalone); diff --git a/src/test/regress/expected/enable_expr_fusion_flatten.out b/src/test/regress/expected/enable_expr_fusion_flatten.out new file mode 100644 index 000000000..46c0fa078 --- /dev/null +++ b/src/test/regress/expected/enable_expr_fusion_flatten.out @@ -0,0 +1,800 @@ +show enable_expr_fusion; + enable_expr_fusion +-------------------- + off +(1 row) + +set enable_expr_fusion = on; +show enable_expr_fusion; + enable_expr_fusion +-------------------- + on +(1 row) + +drop table if exists t1; +NOTICE: table "t1" does not exist, skipping +create table t1 (id int, score int, pk int); +insert into t1 values(1,1,1); +insert into t1 values(1,2,1); +insert into t1 values(1,3,1); +insert into t1 values(2,1,1); +insert into t1 values(1,1,2); +select grouping(id,score), sum(pk), id, score from t1 group by rollup(id,score) order by +grouping, sum, id, score; + grouping | sum | id | score +----------+-----+----+------- + 0 | 1 | 1 | 2 + 0 | 1 | 1 | 3 + 0 | 1 | 2 | 1 + 0 | 3 | 1 | 1 + 1 | 1 | 2 | + 1 | 5 | 1 | + 3 | 6 | | +(7 rows) + +select * from t1 where (id =10) or (score = 10) or (pk =1 and score = 2 and id =1); + id | score | pk +----+-------+---- + 1 | 2 | 1 +(1 row) + +select * from t1 where id is unknown; + id | score | pk +----+-------+---- +(0 rows) + +select * from t1 where id is not unknown; + id | score | pk +----+-------+---- + 1 | 1 | 1 + 1 | 2 | 1 + 1 | 3 | 1 + 2 | 1 | 1 + 1 | 1 | 2 +(5 rows) + +select rownum from t1 ; + rownum +-------- + 1 + 2 + 3 + 4 + 5 +(5 rows) + +select * from t1 where id is distinct from score; + id | score | pk +----+-------+---- + 1 | 2 | 1 + 1 | 3 | 1 + 2 | 1 | 1 +(3 rows) + +create type newtype as (a int ,b int); +create table test_flt(a newtype, b int); +insert into test_flt values (ROW(1,2),3); +update test_flt a set a.a = 12; +NOTICE: update field 'a' of column 'a', though it's ambiguous. +select * from test_flt; + a | b +--------+--- + (12,2) | 3 +(1 row) + +drop table if exists test_flt; +drop type newtype; +select (1,2) > (3,4); + ?column? +---------- + f +(1 row) + +select (1,2) >= (3,4); + ?column? +---------- + f +(1 row) + +select (1,2) < (3,4); + ?column? +---------- + t +(1 row) + +select (1,2) <= (3,4); + ?column? +---------- + t +(1 row) + +select id is true from t1; + ?column? +---------- + t + t + t + t + t +(5 rows) + +select id is false from t1; + ?column? +---------- + f + f + f + f + f +(5 rows) + +select id is not true from t1; + ?column? +---------- + f + f + f + f + f +(5 rows) + +select id is not false from t1; + ?column? +---------- + t + t + t + t + t +(5 rows) + +select * from t1 where id is not null; + id | score | pk +----+-------+---- + 1 | 1 | 1 + 1 | 2 | 1 + 1 | 3 | 1 + 2 | 1 | 1 + 1 | 1 | 2 +(5 rows) + +select nullif(id,'') from t1; + nullif +-------- + 1 + 1 + 1 + 2 + 1 +(5 rows) + +create table employees ( + salary numeric CHECK(salary > 0) +); +insert into employees values (-100); +ERROR: new row for relation "employees" violates check constraint "employees_salary_check" +DETAIL: N/A +drop table if exists employees; +create sequence sequence_test2_flt start with 32; +create sequence sequence_test3 + increment by 1 + minvalue 1 maxvalue 30 + start 1 + cache 5; +NOTICE: Not advised to use MAXVALUE or MINVALUE together with CACHE. +DETAIL: If CACHE is defined, some sequence values may be wasted, causing available sequence numbers to be less than expected. +SELECT * FROM information_schema.sequences WHERE sequence_name IN + ('sequence_test2_flt', 'sequence_test3', 'serialtest2_f3_seq', + 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') + ORDER BY sequence_name ASC; + sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option +------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- + regression | public | sequence_test2_flt | int16 | 128 | 2 | 0 | 32 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | int16 | 128 | 2 | 0 | 1 | 1 | 30 | 1 | NO +(2 rows) + +CREATE TYPE compfoo AS (f1 int, f2 text); +create table t_group_array (a compfoo[], b int, c int[]); +insert into t_group_array (a[5].f1) values(32); +insert into t_group_array values(array[cast((1,'syr') as compfoo),cast((2,'sss') as compfoo)],1,array[1,2]); +insert into t_group_array (a[5].f1) values(32); +update t_group_array set a[5].f2='sss'; +drop table t_group_array; +drop type compfoo; +create table test_d(a int[3],b int); +create table test_s(a int[3],b int); +insert into test_d values('{1,2,3}',4); +insert into test_s values('{10,20,30}',4); +merge into test_d using test_s on(test_d.b=test_s.b) when matched then update set test_d.a=test_s.a; +select * from test_d; + a | b +------------+--- + {10,20,30} | 4 +(1 row) + +truncate table test_s; +insert into test_s values('{11,21,31}',4); +merge into test_d d using test_s on(d.b=test_s.b) when matched then update set d.a=test_s.a; +select * from test_d; + a | b +------------+--- + {11,21,31} | 4 +(1 row) + +--must compatible with previous features, though not perfect +merge into test_d using test_s on(test_d.b=test_s.b) when matched then update set test_d.a[1,3]=test_s.a[1,3]; +select * from test_d; + a | b +------------+--- + {11,21,31} | 4 +(1 row) + +merge into test_d d using test_s on(d.b=test_s.b) when matched then update set d.a[1,3]=test_s.a[1,3]; +select * from test_d; + a | b +------------+--- + {11,21,31} | 4 +(1 row) + +drop table test_d; +drop table test_s; +create table xcreturn_tab1 (id int, v varchar); +ALTER TABLE xcreturn_tab1 ADD PRIMARY KEY(id); +NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "xcreturn_tab1_pkey" for table "xcreturn_tab1" +create table xcreturn_tab2 (id int, v varchar); +ALTER TABLE xcreturn_tab2 ADD PRIMARY KEY(id); +NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "xcreturn_tab2_pkey" for table "xcreturn_tab2" +insert into xcreturn_tab1 values (1, 'firstrow'), (2, 'secondrow'); +WITH wcte AS ( INSERT INTO xcreturn_tab2 VALUES (999, 'opop'), (333, 'sss') , ( 42, 'new' ), (55, 'ppp') RETURNING id AS newid ) +UPDATE xcreturn_tab1 SET id = id + newid FROM wcte; +drop table xcreturn_tab1, xcreturn_tab2; +CREATE TABLE INT4_FLT(f1 int4); +INSERT INTO INT4_FLT(f1) VALUES (' 0 '); +INSERT INTO INT4_FLT(f1) VALUES ('123456 '); +INSERT INTO INT4_FLT(f1) VALUES (' -123456'); +INSERT INTO INT4_FLT(f1) VALUES ('34.5'); +ERROR: invalid input syntax for integer: "34.5" +LINE 1: INSERT INTO INT4_FLT(f1) VALUES ('34.5'); + ^ +CONTEXT: referenced column: f1 +-- largest and smallest values +INSERT INTO INT4_FLT(f1) VALUES ('2147483647'); +INSERT INTO INT4_FLT(f1) VALUES ('-2147483647'); +-- bad input values -- should give errors +INSERT INTO INT4_FLT(f1) VALUES ('1000000000000'); +ERROR: value "1000000000000" is out of range for type integer +LINE 1: INSERT INTO INT4_FLT(f1) VALUES ('1000000000000'); + ^ +CONTEXT: referenced column: f1 +INSERT INTO INT4_FLT(f1) VALUES ('asdf'); +ERROR: invalid input syntax for integer: "asdf" +LINE 1: INSERT INTO INT4_FLT(f1) VALUES ('asdf'); + ^ +CONTEXT: referenced column: f1 +INSERT INTO INT4_FLT(f1) VALUES (' '); +ERROR: invalid input syntax for integer: " " +LINE 1: INSERT INTO INT4_FLT(f1) VALUES (' '); + ^ +CONTEXT: referenced column: f1 +INSERT INTO INT4_FLT(f1) VALUES (' asdf '); +ERROR: invalid input syntax for integer: " asdf " +LINE 1: INSERT INTO INT4_FLT(f1) VALUES (' asdf '); + ^ +CONTEXT: referenced column: f1 +INSERT INTO INT4_FLT(f1) VALUES ('- 1234'); +ERROR: invalid input syntax for integer: "- 1234" +LINE 1: INSERT INTO INT4_FLT(f1) VALUES ('- 1234'); + ^ +CONTEXT: referenced column: f1 +INSERT INTO INT4_FLT(f1) VALUES ('123 5'); +ERROR: invalid input syntax for integer: "123 5" +LINE 1: INSERT INTO INT4_FLT(f1) VALUES ('123 5'); + ^ +CONTEXT: referenced column: f1 +INSERT INTO INT4_FLT(f1) VALUES (''); +select q from (select max(f1) from int4_FLT order by f1) q; +ERROR: column "int4_flt.f1" must appear in the GROUP BY clause or be used in an aggregate function +LINE 1: select q from (select max(f1) from int4_FLT order by f1) q; + ^ +drop table INT4_FLT; +with r(a,b) as materialized + (values (1,row(1,2)), (1,row(null,null)), (1,null), + (null,row(1,2)), (null,row(null,null)), (null,null) ) +select r, r is null as isnull, r is not null as isnotnull from r; + r | isnull | isnotnull +-------------+--------+----------- + (1,"(1,2)") | f | t + (1,"(,)") | f | t + (1,) | f | f + (,"(1,2)") | f | f + (,"(,)") | f | f + (,) | t | f +(6 rows) + +drop table if exists t1; +drop table if exists t2; +NOTICE: table "t2" does not exist, skipping +drop table if exists t3; +NOTICE: table "t3" does not exist, skipping +create table t1 (a int primary key, b int, c int); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1" +create table t2 (a int primary key, b int, c int); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t2_pkey" for table "t2" +create table t3 (a int primary key, b int, c int); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t3_pkey" for table "t3" +-- insert some data to suppress no statistics warning +insert into t1 values(1,1,1); +insert into t2 values(1,1,1); +insert into t3 values(1,1,1); +EXPLAIN (verbose, costs off) select * from t1 where exists (select /*+ noEXPLAIN (verbose, costs off)and*/ t2.a from t2 where t2.a = t1.a); +WARNING: LINE 1: syntax error at 'noEXPLAIN' + QUERY PLAN +----------------------------------- + Hash Join + Output: t1.a, t1.b, t1.c + Hash Cond: (t1.a = t2.a) + -> Seq Scan on public.t1 + Output: t1.a, t1.b, t1.c + -> Hash + Output: t2.a + -> Seq Scan on public.t2 + Output: t2.a +(9 rows) + +select * from t1 where exists (select /*+ noEXPLAIN (verbose, costs off)and*/ t2.a from t2 where t2.a = t1.a); + a | b | c +---+---+--- + 1 | 1 | 1 +(1 row) + +EXPLAIN (verbose, costs off) select * from t1 where t1.a in (select /*+ noEXPLAIN (verbose, costs off)and*/ /*+ noEXPLAIN (verbose, costs off)and noEXPLAIN (verbose, costs off)and*/ t2.a from t2); +WARNING: LINE 1: syntax error at 'noEXPLAIN' + QUERY PLAN +----------------------------------- + Hash Join + Output: t1.a, t1.b, t1.c + Hash Cond: (t1.a = t2.a) + -> Seq Scan on public.t1 + Output: t1.a, t1.b, t1.c + -> Hash + Output: t2.a + -> Seq Scan on public.t2 + Output: t2.a +(9 rows) + +select * from t1 where t1.a in (select /*+ noEXPLAIN (verbose, costs off)and*/ /*+ noEXPLAIN (verbose, costs off)and noEXPLAIN (verbose, costs off)and*/ t2.a from t2); + a | b | c +---+---+--- + 1 | 1 | 1 +(1 row) + +drop table if exists t1; +drop table if exists t2; +drop table if exists t3; +create database test_select_into_var dbcompatibility 'b'; +\c test_select_into_var +set enable_expr_fusion = on; +drop table if exists t; +NOTICE: table "t" does not exist, skipping +create table t(i int, t text, b bool, f float, bi bit(3), vbi bit varying(5)); +insert into t(i, t, b, f, bi, vbi) +values(1, 'aaa', true, 1.11, B'101', B'00'), + (2, 'bbb', false, 2.22, B'100', B'10'), + (3, null, true, 3.33, B'101', B'00'), + (4, 'ddd', null, 4.44, B'100', B'10'), + (5, 'eee', false, null, B'101', B'00'), + (6, 'fff', true, 6.66, null, B'00'), + (7, 'ggg', false, 7.77, B'100', null), + (null, 'hhh', true, 8.88, B'101', B'10'); +select * from t where i in (1,2); + i | t | b | f | bi | vbi +---+-----+---+------+-----+----- + 1 | aaa | t | 1.11 | 101 | 00 + 2 | bbb | f | 2.22 | 100 | 10 +(2 rows) + +\c regression +drop database if exists test_select_into_var; +set enable_expr_fusion = on; +create table t1(a int); +create server my_server foreign data wrapper file_fdw; +create foreign table test_fore(id int) server my_server options(filename '/tmp'); +select table_name,external from adm_tables where table_name = 'test_fore' or table_name = 't1'; +ERROR: relation "adm_tables" does not exist on datanode1 +LINE 1: select table_name,external from adm_tables where table_name ... + ^ +drop table t1; +drop foreign table test_fore; +select ROW(1,2,NULL) < ROW(1,3,0); + ?column? +---------- + t +(1 row) + +select * from ADM_ARGUMENTS where OBJECT_NAME = 'proarg01'; +ERROR: relation "adm_arguments" does not exist on datanode1 +LINE 1: select * from ADM_ARGUMENTS where OBJECT_NAME = 'proarg01'; + ^ +create table t1 (c1 int primary key, c2 int); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1" +create index t1_c2_idx on t1 using btree(c2); +create table t2 (c1 int, c2 int); +create index t2_c2_idx on t2 using btree(c2); +-- insert some data to suppress no statistics warning +insert into t1 values(1,1); +insert into t2 values(1,1); +insert into t1 select c1, c2 from t2 where c2 = 1 on duplicate key update c2 = c2 + 1; +drop table t1; +drop table t2; +CREATE TABLE hash_hash +( + col_1 int , + col_2 int NOT NULL , + col_3 VARCHAR2 ( 30 ) , + col_4 int +) +PARTITION BY hash (col_3) SUBPARTITION BY hash (col_2) +( + PARTITION p_hash_1 + ( + SUBPARTITION p_hash_1_1 , + SUBPARTITION p_hash_1_2 , + SUBPARTITION p_hash_1_3 , + SUBPARTITION p_hash_1_4 + ), + PARTITION p_hash_2 + ( + SUBPARTITION p_hash_2_1 , + SUBPARTITION p_hash_2_2 + ), + PARTITION p_hash_3, + PARTITION p_hash_4 + ( + SUBPARTITION p_hash_4_1 + ), + PARTITION p_hash_5 +); +INSERT INTO hash_hash VALUES(null,1,1,1); +alter table hash_hash add constraint con_hash_hash check(col_1 is not null) NOT VALID ; +INSERT INTO hash_hash VALUES(null,2,1,1); --error +ERROR: new row for relation "hash_hash" violates check constraint "con_hash_hash" +DETAIL: N/A +INSERT INTO hash_hash VALUES(1,3,1,1); --success +alter table hash_hash VALIDATE CONSTRAINT con_hash_hash; --error +ERROR: check constraint "con_hash_hash" is violated by some row +delete from hash_hash where col_1 is null; +alter table hash_hash VALIDATE CONSTRAINT con_hash_hash; --success +drop table hash_hash cascade; +create table test_range (a int, b int, c int) WITH (STORAGE_TYPE=USTORE) +partition by range(a) +( + partition p1 values less than (2000), + partition p2 values less than (3000), + partition p3 values less than (4000), + partition p4 values less than (5000), + partition p5 values less than (maxvalue) +)ENABLE ROW MOVEMENT; +insert into test_range values(1,1,1); +insert into test_range values(3001,1,1); +prepare p1 as select * from test_range where ctid = '(0,1)' and a = $1; +explain (costs off)execute p1(1); + QUERY PLAN +------------------------------------ + Partitioned Tid Scan on test_range + TID Cond: (ctid = '(0,1)'::tid) + Filter: (a = $1) + Selected Partitions: PART +(4 rows) + +execute p1(1); + a | b | c +---+---+--- + 1 | 1 | 1 +(1 row) + +execute p1(3001); + a | b | c +------+---+--- + 3001 | 1 | 1 +(1 row) + +drop table test_range; +set client_encoding ='UTF8'; +drop table if exists test_area; +NOTICE: table "test_area" does not exist, skipping +create table test_area(id int4,name text, fatherid int4, name_desc text); +insert into test_area values (1, '中国', 0, 'China'); +insert into test_area values (2, '湖南省',1 , 'Hunan'); +insert into test_area values (3, '广东省',1 , 'Guangdong'); +insert into test_area values (4, '海南省',1 , 'Hainan'); +insert into test_area values (5, '河北省',1 , 'Hebei'); +insert into test_area values (6, '河南省',1 , 'Henan'); +insert into test_area values (7, '山东省',1 , 'Shandong'); +insert into test_area values (8, '湖北省',1 , 'Hubei'); +insert into test_area values (9, '江苏省',1 , 'Jiangsu'); +insert into test_area values (10,'深圳市',3 , 'Shenzhen'); +insert into test_area values (11,'长沙市',2 , 'Changsha'); +insert into test_area values (22,'祁北县',13, 'Qibei'); +insert into test_area values (12,'南山区',10, 'Nanshan'); +insert into test_area values (21,'祁西县',13, 'Qixi'); +insert into test_area values (13,'衡阳市',2 , 'Hengyang'); +insert into test_area values (14,'耒阳市',13, 'Leiyang'); +insert into test_area values (15,'龙岗区',10, 'Longgang'); +insert into test_area values (16,'福田区',10, 'Futian'); +insert into test_area values (17,'宝安区',10, 'Baoan'); +insert into test_area values (19,'祁东县',13, 'Qidong'); +insert into test_area values (18,'常宁市',13, 'Changning'); +insert into test_area values (20,'祁南县',13, 'Qinan'); +EXPLAIN (COSTS OFF) +SELECT *, LEVEL, connect_by_isleaf, connect_by_iscycle, connect_by_root(name_desc), sys_connect_by_path(name_desc, '@') +FROM test_area +START WITH name = '中国' +CONNECT BY PRIOR id = fatherid; + QUERY PLAN +------------------------------------------------------------------------------------------ + CTE Scan on tmp_reuslt + CTE tmp_reuslt + -> StartWith Operator + Start With pseudo atts: RUITR, array_key_1, array_col_4 + -> Recursive Union + -> Seq Scan on test_area + Filter: (name = '中国'::text) + -> Hash Join + Hash Cond: (public.test_area.fatherid = tmp_reuslt."test_area@id") + -> Seq Scan on test_area + -> Hash + -> WorkTable Scan on tmp_reuslt +(12 rows) + +SELECT *, LEVEL, connect_by_isleaf, connect_by_iscycle, connect_by_root(name_desc), sys_connect_by_path(name_desc, '@') +FROM test_area +START WITH name = '中国' +CONNECT BY PRIOR id = fatherid; + id | name | fatherid | name_desc | level | connect_by_isleaf | connect_by_iscycle | connect_by_root | sys_connect_by_path +----+--------+----------+-----------+-------+-------------------+--------------------+-----------------+------------------------------------ + 1 | 中国 | 0 | China | 1 | 0 | 0 | China | @China + 2 | 湖南省 | 1 | Hunan | 2 | 0 | 0 | China | @China@Hunan + 3 | 广东省 | 1 | Guangdong | 2 | 0 | 0 | China | @China@Guangdong + 4 | 海南省 | 1 | Hainan | 2 | 1 | 0 | China | @China@Hainan + 5 | 河北省 | 1 | Hebei | 2 | 1 | 0 | China | @China@Hebei + 6 | 河南省 | 1 | Henan | 2 | 1 | 0 | China | @China@Henan + 7 | 山东省 | 1 | Shandong | 2 | 1 | 0 | China | @China@Shandong + 8 | 湖北省 | 1 | Hubei | 2 | 1 | 0 | China | @China@Hubei + 9 | 江苏省 | 1 | Jiangsu | 2 | 1 | 0 | China | @China@Jiangsu + 10 | 深圳市 | 3 | Shenzhen | 3 | 0 | 0 | China | @China@Guangdong@Shenzhen + 11 | 长沙市 | 2 | Changsha | 3 | 1 | 0 | China | @China@Hunan@Changsha + 13 | 衡阳市 | 2 | Hengyang | 3 | 0 | 0 | China | @China@Hunan@Hengyang + 22 | 祁北县 | 13 | Qibei | 4 | 1 | 0 | China | @China@Hunan@Hengyang@Qibei + 12 | 南山区 | 10 | Nanshan | 4 | 1 | 0 | China | @China@Guangdong@Shenzhen@Nanshan + 21 | 祁西县 | 13 | Qixi | 4 | 1 | 0 | China | @China@Hunan@Hengyang@Qixi + 14 | 耒阳市 | 13 | Leiyang | 4 | 1 | 0 | China | @China@Hunan@Hengyang@Leiyang + 15 | 龙岗区 | 10 | Longgang | 4 | 1 | 0 | China | @China@Guangdong@Shenzhen@Longgang + 16 | 福田区 | 10 | Futian | 4 | 1 | 0 | China | @China@Guangdong@Shenzhen@Futian + 17 | 宝安区 | 10 | Baoan | 4 | 1 | 0 | China | @China@Guangdong@Shenzhen@Baoan + 19 | 祁东县 | 13 | Qidong | 4 | 1 | 0 | China | @China@Hunan@Hengyang@Qidong + 18 | 常宁市 | 13 | Changning | 4 | 1 | 0 | China | @China@Hunan@Hengyang@Changning + 20 | 祁南县 | 13 | Qinan | 4 | 1 | 0 | China | @China@Hunan@Hengyang@Qinan +(22 rows) + +drop table test_area; +create table test_rownum_push_qual_flt(id int); +insert into test_rownum_push_qual_flt values(generate_series(1, 20)); +select rownum, * from test_rownum_push_qual_flt group by id,rownum having ROWNUM < 10 and id between 10 and 20 order by 1; -- expect 0 rows + rownum | id +--------+---- +(0 rows) + +drop table test_rownum_push_qual_flt; +drop table if exists main_table_flt; +NOTICE: table "main_table_flt" does not exist, skipping +drop function if exists trigger_func_flt; +NOTICE: function trigger_func_flt() does not exist, skipping +CREATE TABLE main_table_flt (a int, b int); +ALTER TABLE main_table_flt ADD PRIMARY KEY(A, B); +NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "main_table_flt_pkey" for table "main_table_flt" +CREATE FUNCTION trigger_func_flt() RETURNS trigger LANGUAGE plpgsql AS ' +BEGIN + RAISE NOTICE ''trigger_func_flt(%) called: action = %, when = %, level = %'', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL; + RETURN NULL; +END;'; +CREATE TRIGGER modified_a BEFORE UPDATE OF a ON main_table_flt +FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func_flt('modified_a'); +CREATE TRIGGER modified_any BEFORE UPDATE OF a ON main_table_flt +FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE PROCEDURE trigger_func_flt('modified_any'); +CREATE TRIGGER insert_a AFTER INSERT ON main_table_flt +FOR EACH ROW WHEN (NEW.a = 123) EXECUTE PROCEDURE trigger_func_flt('insert_a'); +CREATE TRIGGER delete_a AFTER DELETE ON main_table_flt +FOR EACH ROW WHEN (OLD.a = 123) EXECUTE PROCEDURE trigger_func_flt('delete_a'); +CREATE TRIGGER insert_when BEFORE INSERT ON main_table_flt +FOR EACH STATEMENT WHEN (true) EXECUTE PROCEDURE trigger_func_flt('insert_when'); +CREATE TRIGGER delete_when AFTER DELETE ON main_table_flt +FOR EACH STATEMENT WHEN (true) EXECUTE PROCEDURE trigger_func_flt('delete_when'); +INSERT INTO main_table_flt (a) VALUES (123), (456); +NOTICE: trigger_func_flt(insert_when) called: action = INSERT, when = BEFORE, level = STATEMENT +ERROR: null value in column "b" violates not-null constraint +DETAIL: Failing row contains (123, null). +drop table if exists main_table_flt; +drop function if exists trigger_func_flt; +CREATE TABLE interval_normal_exchange (logdate date not null) +PARTITION BY RANGE (logdate) +INTERVAL ('1 month') +( + PARTITION interval_normal_exchange_p1 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_normal_exchange_p2 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_normal_exchange_p3 VALUES LESS THAN ('2020-05-01') +); +select * from interval_normal_exchange where logdate > '2020-06-01' order by logdate; + logdate +--------- +(0 rows) + +drop table interval_normal_exchange; +drop table if exists hw_partition_index_ip; +NOTICE: table "hw_partition_index_ip" does not exist, skipping +create table hw_partition_index_ip +( + c1 int, + c2 int, + logdate date not null +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION hw_partition_index_ip_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION hw_partition_index_ip_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION hw_partition_index_ip_p2 VALUES LESS THAN ('2020-05-01') +); +create unique index CONCURRENTLY on hw_partition_index_ip (logdate) local +( + partition sip1_index_local tablespace PG_DEFAULT, + partition sip2_index_local tablespace PG_DEFAULT, + partition sip3_index_local tablespace PG_DEFAULT +); +ERROR: cannot create concurrent partitioned indexes +drop table if exists hw_partition_index_ip; +drop table if exists inventory_table_02; +NOTICE: table "inventory_table_02" does not exist, skipping +create table inventory_table_02 +( + inv_date_sk integer not null, + inv_item_sk numeric not null, + inv_warehouse_sk integer not null, + inv_quantity_on_hand integer +) +partition by range(inv_date_sk) +( + partition p1 values less than(10000), + partition p2 values less than(20000), + partition p3 values less than(30000), + partition p4 values less than(40000), + partition p5 values less than(50000), + partition p6 values less than(60000), + partition p7 values less than(maxvalue) +); +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_date_sk') where correlation = 1; + bool +------ +(0 rows) + +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_item_sk') where correlation = 1; + bool +------ +(0 rows) + +drop table if exists inventory_table_02; +drop table if exists t_t_mutil_t1_flt; +NOTICE: table "t_t_mutil_t1_flt" does not exist, skipping +drop table if exists t_t_mutil_t2_flt; +NOTICE: table "t_t_mutil_t2_flt" does not exist, skipping +create table t_t_mutil_t1_flt(col1 int,col2 int); +create table t_t_mutil_t2_flt(col1 int,col2 int); +delete from t_t_mutil_t1_flt a,t_t_mutil_t2_flt b where a.col1=b.col1; +ERROR: multi-relation delete only support in B-format database +drop table if exists t_t_mutil_t1_flt; +drop table if exists t_t_mutil_t2_flt; +CREATE TABLE CASE_TBL ( + i integer, + f double precision +); +CREATE TABLE CASE2_TBL ( + i integer, + j integer +); +INSERT INTO CASE_TBL VALUES (1, 10.1); +INSERT INTO CASE_TBL VALUES (2, 20.2); +INSERT INTO CASE_TBL VALUES (3, -30.3); +INSERT INTO CASE_TBL VALUES (4, NULL); +INSERT INTO CASE2_TBL VALUES (1, -1); +INSERT INTO CASE2_TBL VALUES (2, -2); +INSERT INTO CASE2_TBL VALUES (3, -3); +INSERT INTO CASE2_TBL VALUES (2, -4); +INSERT INTO CASE2_TBL VALUES (1, NULL); +INSERT INTO CASE2_TBL VALUES (NULL, -6); +SELECT '' AS Five, NULLIF(a.i,b.i) AS "NULLIF(a.i,b.i)", + NULLIF(b.i, 4) AS "NULLIF(b.i,4)" + FROM CASE_TBL a, CASE2_TBL b + ORDER BY 2, 3; + five | NULLIF(a.i,b.i) | NULLIF(b.i,4) +------+-----------------+--------------- + | 1 | 2 + | 1 | 2 + | 1 | 3 + | 1 | + | 2 | 1 + | 2 | 1 + | 2 | 3 + | 2 | + | 3 | 1 + | 3 | 1 + | 3 | 2 + | 3 | 2 + | 3 | + | 4 | 1 + | 4 | 1 + | 4 | 2 + | 4 | 2 + | 4 | 3 + | 4 | + | | 1 + | | 1 + | | 2 + | | 2 + | | 3 +(24 rows) + +DROP TABLE CASE_TBL; +DROP TABLE CASE2_TBL; +drop table if exists t5; +NOTICE: table "t5" does not exist, skipping +CREATE TABLE t5 ( + col1 INT, + col2 INT DEFAULT 1, + col3 BIGSERIAL, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 INTEGER(10, 5) DEFAULT RANDOM() + 1 +) ; +NOTICE: CREATE TABLE will create implicit sequence "t5_col3_seq" for serial column "t5.col3" +TRUNCATE t5; +CREATE UNIQUE INDEX u_t5_index2 ON t5(col1, col5) WHERE col1 > 2; +INSERT INTO t5 VALUES (3), (3), (3) ON DUPLICATE KEY UPDATE col2 = col3; +drop table if exists t5; +set behavior_compat_options='aformat_null_test'; +with r(a,b) as materialized + (values (1,row(1,2)), (1,row(null,null)), (1,null), + (null,row(1,2)), (null,row(null,null)), (null,null) ) +select r, r is null as isnull, r is not null as isnotnull from r; + r | isnull | isnotnull +-------------+--------+----------- + (1,"(1,2)") | f | t + (1,"(,)") | f | t + (1,) | f | t + (,"(1,2)") | f | t + (,"(,)") | f | t + (,) | t | f +(6 rows) + +reset behavior_compat_options; +CREATE TABLE gtest22c (a int, b int GENERATED ALWAYS AS (a * 2) STORED); +CREATE INDEX gtest22c_b_idx ON gtest22c (b); +CREATE INDEX gtest22c_expr_idx ON gtest22c ((b * 3)); +CREATE INDEX gtest22c_pred_idx ON gtest22c (a) WHERE b > 0; +\d gtest22c + Table "public.gtest22c" + Column | Type | Modifiers +--------+---------+-------------------------------------- + a | integer | + b | integer | generated always as ((a * 2)) stored +Indexes: + "gtest22c_b_idx" btree (b) TABLESPACE pg_default + "gtest22c_expr_idx" btree ((b * 3)) TABLESPACE pg_default + "gtest22c_pred_idx" btree (a) TABLESPACE pg_default WHERE b > 0 + +INSERT INTO gtest22c VALUES (1), (2), (3); +DROP TABLE gtest22c; +show enable_expr_fusion ; + enable_expr_fusion +-------------------- + on +(1 row) + +reset enable_expr_fusion; diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index 0a10c80a3..97085128d 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -1088,3 +1088,6 @@ test: alter_foreign_schema test: slow_sql # test user@host test: user_host_test + +# test for new_expr_by_flatten +test: enable_expr_fusion_flatten diff --git a/src/test/regress/pg_regress.cpp b/src/test/regress/pg_regress.cpp index 507a3f1d5..a89937d0b 100644 --- a/src/test/regress/pg_regress.cpp +++ b/src/test/regress/pg_regress.cpp @@ -5461,7 +5461,7 @@ static void check_global_variables() } } -#define BASE_PGXC_LIKE_MACRO_NUM 1418 +#define BASE_PGXC_LIKE_MACRO_NUM 1419 static void check_pgxc_like_macros() { #ifdef BUILD_BY_CMAKE diff --git a/src/test/regress/sql/enable_expr_fusion_flatten.sql b/src/test/regress/sql/enable_expr_fusion_flatten.sql new file mode 100644 index 000000000..cc2f7a83f --- /dev/null +++ b/src/test/regress/sql/enable_expr_fusion_flatten.sql @@ -0,0 +1,473 @@ +show enable_expr_fusion; +set enable_expr_fusion = on; +show enable_expr_fusion; + +drop table if exists t1; +create table t1 (id int, score int, pk int); +insert into t1 values(1,1,1); +insert into t1 values(1,2,1); +insert into t1 values(1,3,1); +insert into t1 values(2,1,1); +insert into t1 values(1,1,2); + +select grouping(id,score), sum(pk), id, score from t1 group by rollup(id,score) order by +grouping, sum, id, score; +select * from t1 where (id =10) or (score = 10) or (pk =1 and score = 2 and id =1); +select * from t1 where id is unknown; +select * from t1 where id is not unknown; +select rownum from t1 ; + +select * from t1 where id is distinct from score; + +create type newtype as (a int ,b int); +create table test_flt(a newtype, b int); +insert into test_flt values (ROW(1,2),3); +update test_flt a set a.a = 12; +select * from test_flt; + + +drop table if exists test_flt; +drop type newtype; + +select (1,2) > (3,4); +select (1,2) >= (3,4); +select (1,2) < (3,4); +select (1,2) <= (3,4); + +select id is true from t1; +select id is false from t1; +select id is not true from t1; +select id is not false from t1; + +select * from t1 where id is not null; +select nullif(id,'') from t1; + +create table employees ( + salary numeric CHECK(salary > 0) +); + +insert into employees values (-100); + +drop table if exists employees; + +create sequence sequence_test2_flt start with 32; +create sequence sequence_test3 + increment by 1 + minvalue 1 maxvalue 30 + start 1 + cache 5; + +SELECT * FROM information_schema.sequences WHERE sequence_name IN + ('sequence_test2_flt', 'sequence_test3', 'serialtest2_f3_seq', + 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') + ORDER BY sequence_name ASC; + +CREATE TYPE compfoo AS (f1 int, f2 text); +create table t_group_array (a compfoo[], b int, c int[]); +insert into t_group_array (a[5].f1) values(32); +insert into t_group_array values(array[cast((1,'syr') as compfoo),cast((2,'sss') as compfoo)],1,array[1,2]); +insert into t_group_array (a[5].f1) values(32); +update t_group_array set a[5].f2='sss'; +drop table t_group_array; +drop type compfoo; + + +create table test_d(a int[3],b int); +create table test_s(a int[3],b int); +insert into test_d values('{1,2,3}',4); +insert into test_s values('{10,20,30}',4); +merge into test_d using test_s on(test_d.b=test_s.b) when matched then update set test_d.a=test_s.a; +select * from test_d; +truncate table test_s; +insert into test_s values('{11,21,31}',4); +merge into test_d d using test_s on(d.b=test_s.b) when matched then update set d.a=test_s.a; +select * from test_d; +--must compatible with previous features, though not perfect +merge into test_d using test_s on(test_d.b=test_s.b) when matched then update set test_d.a[1,3]=test_s.a[1,3]; +select * from test_d; +merge into test_d d using test_s on(d.b=test_s.b) when matched then update set d.a[1,3]=test_s.a[1,3]; +select * from test_d; +drop table test_d; +drop table test_s; + +create table xcreturn_tab1 (id int, v varchar); +ALTER TABLE xcreturn_tab1 ADD PRIMARY KEY(id); +create table xcreturn_tab2 (id int, v varchar); +ALTER TABLE xcreturn_tab2 ADD PRIMARY KEY(id); +insert into xcreturn_tab1 values (1, 'firstrow'), (2, 'secondrow'); +WITH wcte AS ( INSERT INTO xcreturn_tab2 VALUES (999, 'opop'), (333, 'sss') , ( 42, 'new' ), (55, 'ppp') RETURNING id AS newid ) +UPDATE xcreturn_tab1 SET id = id + newid FROM wcte; + +drop table xcreturn_tab1, xcreturn_tab2; + + + +CREATE TABLE INT4_FLT(f1 int4); + +INSERT INTO INT4_FLT(f1) VALUES (' 0 '); + +INSERT INTO INT4_FLT(f1) VALUES ('123456 '); + +INSERT INTO INT4_FLT(f1) VALUES (' -123456'); + +INSERT INTO INT4_FLT(f1) VALUES ('34.5'); + +-- largest and smallest values +INSERT INTO INT4_FLT(f1) VALUES ('2147483647'); + +INSERT INTO INT4_FLT(f1) VALUES ('-2147483647'); + +-- bad input values -- should give errors +INSERT INTO INT4_FLT(f1) VALUES ('1000000000000'); +INSERT INTO INT4_FLT(f1) VALUES ('asdf'); +INSERT INTO INT4_FLT(f1) VALUES (' '); +INSERT INTO INT4_FLT(f1) VALUES (' asdf '); +INSERT INTO INT4_FLT(f1) VALUES ('- 1234'); +INSERT INTO INT4_FLT(f1) VALUES ('123 5'); +INSERT INTO INT4_FLT(f1) VALUES (''); + +select q from (select max(f1) from int4_FLT order by f1) q; +drop table INT4_FLT; + +with r(a,b) as materialized + (values (1,row(1,2)), (1,row(null,null)), (1,null), + (null,row(1,2)), (null,row(null,null)), (null,null) ) +select r, r is null as isnull, r is not null as isnotnull from r; + +drop table if exists t1; +drop table if exists t2; +drop table if exists t3; +create table t1 (a int primary key, b int, c int); +create table t2 (a int primary key, b int, c int); +create table t3 (a int primary key, b int, c int); +-- insert some data to suppress no statistics warning +insert into t1 values(1,1,1); +insert into t2 values(1,1,1); +insert into t3 values(1,1,1); + +EXPLAIN (verbose, costs off) select * from t1 where exists (select /*+ noEXPLAIN (verbose, costs off)and*/ t2.a from t2 where t2.a = t1.a); +select * from t1 where exists (select /*+ noEXPLAIN (verbose, costs off)and*/ t2.a from t2 where t2.a = t1.a); + +EXPLAIN (verbose, costs off) select * from t1 where t1.a in (select /*+ noEXPLAIN (verbose, costs off)and*/ /*+ noEXPLAIN (verbose, costs off)and noEXPLAIN (verbose, costs off)and*/ t2.a from t2); +select * from t1 where t1.a in (select /*+ noEXPLAIN (verbose, costs off)and*/ /*+ noEXPLAIN (verbose, costs off)and noEXPLAIN (verbose, costs off)and*/ t2.a from t2); + + +drop table if exists t1; +drop table if exists t2; +drop table if exists t3; + + +create database test_select_into_var dbcompatibility 'b'; +\c test_select_into_var +set enable_expr_fusion = on; +drop table if exists t; +create table t(i int, t text, b bool, f float, bi bit(3), vbi bit varying(5)); +insert into t(i, t, b, f, bi, vbi) +values(1, 'aaa', true, 1.11, B'101', B'00'), + (2, 'bbb', false, 2.22, B'100', B'10'), + (3, null, true, 3.33, B'101', B'00'), + (4, 'ddd', null, 4.44, B'100', B'10'), + (5, 'eee', false, null, B'101', B'00'), + (6, 'fff', true, 6.66, null, B'00'), + (7, 'ggg', false, 7.77, B'100', null), + (null, 'hhh', true, 8.88, B'101', B'10'); +select * from t where i in (1,2); + + +\c regression +drop database if exists test_select_into_var; +set enable_expr_fusion = on; + +create table t1(a int); +create server my_server foreign data wrapper file_fdw; +create foreign table test_fore(id int) server my_server options(filename '/tmp'); +select table_name,external from adm_tables where table_name = 'test_fore' or table_name = 't1'; +drop table t1; +drop foreign table test_fore; + +select ROW(1,2,NULL) < ROW(1,3,0); + +select * from ADM_ARGUMENTS where OBJECT_NAME = 'proarg01'; + +create table t1 (c1 int primary key, c2 int); +create index t1_c2_idx on t1 using btree(c2); +create table t2 (c1 int, c2 int); +create index t2_c2_idx on t2 using btree(c2); +-- insert some data to suppress no statistics warning +insert into t1 values(1,1); +insert into t2 values(1,1); + +insert into t1 select c1, c2 from t2 where c2 = 1 on duplicate key update c2 = c2 + 1; + +drop table t1; +drop table t2; + +CREATE TABLE hash_hash +( + col_1 int , + col_2 int NOT NULL , + col_3 VARCHAR2 ( 30 ) , + col_4 int +) +PARTITION BY hash (col_3) SUBPARTITION BY hash (col_2) +( + PARTITION p_hash_1 + ( + SUBPARTITION p_hash_1_1 , + SUBPARTITION p_hash_1_2 , + SUBPARTITION p_hash_1_3 , + SUBPARTITION p_hash_1_4 + ), + PARTITION p_hash_2 + ( + SUBPARTITION p_hash_2_1 , + SUBPARTITION p_hash_2_2 + ), + PARTITION p_hash_3, + PARTITION p_hash_4 + ( + SUBPARTITION p_hash_4_1 + ), + PARTITION p_hash_5 +); + +INSERT INTO hash_hash VALUES(null,1,1,1); +alter table hash_hash add constraint con_hash_hash check(col_1 is not null) NOT VALID ; +INSERT INTO hash_hash VALUES(null,2,1,1); --error +INSERT INTO hash_hash VALUES(1,3,1,1); --success +alter table hash_hash VALIDATE CONSTRAINT con_hash_hash; --error +delete from hash_hash where col_1 is null; +alter table hash_hash VALIDATE CONSTRAINT con_hash_hash; --success + +drop table hash_hash cascade; + +create table test_range (a int, b int, c int) WITH (STORAGE_TYPE=USTORE) +partition by range(a) +( + partition p1 values less than (2000), + partition p2 values less than (3000), + partition p3 values less than (4000), + partition p4 values less than (5000), + partition p5 values less than (maxvalue) +)ENABLE ROW MOVEMENT; + +insert into test_range values(1,1,1); +insert into test_range values(3001,1,1); + +prepare p1 as select * from test_range where ctid = '(0,1)' and a = $1; +explain (costs off)execute p1(1); +execute p1(1); +execute p1(3001); +drop table test_range; + + +set client_encoding ='UTF8'; +drop table if exists test_area; +create table test_area(id int4,name text, fatherid int4, name_desc text); +insert into test_area values (1, '中国', 0, 'China'); +insert into test_area values (2, '湖南省',1 , 'Hunan'); +insert into test_area values (3, '广东省',1 , 'Guangdong'); +insert into test_area values (4, '海南省',1 , 'Hainan'); +insert into test_area values (5, '河北省',1 , 'Hebei'); +insert into test_area values (6, '河南省',1 , 'Henan'); +insert into test_area values (7, '山东省',1 , 'Shandong'); +insert into test_area values (8, '湖北省',1 , 'Hubei'); +insert into test_area values (9, '江苏省',1 , 'Jiangsu'); +insert into test_area values (10,'深圳市',3 , 'Shenzhen'); +insert into test_area values (11,'长沙市',2 , 'Changsha'); +insert into test_area values (22,'祁北县',13, 'Qibei'); +insert into test_area values (12,'南山区',10, 'Nanshan'); +insert into test_area values (21,'祁西县',13, 'Qixi'); +insert into test_area values (13,'衡阳市',2 , 'Hengyang'); +insert into test_area values (14,'耒阳市',13, 'Leiyang'); +insert into test_area values (15,'龙岗区',10, 'Longgang'); +insert into test_area values (16,'福田区',10, 'Futian'); +insert into test_area values (17,'宝安区',10, 'Baoan'); +insert into test_area values (19,'祁东县',13, 'Qidong'); +insert into test_area values (18,'常宁市',13, 'Changning'); +insert into test_area values (20,'祁南县',13, 'Qinan'); + +EXPLAIN (COSTS OFF) +SELECT *, LEVEL, connect_by_isleaf, connect_by_iscycle, connect_by_root(name_desc), sys_connect_by_path(name_desc, '@') +FROM test_area +START WITH name = '中国' +CONNECT BY PRIOR id = fatherid; + +SELECT *, LEVEL, connect_by_isleaf, connect_by_iscycle, connect_by_root(name_desc), sys_connect_by_path(name_desc, '@') +FROM test_area +START WITH name = '中国' +CONNECT BY PRIOR id = fatherid; + +drop table test_area; + + +create table test_rownum_push_qual_flt(id int); + +insert into test_rownum_push_qual_flt values(generate_series(1, 20)); + +select rownum, * from test_rownum_push_qual_flt group by id,rownum having ROWNUM < 10 and id between 10 and 20 order by 1; -- expect 0 rows + +drop table test_rownum_push_qual_flt; + + +drop table if exists main_table_flt; +drop function if exists trigger_func_flt; +CREATE TABLE main_table_flt (a int, b int); +ALTER TABLE main_table_flt ADD PRIMARY KEY(A, B); + +CREATE FUNCTION trigger_func_flt() RETURNS trigger LANGUAGE plpgsql AS ' +BEGIN + RAISE NOTICE ''trigger_func_flt(%) called: action = %, when = %, level = %'', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL; + RETURN NULL; +END;'; + +CREATE TRIGGER modified_a BEFORE UPDATE OF a ON main_table_flt +FOR EACH ROW WHEN (OLD.a <> NEW.a) EXECUTE PROCEDURE trigger_func_flt('modified_a'); +CREATE TRIGGER modified_any BEFORE UPDATE OF a ON main_table_flt +FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE PROCEDURE trigger_func_flt('modified_any'); +CREATE TRIGGER insert_a AFTER INSERT ON main_table_flt +FOR EACH ROW WHEN (NEW.a = 123) EXECUTE PROCEDURE trigger_func_flt('insert_a'); +CREATE TRIGGER delete_a AFTER DELETE ON main_table_flt +FOR EACH ROW WHEN (OLD.a = 123) EXECUTE PROCEDURE trigger_func_flt('delete_a'); +CREATE TRIGGER insert_when BEFORE INSERT ON main_table_flt +FOR EACH STATEMENT WHEN (true) EXECUTE PROCEDURE trigger_func_flt('insert_when'); +CREATE TRIGGER delete_when AFTER DELETE ON main_table_flt +FOR EACH STATEMENT WHEN (true) EXECUTE PROCEDURE trigger_func_flt('delete_when'); +INSERT INTO main_table_flt (a) VALUES (123), (456); + +drop table if exists main_table_flt; +drop function if exists trigger_func_flt; + +CREATE TABLE interval_normal_exchange (logdate date not null) +PARTITION BY RANGE (logdate) +INTERVAL ('1 month') +( + PARTITION interval_normal_exchange_p1 VALUES LESS THAN ('2020-03-01'), + PARTITION interval_normal_exchange_p2 VALUES LESS THAN ('2020-04-01'), + PARTITION interval_normal_exchange_p3 VALUES LESS THAN ('2020-05-01') +); + +select * from interval_normal_exchange where logdate > '2020-06-01' order by logdate; +drop table interval_normal_exchange; + +drop table if exists hw_partition_index_ip; +create table hw_partition_index_ip +( + c1 int, + c2 int, + logdate date not null +) +partition by range (logdate) +INTERVAL ('1 month') +( + PARTITION hw_partition_index_ip_p0 VALUES LESS THAN ('2020-03-01'), + PARTITION hw_partition_index_ip_p1 VALUES LESS THAN ('2020-04-01'), + PARTITION hw_partition_index_ip_p2 VALUES LESS THAN ('2020-05-01') +); + +create unique index CONCURRENTLY on hw_partition_index_ip (logdate) local +( + partition sip1_index_local tablespace PG_DEFAULT, + partition sip2_index_local tablespace PG_DEFAULT, + partition sip3_index_local tablespace PG_DEFAULT +); + +drop table if exists hw_partition_index_ip; + + + +drop table if exists inventory_table_02; + +create table inventory_table_02 +( + inv_date_sk integer not null, + inv_item_sk numeric not null, + inv_warehouse_sk integer not null, + inv_quantity_on_hand integer +) +partition by range(inv_date_sk) +( + partition p1 values less than(10000), + partition p2 values less than(20000), + partition p3 values less than(30000), + partition p4 values less than(40000), + partition p5 values less than(50000), + partition p6 values less than(60000), + partition p7 values less than(maxvalue) +); + +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_date_sk') where correlation = 1; +select true from (select correlation from pg_stats where tablename='inventory_table_02' and attname='inv_item_sk') where correlation = 1; + +drop table if exists inventory_table_02; + +drop table if exists t_t_mutil_t1_flt; +drop table if exists t_t_mutil_t2_flt; +create table t_t_mutil_t1_flt(col1 int,col2 int); +create table t_t_mutil_t2_flt(col1 int,col2 int); +delete from t_t_mutil_t1_flt a,t_t_mutil_t2_flt b where a.col1=b.col1; +drop table if exists t_t_mutil_t1_flt; +drop table if exists t_t_mutil_t2_flt; + +CREATE TABLE CASE_TBL ( + i integer, + f double precision +); + +CREATE TABLE CASE2_TBL ( + i integer, + j integer +); + +INSERT INTO CASE_TBL VALUES (1, 10.1); +INSERT INTO CASE_TBL VALUES (2, 20.2); +INSERT INTO CASE_TBL VALUES (3, -30.3); +INSERT INTO CASE_TBL VALUES (4, NULL); + +INSERT INTO CASE2_TBL VALUES (1, -1); +INSERT INTO CASE2_TBL VALUES (2, -2); +INSERT INTO CASE2_TBL VALUES (3, -3); +INSERT INTO CASE2_TBL VALUES (2, -4); +INSERT INTO CASE2_TBL VALUES (1, NULL); +INSERT INTO CASE2_TBL VALUES (NULL, -6); + +SELECT '' AS Five, NULLIF(a.i,b.i) AS "NULLIF(a.i,b.i)", + NULLIF(b.i, 4) AS "NULLIF(b.i,4)" + FROM CASE_TBL a, CASE2_TBL b + ORDER BY 2, 3; + +DROP TABLE CASE_TBL; +DROP TABLE CASE2_TBL; + +drop table if exists t5; +CREATE TABLE t5 ( + col1 INT, + col2 INT DEFAULT 1, + col3 BIGSERIAL, +-- col4 TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + col5 INTEGER(10, 5) DEFAULT RANDOM() + 1 +) ; + +TRUNCATE t5; +CREATE UNIQUE INDEX u_t5_index2 ON t5(col1, col5) WHERE col1 > 2; +INSERT INTO t5 VALUES (3), (3), (3) ON DUPLICATE KEY UPDATE col2 = col3; +drop table if exists t5; + +set behavior_compat_options='aformat_null_test'; +with r(a,b) as materialized + (values (1,row(1,2)), (1,row(null,null)), (1,null), + (null,row(1,2)), (null,row(null,null)), (null,null) ) +select r, r is null as isnull, r is not null as isnotnull from r; +reset behavior_compat_options; + +CREATE TABLE gtest22c (a int, b int GENERATED ALWAYS AS (a * 2) STORED); +CREATE INDEX gtest22c_b_idx ON gtest22c (b); +CREATE INDEX gtest22c_expr_idx ON gtest22c ((b * 3)); +CREATE INDEX gtest22c_pred_idx ON gtest22c (a) WHERE b > 0; +\d gtest22c + +INSERT INTO gtest22c VALUES (1), (2), (3); +DROP TABLE gtest22c; + +show enable_expr_fusion ; +reset enable_expr_fusion; diff --git a/src/test/ut/db4ai/direct_ml/pg_mock.cpp b/src/test/ut/db4ai/direct_ml/pg_mock.cpp index 9eb72fe44..38004a7b7 100644 --- a/src/test/ut/db4ai/direct_ml/pg_mock.cpp +++ b/src/test/ut/db4ai/direct_ml/pg_mock.cpp @@ -160,20 +160,6 @@ static PGFunction mock_get_output_func(Oid functionId) return get_type_map(functionId)->outfunc; } -// overwrite -void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt) -{ - // fmgr_info_cxt_security(functionId, finfo, mcxt, false) - finfo->fn_oid = functionId; - finfo->fn_extra = NULL; - finfo->fn_mcxt = mcxt; - finfo->fn_expr = NULL; /* caller may set this later */ - finfo->fn_fenced = false; - finfo->fnLibPath = NULL; - - finfo->fn_addr = mock_get_output_func(functionId); -} - // overwrite void get_type_io_data(Oid typid, IOFuncSelector which_func, int16 *typlen, bool *typbyval, char *typalign, char *typdelim, Oid *typioparam, Oid *func)