/* * Compose and dispatch the MPPEXEC commands corresponding to a plan tree * within a complete parallel plan. (A plan tree will correspond either * to an initPlan or to the main plan.) * * If cancelOnError is true, then any dispatching error, a cancellation * request from the client, or an error from any of the associated QEs, * may cause the unfinished portion of the plan to be abandoned or canceled; * and in the event this occurs before all gangs have been dispatched, this * function does not return, but waits for all QEs to stop and exits to * the caller's error catcher via ereport(ERROR,...).Otherwise this * function returns normally and errors are not reported until later. * * If cancelOnError is false, the plan is to be dispatched as fully as * possible and the QEs allowed to proceed regardless of cancellation * requests, errors or connection failures from other QEs, etc. * * The CdbDispatchResults objects allocated for the plan are returned * in *pPrimaryResults. The caller, after calling * CdbCheckDispatchResult(), can examine the CdbDispatchResults * objects, can keep them as long as needed, and ultimately must free * them with cdbdisp_destroyDispatcherState() prior to deallocation of * the caller's memory context. Callers should use PG_TRY/PG_CATCH to * ensure proper cleanup. * * To wait for completion, check for errors, and clean up, it is * suggested that the caller use cdbdisp_finishCommand(). * * Note that the slice tree dispatched is the one specified in the EState * of the argument QueryDesc as es_cur__slice. * * Note that the QueryDesc params must include PARAM_EXEC_REMOTE parameters * containing the values of any initplans required by the slice to be run. * (This is handled by calls to addRemoteExecParamsToParamList() from the * functions preprocess_initplans() and ExecutorRun().) * * Each QE receives its assignment as a message of type 'M' in PostgresMain(). * The message is deserialized and processed by exec_mpp_query() in postgres.c. */ void cdbdisp_dispatchPlan(struct QueryDesc *queryDesc, bool planRequiresTxn, bool cancelOnError, struct CdbDispatcherState *ds) { char *splan, *sddesc, *sparams; int splan_len, splan_len_uncompressed, sddesc_len, sparams_len; SliceTable *sliceTbl; int rootIdx; int oldLocalSlice; PlannedStmt *stmt; bool is_SRI; DispatchCommandQueryParms queryParms; CdbComponentDatabaseInfo *qdinfo; ds->primaryResults = NULL; ds->dispatchThreads = NULL; Assert(Gp_role == GP_ROLE_DISPATCH); Assert(queryDesc != NULL && queryDesc->estate != NULL); /* * Later we'll need to operate with the slice table provided via the * EState structure in the argument QueryDesc. Cache this information * locally and assert our expectations about it. */ sliceTbl = queryDesc->estate->es_sliceTable; rootIdx = RootSliceIndex(queryDesc->estate); Assert(sliceTbl != NULL); Assert(rootIdx == 0 || (rootIdx > sliceTbl->nMotions && rootIdx <= sliceTbl->nMotions + sliceTbl->nInitPlans)); /* * Keep old value so we can restore it. We use this field as a parameter. */ oldLocalSlice = sliceTbl->localSlice; /* * This function is called only for planned statements. */ stmt = queryDesc->plannedstmt; Assert(stmt); /* * Let's evaluate STABLE functions now, so we get consistent values on the QEs * * Also, if this is a single-row INSERT statement, let's evaluate * nextval() and currval() now, so that we get the QD's values, and a * consistent value for everyone * */ is_SRI = false; if (queryDesc->operation == CMD_INSERT) { Assert(stmt->commandType == CMD_INSERT); /* * We might look for constant input relation (instead of SRI), but I'm afraid * * that wouldn't scale. */ is_SRI = IsA(stmt->planTree, Result) && stmt->planTree->lefttree == NULL; } if (!is_SRI) clear_relsize_cache(); if (queryDesc->operation == CMD_INSERT || queryDesc->operation == CMD_SELECT || queryDesc->operation == CMD_UPDATE || queryDesc->operation == CMD_DELETE) { MemoryContext oldContext; oldContext = CurrentMemoryContext; if (stmt->qdContext) { oldContext = MemoryContextSwitchTo(stmt->qdContext); } else /* * memory context of plan tree should not change */ { MemoryContext mc = GetMemoryChunkContext(stmt->planTree); oldContext = MemoryContextSwitchTo(mc); } stmt->planTree = (Plan *) exec_make_plan_constant(stmt, is_SRI); MemoryContextSwitchTo(oldContext); } /* * Cursor queries and bind/execute path queries don't run on the * writer-gang QEs; but they require snapshot-synchronization to * get started. * * initPlans, and other work (see the function pre-evaluation * above) may advance the snapshot "segmateSync" value, so we're * best off setting the shared-snapshot-ready value here. This * will dispatch to the writer gang and force it to set its * snapshot; we'll then be able to serialize the same snapshot * version (see qdSerializeDtxContextInfo() below). */ if (queryDesc->extended_query) { verify_shared_snapshot_ready(); } /* * serialized plan tree. Note that we're called for a single * slice tree (corresponding to an initPlan or the main plan), so the * parameters are fixed and we can include them in the prefix. */ splan = serializeNode((Node *) queryDesc->plannedstmt, &splan_len, &splan_len_uncompressed); uint64 plan_size_in_kb = ((uint64) splan_len_uncompressed) / (uint64) 1024; if (0 < gp_max_plan_size && plan_size_in_kb > gp_max_plan_size) { ereport(ERROR, (errcode(ERRCODE_STATEMENT_TOO_COMPLEX), (errmsg("Query plan size limit exceeded, current size: " UINT64_FORMAT "KB, max allowed size: %dKB", plan_size_in_kb, gp_max_plan_size), errhint("Size controlled by gp_max_plan_size")))); } Assert(splan != NULL && splan_len > 0 && splan_len_uncompressed > 0); if (queryDesc->params != NULL && queryDesc->params->numParams > 0) { ParamListInfoData *pli; ParamExternData *pxd; StringInfoData parambuf; Size length; int plioff; int32 iparam; /* * Allocate buffer for params */ initStringInfo(¶mbuf); /* * Copy ParamListInfoData header and ParamExternData array */ pli = queryDesc->params; length = (char *) &pli->params[pli->numParams] - (char *) pli; plioff = parambuf.len; Assert(plioff == MAXALIGN(plioff)); appendBinaryStringInfo(¶mbuf, pli, length); /* * Copy pass-by-reference param values. */ for (iparam = 0; iparam < queryDesc->params->numParams; iparam++) { int16 typlen; bool typbyval; /* * Recompute pli each time in case parambuf.data is repalloc'ed */ pli = (ParamListInfoData *) (parambuf.data + plioff); pxd = &pli->params[iparam]; if (pxd->ptype == InvalidOid) continue; /* * Does pxd->value contain the value itself, or a pointer? */ get_typlenbyval(pxd->ptype, &typlen, &typbyval); if (!typbyval) { char *s = DatumGetPointer(pxd->value); if (pxd->isnull || !PointerIsValid(s)) { pxd->isnull = true; pxd->value = 0; } else { length = datumGetSize(pxd->value, typbyval, typlen); /* * We *must* set this before we * append. Appending may realloc, which will * invalidate our pxd ptr. (obviously we could * append first if we recalculate pxd from the new * base address) */ pxd->value = Int32GetDatum(length); appendBinaryStringInfo(¶mbuf, &iparam, sizeof(iparam)); appendBinaryStringInfo(¶mbuf, s, length); } } } sparams = parambuf.data; sparams_len = parambuf.len; } else { sparams = NULL; sparams_len = 0; } sddesc = serializeNode((Node *) queryDesc->ddesc, &sddesc_len, NULL /*uncompressed_size */ ); MemSet(&queryParms, 0, sizeof(queryParms)); queryParms.strCommand = queryDesc->sourceText; queryParms.serializedQuerytree = NULL; queryParms.serializedQuerytreelen = 0; queryParms.serializedPlantree = splan; queryParms.serializedPlantreelen = splan_len; queryParms.serializedParams = sparams; queryParms.serializedParamslen = sparams_len; queryParms.serializedQueryDispatchDesc = sddesc; queryParms.serializedQueryDispatchDesclen = sddesc_len; queryParms.rootIdx = rootIdx; /* * sequence server info */ qdinfo = &(getComponentDatabases()->entry_db_info[0]); Assert(qdinfo != NULL && qdinfo->hostip != NULL); queryParms.seqServerHost = pstrdup(qdinfo->hostip); queryParms.seqServerHostlen = strlen(qdinfo->hostip) + 1; queryParms.seqServerPort = seqServerCtl->seqServerPort; /* * serialized a version of our snapshot */ /* * Generate our transction isolations. We generally want Plan * based dispatch to be in a global transaction. The executor gets * to decide if the special circumstances exist which allow us to * dispatch without starting a global xact. */ queryParms.serializedDtxContextInfo = qdSerializeDtxContextInfo(&queryParms.serializedDtxContextInfolen, true /* wantSnapshot */ , queryDesc->extended_query, mppTxnOptions(planRequiresTxn), "cdbdisp_dispatchPlan"); cdbdisp_dispatchX(&queryParms, cancelOnError, sliceTbl, ds); sliceTbl->localSlice = oldLocalSlice; }
static void FunctionParserInit(FunctionParser *self, Checker *checker, const char *infile, TupleDesc desc, bool multi_process, Oid collation) { int i; ParsedFunction function; int nargs; Oid funcid; HeapTuple ftup; Form_pg_proc pp; bool tupledesc_matched = false; if (pg_strcasecmp(infile, "stdin") == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot load from STDIN in the case of \"TYPE = FUNCTION\""))); if (checker->encoding != -1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("does not support parameter \"ENCODING\" in \"TYPE = FUNCTION\""))); function = ParseFunction(infile, false); funcid = function.oid; fmgr_info(funcid, &self->flinfo); if (!self->flinfo.fn_retset) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function must return set"))); ftup = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); pp = (Form_pg_proc) GETSTRUCT(ftup); /* Check data type of the function result value */ if (pp->prorettype == desc->tdtypeid && desc->tdtypeid != RECORDOID) tupledesc_matched = true; else if (pp->prorettype == RECORDOID) { TupleDesc resultDesc = NULL; /* Check for OUT parameters defining a RECORD result */ resultDesc = build_function_result_tupdesc_t(ftup); if (resultDesc) { tupledesc_match(desc, resultDesc); tupledesc_matched = true; FreeTupleDesc(resultDesc); } } else if (get_typtype(pp->prorettype) != TYPTYPE_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return data type and target table data type do not match"))); if (tupledesc_matched && checker->tchecker) checker->tchecker->status = NO_COERCION; /* * assign arguments */ nargs = function.nargs; for (i = 0; #if PG_VERSION_NUM >= 80400 i < nargs - function.nvargs; #else i < nargs; #endif ++i) { if (function.args[i] == NULL) { if (self->flinfo.fn_strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function is strict, but argument %d is NULL", i))); self->fcinfo.argnull[i] = true; } else { Oid typinput; Oid typioparam; getTypeInputInfo(pp->proargtypes.values[i], &typinput, &typioparam); self->fcinfo.arg[i] = OidInputFunctionCall(typinput, (char *) function.args[i], typioparam, -1); self->fcinfo.argnull[i] = false; pfree(function.args[i]); } } /* * assign variadic arguments */ #if PG_VERSION_NUM >= 80400 if (function.nvargs > 0) { int nfixedarg; Oid func; Oid element_type; int16 elmlen; bool elmbyval; char elmalign; char elmdelim; Oid elmioparam; Datum *elems; bool *nulls; int dims[1]; int lbs[1]; ArrayType *arry; nfixedarg = i; element_type = pp->provariadic; /* * Get info about element type, including its input conversion proc */ get_type_io_data(element_type, IOFunc_input, &elmlen, &elmbyval, &elmalign, &elmdelim, &elmioparam, &func); elems = (Datum *) palloc(function.nvargs * sizeof(Datum)); nulls = (bool *) palloc0(function.nvargs * sizeof(bool)); for (i = 0; i < function.nvargs; i++) { if (function.args[nfixedarg + i] == NULL) nulls[i] = true; else { elems[i] = OidInputFunctionCall(func, (char *) function.args[nfixedarg + i], elmioparam, -1); pfree(function.args[nfixedarg + i]); } } dims[0] = function.nvargs; lbs[0] = 1; arry = construct_md_array(elems, nulls, 1, dims, lbs, element_type, elmlen, elmbyval, elmalign); self->fcinfo.arg[nfixedarg] = PointerGetDatum(arry); } /* * assign default arguments */ if (function.ndargs > 0) { Datum proargdefaults; bool isnull; char *str; List *defaults; int ndelete; ListCell *l; /* shouldn't happen, FuncnameGetCandidates messed up */ if (function.ndargs > pp->pronargdefaults) elog(ERROR, "not enough default arguments"); proargdefaults = SysCacheGetAttr(PROCOID, ftup, Anum_pg_proc_proargdefaults, &isnull); Assert(!isnull); str = TextDatumGetCString(proargdefaults); defaults = (List *) stringToNode(str); Assert(IsA(defaults, List)); pfree(str); /* Delete any unused defaults from the returned list */ ndelete = list_length(defaults) - function.ndargs; while (ndelete-- > 0) defaults = list_delete_first(defaults); self->arg_econtext = CreateStandaloneExprContext(); foreach(l, defaults) { Expr *expr = (Expr *) lfirst(l); ExprState *argstate; ExprDoneCond thisArgIsDone; /* probably shouldn't happen ... */ if (nargs >= FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("cannot pass more than %d arguments to a function", FUNC_MAX_ARGS))); argstate = ExecInitExpr(expr, NULL); self->fcinfo.arg[nargs] = ExecEvalExpr(argstate, self->arg_econtext, &self->fcinfo.argnull[nargs], &thisArgIsDone); if (thisArgIsDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("functions and operators can take at most one set argument"))); nargs++; }
/* * This function executes set evaluation. The parser sets up a set reference * as a call to this function with the OID of the set to evaluate as argument. * * We build a new fcache for execution of the set's function and run the * function until it says "no mas". The fn_extra field of the call's * FmgrInfo record is a handy place to hold onto the fcache. (Since this * is a built-in function, there is no competing use of fn_extra.) */ Datum seteval(PG_FUNCTION_ARGS) { Oid funcoid = PG_GETARG_OID(0); FuncExprState *fcache; Datum result; bool isNull; ExprDoneCond isDone; /* * If this is the first call, we need to set up the fcache for the * target set's function. */ fcache = (FuncExprState *) fcinfo->flinfo->fn_extra; if (fcache == NULL) { MemoryContext oldcontext; FuncExpr *func; oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); func = makeNode(FuncExpr); func->funcid = funcoid; func->funcresulttype = InvalidOid; /* nothing will look at * this */ func->funcretset = true; func->funcformat = COERCE_EXPLICIT_CALL; func->args = NIL; /* there are no arguments */ fcache = (FuncExprState *) ExecInitExpr((Expr *) func, NULL); MemoryContextSwitchTo(oldcontext); init_fcache(funcoid, fcache, fcinfo->flinfo->fn_mcxt); fcinfo->flinfo->fn_extra = (void *) fcache; } /* * Evaluate the function. NOTE: we need no econtext because there are * no arguments to evaluate. */ /* ExecMakeFunctionResult assumes these are initialized at call: */ isNull = false; isDone = ExprSingleResult; result = ExecMakeFunctionResult(fcache, NULL, /* no econtext, see above */ &isNull, &isDone); /* * Return isNull/isDone status. */ fcinfo->isnull = isNull; if (isDone != ExprSingleResult) { ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (rsi && IsA(rsi, ReturnSetInfo)) rsi->isDone = isDone; else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that " "cannot accept a set"))); } PG_RETURN_DATUM(result); }
void parseHwParameters(List *parameterList, HoltWintersModel *specificModel) { ListCell *cell; foreach(cell,parameterList) { AlgorithmParameter *param = lfirst(cell); /* Seasonflag*/ if(strcmp(param->key,"has_season") == 0) { if(IsA(&(param->value->val),Integer)) { specificModel->doseasonal = intVal(¶m->value->val); specificModel->optflag[2]=specificModel->doseasonal; } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Parameter value has to be an Integer value"), errposition(param->value->location))); } else if(strcmp(param->key,"has_trend") == 0) { if(IsA(&(param->value->val),Integer)) { specificModel->dotrend = intVal(¶m->value->val); specificModel->optflag[1]=specificModel->dotrend; } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Parameter value has to be an Integer value"), errposition(param->value->location))); } else if(strcmp(param->key,"seasontype") == 0) { if(IsA(&(param->value->val),Integer)) { specificModel->seasonType = intVal(¶m->value->val); } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Parameter value has to be an Integer value"), errposition(param->value->location))); } else if(strcmp(param->key,"alpha") == 0) { if(IsA(&(param->value->val),Float)) { specificModel->alpha = floatVal(¶m->value->val); specificModel->optflag[0]=0; } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Parameter value has to be an float value"), errposition(param->value->location))); }else if(strcmp(param->key,"beta") == 0) { if(IsA(&(param->value->val),Float)) { specificModel->beta = floatVal(¶m->value->val); specificModel->optflag[1]=0; specificModel->dotrend=1; } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Parameter value has to be an float value"), errposition(param->value->location))); }else if(strcmp(param->key,"gamma") == 0) { if(IsA(&(param->value->val),Float)) { specificModel->gamma = floatVal(¶m->value->val); specificModel->optflag[2]=0; specificModel->doseasonal=1; } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Parameter value has to be an float value"), errposition(param->value->location))); }else if(strcmp(param->key,"season") == 0) { if(IsA(&(param->value->val),Integer)) { specificModel->period = intVal(¶m->value->val); specificModel->doseasonal = 1; specificModel->optflag[2]=specificModel->doseasonal; } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Parameter value has to be an Integer value"), errposition(param->value->location))); } else if(strcmp(param->key,"error") == 0) { if(IsA(&(param->value->val),String)) { specificModel->errorfunction = palloc0((strlen(strVal(¶m->value->val))+1)*sizeof(char)); strcpy(specificModel->errorfunction,strVal(¶m->value->val)); } else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Parameter value has to be an String value"), errposition(param->value->location))); } else ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Parameter not known"), errposition(((A_Const *)param->value)->location))); }
/* * internal_get_result_type -- workhorse code implementing all the above * * funcid must always be supplied. call_expr and rsinfo can be NULL if not * available. We will return TYPEFUNC_RECORD, and store NULL into * *resultTupleDesc, if we cannot deduce the complete result rowtype from * the available information. */ static TypeFuncClass internal_get_result_type(Oid funcid, Node *call_expr, ReturnSetInfo *rsinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc) { TypeFuncClass result; HeapTuple tp; Form_pg_proc procform; Oid rettype; TupleDesc tupdesc; /* First fetch the function's pg_proc row to inspect its rettype */ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for function %u", funcid); procform = (Form_pg_proc) GETSTRUCT(tp); rettype = procform->prorettype; /* Check for OUT parameters defining a RECORD result */ tupdesc = build_function_result_tupdesc_t(tp); if (tupdesc) { /* * It has OUT parameters, so it's basically like a regular composite * type, except we have to be able to resolve any polymorphic OUT * parameters. */ if (resultTypeId) *resultTypeId = rettype; if (resolve_polymorphic_tupdesc(tupdesc, &procform->proargtypes, call_expr)) { if (tupdesc->tdtypeid == RECORDOID && tupdesc->tdtypmod < 0) assign_record_type_typmod(tupdesc); if (resultTupleDesc) *resultTupleDesc = tupdesc; result = TYPEFUNC_COMPOSITE; } else { if (resultTupleDesc) *resultTupleDesc = NULL; result = TYPEFUNC_RECORD; } ReleaseSysCache(tp); return result; } /* * If scalar polymorphic result, try to resolve it. */ if (IsPolymorphicType(rettype)) { Oid newrettype = exprType(call_expr); if (newrettype == InvalidOid) /* this probably should not happen */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not determine actual result type for function \"%s\" declared to return type %s", NameStr(procform->proname), format_type_be(rettype)))); rettype = newrettype; } if (resultTypeId) *resultTypeId = rettype; if (resultTupleDesc) *resultTupleDesc = NULL; /* default result */ /* Classify the result type */ result = get_type_func_class(rettype); switch (result) { case TYPEFUNC_COMPOSITE: if (resultTupleDesc) *resultTupleDesc = lookup_rowtype_tupdesc_copy(rettype, -1); /* Named composite types can't have any polymorphic columns */ break; case TYPEFUNC_SCALAR: break; case TYPEFUNC_RECORD: /* We must get the tupledesc from call context */ if (rsinfo && IsA(rsinfo, ReturnSetInfo) && rsinfo->expectedDesc != NULL) { result = TYPEFUNC_COMPOSITE; if (resultTupleDesc) *resultTupleDesc = rsinfo->expectedDesc; /* Assume no polymorphic columns here, either */ } break; default: break; } ReleaseSysCache(tp); return result; }
/* * tsmatchsel -- Selectivity of "@@" * * restriction selectivity function for tsvector @@ tsquery and * tsquery @@ tsvector */ Datum tsmatchsel(PG_FUNCTION_ARGS) { PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0); #ifdef NOT_USED Oid operator = PG_GETARG_OID(1); #endif List *args = (List *) PG_GETARG_POINTER(2); int varRelid = PG_GETARG_INT32(3); VariableStatData vardata; Node *other; bool varonleft; Selectivity selec; /* * If expression is not variable = something or something = variable, then * punt and return a default estimate. */ if (!get_restriction_variable(root, args, varRelid, &vardata, &other, &varonleft)) PG_RETURN_FLOAT8(DEFAULT_TS_MATCH_SEL); /* * Can't do anything useful if the something is not a constant, either. */ if (!IsA(other, Const)) { ReleaseVariableStats(vardata); PG_RETURN_FLOAT8(DEFAULT_TS_MATCH_SEL); } /* * The "@@" operator is strict, so we can cope with NULL right away */ if (((Const *) other)->constisnull) { ReleaseVariableStats(vardata); PG_RETURN_FLOAT8(0.0); } /* * OK, there's a Var and a Const we're dealing with here. We need the Var * to be a TSVector (or else we don't have any useful statistic for it). * We have to check this because the Var might be the TSQuery not the * TSVector. */ if (vardata.vartype == TSVECTOROID) { /* tsvector @@ tsquery or the other way around */ Assert(((Const *) other)->consttype == TSQUERYOID); selec = tsquerysel(&vardata, ((Const *) other)->constvalue); } else { /* The Var is something we don't have useful statistics for */ selec = DEFAULT_TS_MATCH_SEL; } ReleaseVariableStats(vardata); CLAMP_PROBABILITY(selec); PG_RETURN_FLOAT8((float8) selec); }
/* ---------------- * 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!) Note that * the given tlist should be a list of ExprState nodes, not Expr nodes. * * 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. * ---------------- */ ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, TupleDesc inputDesc) { ProjectionInfo *projInfo = makeNode(ProjectionInfo); int len = ExecTargetListLength(targetList); int *workspace; int *varSlotOffsets; int *varNumbers; int *varOutputCols; List *exprlist; int numSimpleVars; bool directMap; ListCell *tl; projInfo->pi_exprContext = econtext; projInfo->pi_slot = slot; /* since these are all int arrays, we need do just one palloc */ workspace = (int *) palloc(len * 3 * sizeof(int)); projInfo->pi_varSlotOffsets = varSlotOffsets = workspace; projInfo->pi_varNumbers = varNumbers = workspace + len; projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2; projInfo->pi_lastInnerVar = 0; projInfo->pi_lastOuterVar = 0; projInfo->pi_lastScanVar = 0; /* * We separate the target list elements into simple Var references and * expressions which require the full ExecTargetList machinery. To be a * simple Var, a Var has to be a user attribute and not mismatch the * inputDesc. (Note: if there is a type mismatch then ExecEvalScalarVar * will probably throw an error at runtime, but we leave that to it.) */ exprlist = NIL; numSimpleVars = 0; directMap = true; foreach(tl, targetList) { GenericExprState *gstate = (GenericExprState *) lfirst(tl); Var *variable = (Var *) gstate->arg->expr; bool isSimpleVar = false; if (variable != NULL && IsA(variable, Var) && variable->varattno > 0) { if (!inputDesc) isSimpleVar = true; /* can't check type, assume OK */ else if (variable->varattno <= inputDesc->natts) { Form_pg_attribute attr; attr = inputDesc->attrs[variable->varattno - 1]; if (!attr->attisdropped && variable->vartype == attr->atttypid) isSimpleVar = true; } } if (isSimpleVar) { TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; AttrNumber attnum = variable->varattno; varNumbers[numSimpleVars] = attnum; varOutputCols[numSimpleVars] = tle->resno; if (tle->resno != numSimpleVars + 1) directMap = false; switch (variable->varno) { case INNER_VAR: varSlotOffsets[numSimpleVars] = offsetof(ExprContext, ecxt_innertuple); if (projInfo->pi_lastInnerVar < attnum) projInfo->pi_lastInnerVar = attnum; break; case OUTER_VAR: varSlotOffsets[numSimpleVars] = offsetof(ExprContext, ecxt_outertuple); if (projInfo->pi_lastOuterVar < attnum) projInfo->pi_lastOuterVar = attnum; break; /* INDEX_VAR is handled by default case */ default: varSlotOffsets[numSimpleVars] = offsetof(ExprContext, ecxt_scantuple); if (projInfo->pi_lastScanVar < attnum) projInfo->pi_lastScanVar = attnum; break; } numSimpleVars++; } else { /* Not a simple variable, add it to generic targetlist */ exprlist = lappend(exprlist, gstate); /* Examine expr to include contained Vars in lastXXXVar counts */ get_last_attnums((Node *) variable, projInfo); } }
/* ---------------------------------------------------------------- * ExecInitCteScan * ---------------------------------------------------------------- */ CteScanState * ExecInitCteScan(CteScan *node, EState *estate, int eflags) { CteScanState *scanstate; ParamExecData *prmdata; /* check for unsupported flags */ Assert(!(eflags & EXEC_FLAG_MARK)); /* * For the moment we have to force the tuplestore to allow REWIND, because * we might be asked to rescan the CTE even though upper levels didn't * tell us to be prepared to do it efficiently. Annoying, since this * prevents truncation of the tuplestore. XXX FIXME */ eflags |= EXEC_FLAG_REWIND; /* * CteScan should not have any children. */ Assert(outerPlan(node) == NULL); Assert(innerPlan(node) == NULL); /* * create new CteScanState for node */ scanstate = makeNode(CteScanState); scanstate->ss.ps.plan = (Plan *) node; scanstate->ss.ps.state = estate; scanstate->eflags = eflags; scanstate->cte_table = NULL; scanstate->eof_cte = false; /* * Find the already-initialized plan for the CTE query. */ scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates, node->ctePlanId - 1); /* * The Param slot associated with the CTE query is used to hold a pointer * to the CteState of the first CteScan node that initializes for this * CTE. This node will be the one that holds the shared state for all the * CTEs, particularly the shared tuplestore. */ prmdata = &(estate->es_param_exec_vals[node->cteParam]); Assert(prmdata->execPlan == NULL); Assert(!prmdata->isnull); scanstate->leader = (CteScanState *) DatumGetPointer(prmdata->value); if (scanstate->leader == NULL) { /* I am the leader */ prmdata->value = PointerGetDatum(scanstate); scanstate->leader = scanstate; scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem); tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags); scanstate->readptr = 0; } else { /* Not the leader */ Assert(IsA(scanstate->leader, CteScanState)); scanstate->readptr = tuplestore_alloc_read_pointer(scanstate->leader->cte_table, scanstate->eflags); } /* * Miscellaneous initialization * * create expression context for node */ ExecAssignExprContext(estate, &scanstate->ss.ps); /* * 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); /* * tuple table initialization */ ExecInitResultTupleSlot(estate, &scanstate->ss.ps); ExecInitScanTupleSlot(estate, &scanstate->ss); /* * The scan tuple type (ie, the rowtype we expect to find in the work * table) is the same as the result rowtype of the CTE query. */ ExecAssignScanType(&scanstate->ss, ExecGetResultType(scanstate->cteplanstate)); /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&scanstate->ss.ps); ExecAssignScanProjectionInfo(&scanstate->ss); scanstate->ss.ps.ps_TupFromTlist = false; return scanstate; }
/* * Helper function for the various SQL callable logical decoding functions. */ static Datum pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool binary) { Name name; XLogRecPtr upto_lsn; int32 upto_nchanges; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; MemoryContext per_query_ctx; MemoryContext oldcontext; XLogRecPtr end_of_wal; XLogRecPtr startptr; LogicalDecodingContext *ctx; ResourceOwner old_resowner = CurrentResourceOwner; ArrayType *arr; Size ndim; List *options = NIL; DecodingOutputState *p; check_permissions(); CheckLogicalDecodingRequirements(); if (PG_ARGISNULL(0)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("slot name must not be null"))); name = PG_GETARG_NAME(0); if (PG_ARGISNULL(1)) upto_lsn = InvalidXLogRecPtr; else upto_lsn = PG_GETARG_LSN(1); if (PG_ARGISNULL(2)) upto_nchanges = InvalidXLogRecPtr; else upto_nchanges = PG_GETARG_INT32(2); if (PG_ARGISNULL(3)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("options array must not be null"))); arr = PG_GETARG_ARRAYTYPE_P(3); /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not allowed in this context"))); /* state to write output to */ p = palloc0(sizeof(DecodingOutputState)); p->binary_output = binary; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* Deconstruct options array */ ndim = ARR_NDIM(arr); if (ndim > 1) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("array must be one-dimensional"))); } else if (array_contains_nulls(arr)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("array must not contain nulls"))); } else if (ndim == 1) { int nelems; Datum *datum_opts; int i; Assert(ARR_ELEMTYPE(arr) == TEXTOID); deconstruct_array(arr, TEXTOID, -1, false, 'i', &datum_opts, NULL, &nelems); if (nelems % 2 != 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("array must have even number of elements"))); for (i = 0; i < nelems; i += 2) { char *name = TextDatumGetCString(datum_opts[i]); char *opt = TextDatumGetCString(datum_opts[i + 1]); options = lappend(options, makeDefElem(name, (Node *) makeString(opt), -1)); } } p->tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = p->tupstore; rsinfo->setDesc = p->tupdesc; /* compute the current end-of-wal */ if (!RecoveryInProgress()) end_of_wal = GetFlushRecPtr(); else end_of_wal = GetXLogReplayRecPtr(NULL); ReplicationSlotAcquire(NameStr(*name)); PG_TRY(); { /* restart at slot's confirmed_flush */ ctx = CreateDecodingContext(InvalidXLogRecPtr, options, logical_read_local_xlog_page, LogicalOutputPrepareWrite, LogicalOutputWrite); MemoryContextSwitchTo(oldcontext); /* * Check whether the output plugin writes textual output if that's * what we need. */ if (!binary && ctx->options.output_type !=OUTPUT_PLUGIN_TEXTUAL_OUTPUT) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("logical decoding output plugin \"%s\" produces binary output, but function \"%s\" expects textual data", NameStr(MyReplicationSlot->data.plugin), format_procedure(fcinfo->flinfo->fn_oid)))); ctx->output_writer_private = p; /* * Decoding of WAL must start at restart_lsn so that the entirety of * xacts that committed after the slot's confirmed_flush can be * accumulated into reorder buffers. */ startptr = MyReplicationSlot->data.restart_lsn; CurrentResourceOwner = ResourceOwnerCreate(CurrentResourceOwner, "logical decoding"); /* invalidate non-timetravel entries */ InvalidateSystemCaches(); while ((startptr != InvalidXLogRecPtr && startptr < end_of_wal) || (ctx->reader->EndRecPtr != InvalidXLogRecPtr && ctx->reader->EndRecPtr < end_of_wal)) { XLogRecord *record; char *errm = NULL; record = XLogReadRecord(ctx->reader, startptr, &errm); if (errm) elog(ERROR, "%s", errm); /* * Now that we've set up the xlog reader state, subsequent calls * pass InvalidXLogRecPtr to say "continue from last record" */ startptr = InvalidXLogRecPtr; /* * The {begin_txn,change,commit_txn}_wrapper callbacks above will * store the description into our tuplestore. */ if (record != NULL) LogicalDecodingProcessRecord(ctx, ctx->reader); /* check limits */ if (upto_lsn != InvalidXLogRecPtr && upto_lsn <= ctx->reader->EndRecPtr) break; if (upto_nchanges != 0 && upto_nchanges <= p->returned_rows) break; CHECK_FOR_INTERRUPTS(); } tuplestore_donestoring(tupstore); CurrentResourceOwner = old_resowner; /* * Next time, start where we left off. (Hunting things, the family * business..) */ if (ctx->reader->EndRecPtr != InvalidXLogRecPtr && confirm) { LogicalConfirmReceivedLocation(ctx->reader->EndRecPtr); /* * If only the confirmed_flush_lsn has changed the slot won't get * marked as dirty by the above. Callers on the walsender interface * are expected to keep track of their own progress and don't need * it written out. But SQL-interface users cannot specify their own * start positions and it's harder for them to keep track of their * progress, so we should make more of an effort to save it for them. * * Dirty the slot so it's written out at the next checkpoint. We'll * still lose its position on crash, as documented, but it's better * than always losing the position even on clean restart. */ ReplicationSlotMarkDirty(); } /* free context, call shutdown callback */ FreeDecodingContext(ctx); ReplicationSlotRelease(); InvalidateSystemCaches(); } PG_CATCH(); { /* clear all timetravel entries */ InvalidateSystemCaches(); PG_RE_THROW(); } PG_END_TRY(); return (Datum) 0; }
/* * 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) { List *evalList = tidstate->tss_tidquals; ExprContext *econtext = tidstate->ss.ps.ps_ExprContext; BlockNumber nblocks; ItemPointerData *tidList; int numAllocTids; int numTids; ListCell *l; /* * 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.) */ 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. */ numAllocTids = list_length(evalList); tidList = (ItemPointerData *) palloc(numAllocTids * sizeof(ItemPointerData)); numTids = 0; tidstate->tss_isCurrentOf = false; foreach(l, evalList) { ExprState *exstate = (ExprState *) lfirst(l); Expr *expr = exstate->expr; ItemPointer itemptr; bool isNull; if (is_opclause(expr)) { FuncExprState *fexstate = (FuncExprState *) exstate; Node *arg1; Node *arg2; arg1 = get_leftop(expr); arg2 = get_rightop(expr); if (IsCTIDVar(arg1)) exstate = (ExprState *) lsecond(fexstate->args); else if (IsCTIDVar(arg2)) exstate = (ExprState *) linitial(fexstate->args); else elog(ERROR, "could not identify CTID variable"); itemptr = (ItemPointer) DatumGetPointer(ExecEvalExprSwitchContext(exstate, econtext, &isNull, NULL)); if (!isNull && ItemPointerIsValid(itemptr) && ItemPointerGetBlockNumber(itemptr) < nblocks) { if (numTids >= numAllocTids) { numAllocTids *= 2; tidList = (ItemPointerData *) repalloc(tidList, numAllocTids * sizeof(ItemPointerData)); } tidList[numTids++] = *itemptr; } } else if (expr && IsA(expr, ScalarArrayOpExpr)) { ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate; Datum arraydatum; ArrayType *itemarray; Datum *ipdatums; bool *ipnulls; int ndatums; int i; exstate = (ExprState *) lsecond(saexstate->fxprstate.args); arraydatum = ExecEvalExprSwitchContext(exstate, econtext, &isNull, NULL); if (isNull) continue; itemarray = DatumGetArrayTypeP(arraydatum); deconstruct_array(itemarray, TIDOID, SizeOfIptrData, false, 's', &ipdatums, &ipnulls, &ndatums); if (numTids + ndatums > numAllocTids) { numAllocTids = numTids + ndatums; tidList = (ItemPointerData *) repalloc(tidList, numAllocTids * sizeof(ItemPointerData)); } for (i = 0; i < ndatums; i++) { if (!ipnulls[i]) { itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]); if (ItemPointerIsValid(itemptr) && ItemPointerGetBlockNumber(itemptr) < nblocks) tidList[numTids++] = *itemptr; } } pfree(ipdatums); pfree(ipnulls); } else if (expr && IsA(expr, CurrentOfExpr)) { CurrentOfExpr *cexpr = (CurrentOfExpr *) expr; ItemPointerData cursor_tid; if (execCurrentOf(cexpr, econtext, RelationGetRelid(tidstate->ss.ss_currentRelation), &cursor_tid)) { if (numTids >= numAllocTids) { numAllocTids *= 2; tidList = (ItemPointerData *) repalloc(tidList, numAllocTids * sizeof(ItemPointerData)); } tidList[numTids++] = cursor_tid; tidstate->tss_isCurrentOf = true; } } else elog(ERROR, "could not identify CTID expression"); }
/* * Retrieve statement statistics. */ Datum pg_stat_statements(PG_FUNCTION_ARGS) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; Oid userid = GetUserId(); bool is_superuser = superuser(); HASH_SEQ_STATUS hash_seq; pgssEntry *entry; if (!pgss || !pgss_hash) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("pg_stat_statements must be loaded via shared_preload_libraries"))); /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); LWLockAcquire(pgss->lock, LW_SHARED); hash_seq_init(&hash_seq, pgss_hash); while ((entry = hash_seq_search(&hash_seq)) != NULL) { Datum values[PG_STAT_STATEMENTS_COLS]; bool nulls[PG_STAT_STATEMENTS_COLS]; int i = 0; Counters tmp; memset(values, 0, sizeof(values)); memset(nulls, 0, sizeof(nulls)); values[i++] = ObjectIdGetDatum(entry->key.userid); values[i++] = ObjectIdGetDatum(entry->key.dbid); if (is_superuser || entry->key.userid == userid) { char *qstr; qstr = (char *) pg_do_encoding_conversion((unsigned char *) entry->query, entry->key.query_len, entry->key.encoding, GetDatabaseEncoding()); values[i++] = CStringGetTextDatum(qstr); if (qstr != entry->query) pfree(qstr); } else values[i++] = CStringGetTextDatum("<insufficient privilege>"); /* copy counters to a local variable to keep locking time short */ { volatile pgssEntry *e = (volatile pgssEntry *) entry; SpinLockAcquire(&e->mutex); tmp = e->counters; SpinLockRelease(&e->mutex); } values[i++] = Int64GetDatumFast(tmp.calls); values[i++] = Float8GetDatumFast(tmp.total_time); values[i++] = Int64GetDatumFast(tmp.rows); values[i++] = Int64GetDatumFast(tmp.shared_blks_hit); values[i++] = Int64GetDatumFast(tmp.shared_blks_read); values[i++] = Int64GetDatumFast(tmp.shared_blks_written); values[i++] = Int64GetDatumFast(tmp.local_blks_hit); values[i++] = Int64GetDatumFast(tmp.local_blks_read); values[i++] = Int64GetDatumFast(tmp.local_blks_written); values[i++] = Int64GetDatumFast(tmp.temp_blks_read); values[i++] = Int64GetDatumFast(tmp.temp_blks_written); Assert(i == PG_STAT_STATEMENTS_COLS); tuplestore_putvalues(tupstore, tupdesc, values, nulls); } LWLockRelease(pgss->lock); /* clean up and return the tuplestore */ tuplestore_donestoring(tupstore); return (Datum) 0; }
/* * pg_stop_backup_v2: finish taking exclusive or nonexclusive on-line backup. * * Works the same as pg_stop_backup, except for non-exclusive backups it returns * the backup label and tablespace map files as text fields in as part of the * resultset. * * Permission checking for this function is managed through the normal * GRANT system. */ Datum pg_stop_backup_v2(PG_FUNCTION_ARGS) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; Datum values[3]; bool nulls[3]; bool exclusive = PG_GETARG_BOOL(0); XLogRecPtr stoppoint; /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); MemSet(values, 0, sizeof(values)); MemSet(nulls, 0, sizeof(nulls)); if (exclusive) { if (nonexclusive_backup_running) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("non-exclusive backup in progress"), errhint("Did you mean to use pg_stop_backup('f')?"))); /* * Stop the exclusive backup, and since we're in an exclusive backup * return NULL for both backup_label and tablespace_map. */ stoppoint = do_pg_stop_backup(NULL, true, NULL); exclusive_backup_running = false; nulls[1] = true; nulls[2] = true; } else { if (!nonexclusive_backup_running) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("non-exclusive backup is not in progress"), errhint("Did you mean to use pg_stop_backup('t')?"))); /* * Stop the non-exclusive backup. Return a copy of the backup * label and tablespace map so they can be written to disk by * the caller. */ stoppoint = do_pg_stop_backup(label_file->data, true, NULL); nonexclusive_backup_running = false; cancel_before_shmem_exit(nonexclusive_base_backup_cleanup, (Datum) 0); values[1] = CStringGetTextDatum(label_file->data); values[2] = CStringGetTextDatum(tblspc_map_file->data); /* Free structures allocated in TopMemoryContext */ pfree(label_file->data); pfree(label_file); label_file = NULL; pfree(tblspc_map_file->data); pfree(tblspc_map_file); tblspc_map_file = NULL; } /* Stoppoint is included on both exclusive and nonexclusive backups */ values[0] = LSNGetDatum(stoppoint); tuplestore_putvalues(tupstore, tupdesc, values, nulls); tuplestore_donestoring(typstore); return (Datum) 0; }
/* * PerformCursorOpen * Execute SQL DECLARE CURSOR command. * * The query has already been through parse analysis, rewriting, and planning. * When it gets here, it looks like a SELECT PlannedStmt, except that the * utilityStmt field is set. */ void PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params, const char *queryString, bool isTopLevel) { DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt; Portal portal; MemoryContext oldContext; if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt)) elog(ERROR, "PerformCursorOpen called for non-cursor query"); /* * Disallow empty-string cursor name (conflicts with protocol-level * unnamed portal). */ if (!cstmt->portalname || cstmt->portalname[0] == '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_NAME), errmsg("invalid cursor name: must not be empty"))); /* * If this is a non-holdable cursor, we require that this statement has * been executed inside a transaction block (or else, it would have no * user-visible effect). */ if (!(cstmt->options & CURSOR_OPT_HOLD)) RequireTransactionChain(isTopLevel, "DECLARE CURSOR"); /* * Create a portal and copy the plan and queryString into its memory. */ portal = CreatePortal(cstmt->portalname, false, false); oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); stmt = copyObject(stmt); stmt->utilityStmt = NULL; /* make it look like plain SELECT */ queryString = pstrdup(queryString); PortalDefineQuery(portal, NULL, queryString, "SELECT", /* cursor's query is always a SELECT */ list_make1(stmt), NULL); /*---------- * Also copy the outer portal's parameter list into the inner portal's * memory context. We want to pass down the parameter values in case we * had a command like * DECLARE c CURSOR FOR SELECT ... WHERE foo = $1 * This will have been parsed using the outer parameter set and the * parameter value needs to be preserved for use when the cursor is * executed. *---------- */ params = copyParamList(params); MemoryContextSwitchTo(oldContext); /* * Set up options for portal. * * If the user didn't specify a SCROLL type, allow or disallow scrolling * based on whether it would require any additional runtime overhead to do * so. Also, we disallow scrolling for FOR UPDATE cursors. */ portal->cursorOptions = cstmt->options; if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL))) { if (stmt->rowMarks == NIL && ExecSupportsBackwardScan(stmt->planTree)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; } /* * Start execution, inserting parameters if any. */ PortalStart(portal, params, 0, GetActiveSnapshot()); Assert(portal->strategy == PORTAL_ONE_SELECT); /* * We're done; the query won't actually be run until PerformPortalFetch is * called. */ }
/* * _outNode - * converts a Node into binary string and append it to 'str' */ static void _outNode(StringInfo str, void *obj) { if (obj == NULL) { int16 tg = 0; appendBinaryStringInfo(str, (const char *)&tg, sizeof(int16)); } else if (IsA(obj, List) ||IsA(obj, IntList) || IsA(obj, OidList)) _outList(str, obj); else if (IsA(obj, Integer) || IsA(obj, Float) || IsA(obj, String) || IsA(obj, Null) || IsA(obj, BitString)) { _outValue(str, obj); } else { switch (nodeTag(obj)) { case T_PlannedStmt: _outPlannedStmt(str,obj); break; case T_QueryDispatchDesc: _outQueryDispatchDesc(str,obj); break; case T_OidAssignment: _outOidAssignment(str,obj); break; case T_Plan: _outPlan(str, obj); break; case T_Result: _outResult(str, obj); break; case T_Repeat: _outRepeat(str, obj); break; case T_Append: _outAppend(str, obj); break; case T_Sequence: _outSequence(str, obj); break; case T_BitmapAnd: _outBitmapAnd(str, obj); break; case T_BitmapOr: _outBitmapOr(str, obj); break; case T_Scan: _outScan(str, obj); break; case T_SeqScan: _outSeqScan(str, obj); break; case T_AppendOnlyScan: _outAppendOnlyScan(str, obj); break; case T_AOCSScan: _outAOCSScan(str, obj); break; case T_TableScan: _outTableScan(str, obj); break; case T_DynamicTableScan: _outDynamicTableScan(str, obj); break; case T_ExternalScan: _outExternalScan(str, obj); break; case T_IndexScan: _outIndexScan(str, obj); break; case T_DynamicIndexScan: _outDynamicIndexScan(str, obj); break; case T_BitmapIndexScan: _outBitmapIndexScan(str, obj); break; case T_BitmapHeapScan: _outBitmapHeapScan(str, obj); break; case T_BitmapAppendOnlyScan: _outBitmapAppendOnlyScan(str, obj); break; case T_BitmapTableScan: _outBitmapTableScan(str, obj); break; case T_TidScan: _outTidScan(str, obj); break; case T_SubqueryScan: _outSubqueryScan(str, obj); break; case T_FunctionScan: _outFunctionScan(str, obj); break; case T_ValuesScan: _outValuesScan(str, obj); break; case T_Join: _outJoin(str, obj); break; case T_NestLoop: _outNestLoop(str, obj); break; case T_MergeJoin: _outMergeJoin(str, obj); break; case T_HashJoin: _outHashJoin(str, obj); break; case T_Agg: _outAgg(str, obj); break; case T_WindowKey: _outWindowKey(str, obj); break; case T_Window: _outWindow(str, obj); break; case T_TableFunctionScan: _outTableFunctionScan(str, obj); break; case T_Material: _outMaterial(str, obj); break; case T_ShareInputScan: _outShareInputScan(str, obj); break; case T_Sort: _outSort(str, obj); break; case T_Unique: _outUnique(str, obj); break; case T_SetOp: _outSetOp(str, obj); break; case T_Limit: _outLimit(str, obj); break; case T_Hash: _outHash(str, obj); break; case T_Motion: _outMotion(str, obj); break; case T_DML: _outDML(str, obj); break; case T_SplitUpdate: _outSplitUpdate(str, obj); break; case T_RowTrigger: _outRowTrigger(str, obj); break; case T_AssertOp: _outAssertOp(str, obj); break; case T_PartitionSelector: _outPartitionSelector(str, obj); break; case T_Alias: _outAlias(str, obj); break; case T_RangeVar: _outRangeVar(str, obj); break; case T_IntoClause: _outIntoClause(str, obj); break; case T_Var: _outVar(str, obj); break; case T_Const: _outConst(str, obj); break; case T_Param: _outParam(str, obj); break; case T_Aggref: _outAggref(str, obj); break; case T_AggOrder: _outAggOrder(str, obj); break; case T_WindowRef: _outWindowRef(str, obj); break; case T_ArrayRef: _outArrayRef(str, obj); break; case T_FuncExpr: _outFuncExpr(str, obj); break; case T_OpExpr: _outOpExpr(str, obj); break; case T_DistinctExpr: _outDistinctExpr(str, obj); break; case T_ScalarArrayOpExpr: _outScalarArrayOpExpr(str, obj); break; case T_BoolExpr: _outBoolExpr(str, obj); break; case T_SubLink: _outSubLink(str, obj); break; case T_SubPlan: _outSubPlan(str, obj); break; case T_FieldSelect: _outFieldSelect(str, obj); break; case T_FieldStore: _outFieldStore(str, obj); break; case T_RelabelType: _outRelabelType(str, obj); break; case T_CoerceViaIO: _outCoerceViaIO(str, obj); break; case T_ArrayCoerceExpr: _outArrayCoerceExpr(str, obj); break; case T_ConvertRowtypeExpr: _outConvertRowtypeExpr(str, obj); break; case T_CaseExpr: _outCaseExpr(str, obj); break; case T_CaseWhen: _outCaseWhen(str, obj); break; case T_CaseTestExpr: _outCaseTestExpr(str, obj); break; case T_ArrayExpr: _outArrayExpr(str, obj); break; case T_RowExpr: _outRowExpr(str, obj); break; case T_RowCompareExpr: _outRowCompareExpr(str, obj); break; case T_CoalesceExpr: _outCoalesceExpr(str, obj); break; case T_MinMaxExpr: _outMinMaxExpr(str, obj); break; case T_NullIfExpr: _outNullIfExpr(str, obj); break; case T_NullTest: _outNullTest(str, obj); break; case T_BooleanTest: _outBooleanTest(str, obj); break; case T_XmlExpr: _outXmlExpr(str, obj); break; case T_CoerceToDomain: _outCoerceToDomain(str, obj); break; case T_CoerceToDomainValue: _outCoerceToDomainValue(str, obj); break; case T_SetToDefault: _outSetToDefault(str, obj); break; case T_CurrentOfExpr: _outCurrentOfExpr(str, obj); break; case T_TargetEntry: _outTargetEntry(str, obj); break; case T_RangeTblRef: _outRangeTblRef(str, obj); break; case T_JoinExpr: _outJoinExpr(str, obj); break; case T_FromExpr: _outFromExpr(str, obj); break; case T_Flow: _outFlow(str, obj); break; case T_Path: _outPath(str, obj); break; case T_IndexPath: _outIndexPath(str, obj); break; case T_BitmapHeapPath: _outBitmapHeapPath(str, obj); break; case T_BitmapAppendOnlyPath: _outBitmapAppendOnlyPath(str, obj); break; case T_BitmapAndPath: _outBitmapAndPath(str, obj); break; case T_BitmapOrPath: _outBitmapOrPath(str, obj); break; case T_TidPath: _outTidPath(str, obj); break; case T_AppendPath: _outAppendPath(str, obj); break; case T_AppendOnlyPath: _outAppendOnlyPath(str, obj); break; case T_AOCSPath: _outAOCSPath(str, obj); break; case T_ResultPath: _outResultPath(str, obj); break; case T_MaterialPath: _outMaterialPath(str, obj); break; case T_UniquePath: _outUniquePath(str, obj); break; case T_NestPath: _outNestPath(str, obj); break; case T_MergePath: _outMergePath(str, obj); break; case T_HashPath: _outHashPath(str, obj); break; case T_CdbMotionPath: _outCdbMotionPath(str, obj); break; case T_PlannerInfo: _outPlannerInfo(str, obj); break; case T_PlannerParamItem: _outPlannerParamItem(str, obj); break; case T_RelOptInfo: _outRelOptInfo(str, obj); break; case T_IndexOptInfo: _outIndexOptInfo(str, obj); break; case T_CdbRelDedupInfo: _outCdbRelDedupInfo(str, obj); break; case T_PathKey: _outPathKey(str, obj); break; case T_RestrictInfo: _outRestrictInfo(str, obj); break; case T_InnerIndexscanInfo: _outInnerIndexscanInfo(str, obj); break; case T_OuterJoinInfo: _outOuterJoinInfo(str, obj); break; case T_InClauseInfo: _outInClauseInfo(str, obj); break; case T_AppendRelInfo: _outAppendRelInfo(str, obj); break; case T_CreateExtensionStmt: _outCreateExtensionStmt(str, obj); break; case T_GrantStmt: _outGrantStmt(str, obj); break; case T_PrivGrantee: _outPrivGrantee(str, obj); break; case T_FuncWithArgs: _outFuncWithArgs(str, obj); break; case T_GrantRoleStmt: _outGrantRoleStmt(str, obj); break; case T_LockStmt: _outLockStmt(str, obj); break; case T_CreateStmt: _outCreateStmt(str, obj); break; case T_ColumnReferenceStorageDirective: _outColumnReferenceStorageDirective(str, obj); break; case T_PartitionBy: _outPartitionBy(str, obj); break; case T_PartitionElem: _outPartitionElem(str, obj); break; case T_PartitionRangeItem: _outPartitionRangeItem(str, obj); break; case T_PartitionBoundSpec: _outPartitionBoundSpec(str, obj); break; case T_PartitionSpec: _outPartitionSpec(str, obj); break; case T_PartitionValuesSpec: _outPartitionValuesSpec(str, obj); break; case T_Partition: _outPartition(str, obj); break; case T_PartitionRule: _outPartitionRule(str, obj); break; case T_PartitionNode: _outPartitionNode(str, obj); break; case T_PgPartRule: _outPgPartRule(str, obj); break; case T_SegfileMapNode: _outSegfileMapNode(str, obj); break; case T_ExtTableTypeDesc: _outExtTableTypeDesc(str, obj); break; case T_CreateExternalStmt: _outCreateExternalStmt(str, obj); break; case T_IndexStmt: _outIndexStmt(str, obj); break; case T_ReindexStmt: _outReindexStmt(str, obj); break; case T_ConstraintsSetStmt: _outConstraintsSetStmt(str, obj); break; case T_CreateFunctionStmt: _outCreateFunctionStmt(str, obj); break; case T_FunctionParameter: _outFunctionParameter(str, obj); break; case T_RemoveFuncStmt: _outRemoveFuncStmt(str, obj); break; case T_AlterFunctionStmt: _outAlterFunctionStmt(str, obj); break; case T_DefineStmt: _outDefineStmt(str,obj); break; case T_CompositeTypeStmt: _outCompositeTypeStmt(str,obj); break; case T_CreateEnumStmt: _outCreateEnumStmt(str,obj); break; case T_CreateCastStmt: _outCreateCastStmt(str,obj); break; case T_DropCastStmt: _outDropCastStmt(str,obj); break; case T_CreateOpClassStmt: _outCreateOpClassStmt(str,obj); break; case T_CreateOpClassItem: _outCreateOpClassItem(str,obj); break; case T_CreateOpFamilyStmt: _outCreateOpFamilyStmt(str,obj); break; case T_AlterOpFamilyStmt: _outAlterOpFamilyStmt(str,obj); break; case T_RemoveOpClassStmt: _outRemoveOpClassStmt(str,obj); break; case T_RemoveOpFamilyStmt: _outRemoveOpFamilyStmt(str,obj); break; case T_CreateConversionStmt: _outCreateConversionStmt(str,obj); break; case T_ViewStmt: _outViewStmt(str, obj); break; case T_RuleStmt: _outRuleStmt(str, obj); break; case T_DropStmt: _outDropStmt(str, obj); break; case T_DropPropertyStmt: _outDropPropertyStmt(str, obj); break; case T_DropOwnedStmt: _outDropOwnedStmt(str, obj); break; case T_ReassignOwnedStmt: _outReassignOwnedStmt(str, obj); break; case T_TruncateStmt: _outTruncateStmt(str, obj); break; case T_AlterTableStmt: _outAlterTableStmt(str, obj); break; case T_AlterTableCmd: _outAlterTableCmd(str, obj); break; case T_SetDistributionCmd: _outSetDistributionCmd(str, obj); break; case T_InheritPartitionCmd: _outInheritPartitionCmd(str, obj); break; case T_AlterPartitionCmd: _outAlterPartitionCmd(str, obj); break; case T_AlterPartitionId: _outAlterPartitionId(str, obj); break; case T_CreateRoleStmt: _outCreateRoleStmt(str, obj); break; case T_DropRoleStmt: _outDropRoleStmt(str, obj); break; case T_AlterRoleStmt: _outAlterRoleStmt(str, obj); break; case T_AlterRoleSetStmt: _outAlterRoleSetStmt(str, obj); break; case T_AlterObjectSchemaStmt: _outAlterObjectSchemaStmt(str, obj); break; case T_AlterOwnerStmt: _outAlterOwnerStmt(str, obj); break; case T_RenameStmt: _outRenameStmt(str, obj); break; case T_CreateSeqStmt: _outCreateSeqStmt(str, obj); break; case T_AlterSeqStmt: _outAlterSeqStmt(str, obj); break; case T_ClusterStmt: _outClusterStmt(str, obj); break; case T_CreatedbStmt: _outCreatedbStmt(str, obj); break; case T_DropdbStmt: _outDropdbStmt(str, obj); break; case T_CreateDomainStmt: _outCreateDomainStmt(str, obj); break; case T_AlterDomainStmt: _outAlterDomainStmt(str, obj); break; case T_TransactionStmt: _outTransactionStmt(str, obj); break; case T_NotifyStmt: _outNotifyStmt(str, obj); break; case T_DeclareCursorStmt: _outDeclareCursorStmt(str, obj); break; case T_SingleRowErrorDesc: _outSingleRowErrorDesc(str, obj); break; case T_CopyStmt: _outCopyStmt(str, obj); break; case T_SelectStmt: _outSelectStmt(str, obj); break; case T_InsertStmt: _outInsertStmt(str, obj); break; case T_DeleteStmt: _outDeleteStmt(str, obj); break; case T_UpdateStmt: _outUpdateStmt(str, obj); break; case T_ColumnDef: _outColumnDef(str, obj); break; case T_TypeName: _outTypeName(str, obj); break; case T_SortBy: _outSortBy(str, obj); break; case T_TypeCast: _outTypeCast(str, obj); break; case T_IndexElem: _outIndexElem(str, obj); break; case T_Query: _outQuery(str, obj); break; case T_SortClause: _outSortClause(str, obj); break; case T_GroupClause: _outGroupClause(str, obj); break; case T_GroupingClause: _outGroupingClause(str, obj); break; case T_GroupingFunc: _outGroupingFunc(str, obj); break; case T_Grouping: _outGrouping(str, obj); break; case T_GroupId: _outGroupId(str, obj); break; case T_WindowSpecParse: _outWindowSpecParse(str, obj); break; case T_WindowSpec: _outWindowSpec(str, obj); break; case T_WindowFrame: _outWindowFrame(str, obj); break; case T_WindowFrameEdge: _outWindowFrameEdge(str, obj); break; case T_PercentileExpr: _outPercentileExpr(str, obj); break; case T_RowMarkClause: _outRowMarkClause(str, obj); break; case T_WithClause: _outWithClause(str, obj); break; case T_CommonTableExpr: _outCommonTableExpr(str, obj); break; case T_SetOperationStmt: _outSetOperationStmt(str, obj); break; case T_RangeTblEntry: _outRangeTblEntry(str, obj); break; case T_A_Expr: _outAExpr(str, obj); break; case T_ColumnRef: _outColumnRef(str, obj); break; case T_ParamRef: _outParamRef(str, obj); break; case T_A_Const: _outAConst(str, obj); break; case T_A_Indices: _outA_Indices(str, obj); break; case T_A_Indirection: _outA_Indirection(str, obj); break; case T_A_ArrayExpr: _outA_ArrayExpr(str,obj); break; case T_ResTarget: _outResTarget(str, obj); break; case T_Constraint: _outConstraint(str, obj); break; case T_FkConstraint: _outFkConstraint(str, obj); break; case T_FuncCall: _outFuncCall(str, obj); break; case T_DefElem: _outDefElem(str, obj); break; case T_LockingClause: _outLockingClause(str, obj); break; case T_XmlSerialize: _outXmlSerialize(str, obj); break; case T_CreateSchemaStmt: _outCreateSchemaStmt(str, obj); break; case T_CreatePLangStmt: _outCreatePLangStmt(str, obj); break; case T_DropPLangStmt: _outDropPLangStmt(str, obj); break; case T_VacuumStmt: _outVacuumStmt(str, obj); break; case T_CdbProcess: _outCdbProcess(str, obj); break; case T_Slice: _outSlice(str, obj); break; case T_SliceTable: _outSliceTable(str, obj); break; case T_CursorPosInfo: _outCursorPosInfo(str, obj); break; case T_VariableSetStmt: _outVariableSetStmt(str, obj); break; case T_DMLActionExpr: _outDMLActionExpr(str, obj); break; case T_PartOidExpr: _outPartOidExpr(str, obj); break; case T_PartDefaultExpr: _outPartDefaultExpr(str, obj); break; case T_PartBoundExpr: _outPartBoundExpr(str, obj); break; case T_PartBoundInclusionExpr: _outPartBoundInclusionExpr(str, obj); break; case T_PartBoundOpenExpr: _outPartBoundOpenExpr(str, obj); break; case T_CreateTrigStmt: _outCreateTrigStmt(str, obj); break; case T_CreateFileSpaceStmt: _outCreateFileSpaceStmt(str, obj); break; case T_FileSpaceEntry: _outFileSpaceEntry(str, obj); break; case T_CreateTableSpaceStmt: _outCreateTableSpaceStmt(str, obj); break; case T_CreateQueueStmt: _outCreateQueueStmt(str, obj); break; case T_AlterQueueStmt: _outAlterQueueStmt(str, obj); break; case T_DropQueueStmt: _outDropQueueStmt(str, obj); break; case T_CommentStmt: _outCommentStmt(str, obj); break; case T_TableValueExpr: _outTableValueExpr(str, obj); break; case T_DenyLoginInterval: _outDenyLoginInterval(str, obj); break; case T_DenyLoginPoint: _outDenyLoginPoint(str, obj); break; case T_AlterTypeStmt: _outAlterTypeStmt(str, obj); break; case T_AlterExtensionStmt: _outAlterExtensionStmt(str, obj); break; case T_AlterExtensionContentsStmt: _outAlterExtensionContentsStmt(str, obj); break; case T_TupleDescNode: _outTupleDescNode(str, obj); break; case T_AlterTSConfigurationStmt: _outAlterTSConfigurationStmt(str, obj); break; case T_AlterTSDictionaryStmt: _outAlterTSDictionaryStmt(str, obj); break; default: elog(ERROR, "could not serialize unrecognized node type: %d", (int) nodeTag(obj)); break; } } }
/* * Generate targetlist for a set-operation plan node * * colTypes: column datatypes for non-junk columns * flag: -1 if no flag column needed, 0 or 1 to create a const flag column * hack_constants: true to copy up constants (see comments in code) * input_tlist: targetlist of this node's input node * refnames_tlist: targetlist to take column names from */ static List * generate_setop_tlist(List *colTypes, int flag, bool hack_constants, List *input_tlist, List *refnames_tlist) { List *tlist = NIL; int resno = 1; List *i; Resdom *resdom; Node *expr; foreach(i, colTypes) { Oid colType = lfirsto(i); TargetEntry *inputtle = (TargetEntry *) lfirst(input_tlist); TargetEntry *reftle = (TargetEntry *) lfirst(refnames_tlist); int32 colTypmod; Assert(inputtle->resdom->resno == resno); Assert(reftle->resdom->resno == resno); Assert(!inputtle->resdom->resjunk); Assert(!reftle->resdom->resjunk); /* * Generate columns referencing input columns and having * appropriate data types and column names. Insert datatype * coercions where necessary. * * HACK: constants in the input's targetlist are copied up as-is * rather than being referenced as subquery outputs. This is * mainly to ensure that when we try to coerce them to the output * column's datatype, the right things happen for UNKNOWN * constants. But do this only at the first level of * subquery-scan plans; we don't want phony constants appearing in * the output tlists of upper-level nodes! */ if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const)) expr = (Node *) inputtle->expr; else expr = (Node *) makeVar(0, inputtle->resdom->resno, inputtle->resdom->restype, inputtle->resdom->restypmod, 0); if (inputtle->resdom->restype == colType) { /* no coercion needed, and believe the input typmod */ colTypmod = inputtle->resdom->restypmod; } else { expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */ expr, colType, "UNION/INTERSECT/EXCEPT"); colTypmod = -1; } resdom = makeResdom((AttrNumber) resno++, colType, colTypmod, pstrdup(reftle->resdom->resname), false); tlist = lappend(tlist, makeTargetEntry(resdom, (Expr *) expr)); input_tlist = lnext(input_tlist); refnames_tlist = lnext(refnames_tlist); }
/*! UDA transition function for the fmsketch aggregate. */ Datum __fmsketch_trans(PG_FUNCTION_ARGS) { bytea * transblob = (bytea *)PG_GETARG_BYTEA_P(0); fmtransval *transval; Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1); Oid funcOid; bool typIsVarlena; Datum retval; Datum inval; /* * This is Postgres boilerplate for UDFs that modify the data in their own context. * Such UDFs can only be correctly called in an agg context since regular scalar * UDFs are essentially stateless across invocations. */ if (!(fcinfo->context && (IsA(fcinfo->context, AggState) #ifdef NOTGP || IsA(fcinfo->context, WindowAggState) #endif ))) elog( ERROR, "UDF call to a function that only works for aggs (destructive pass by reference)"); /* get the provided element, being careful in case it's NULL */ if (!PG_ARGISNULL(1)) { if (!OidIsValid(element_type)) elog(ERROR, "could not determine data type of input"); inval = PG_GETARG_DATUM(1); /* * if this is the first call, initialize transval to hold a sortasort * on the first call, we should have the empty string (if the agg was declared properly!) */ if (VARSIZE(transblob) <= VARHDRSZ) { size_t blobsz = VARHDRSZ + sizeof(fmtransval) + SORTASORT_INITIAL_STORAGE; transblob = (bytea *)palloc0(blobsz); SET_VARSIZE(transblob, blobsz); transval = (fmtransval *)VARDATA(transblob); transval->typOid = element_type; /* figure out the outfunc for this type */ getTypeOutputInfo(element_type, &funcOid, &typIsVarlena); get_typlenbyval(element_type, &(transval->typLen), &(transval->typByVal)); transval->status = SMALL; sortasort_init((sortasort *)transval->storage, MINVALS, SORTASORT_INITIAL_STORAGE, transval->typLen, transval->typByVal); } else { // check_fmtransval(transblob); /* extract the existing transval from the transblob */ transval = (fmtransval *)VARDATA(transblob); // if (transval->typOid != element_type) { // elog(ERROR, "cannot aggregate on elements with different types"); // } } /* * if we've seen < MINVALS distinct values, place datum into the sortasort * XXXX Would be cleaner to try the sortasort insert and if it fails, then continue. */ if (transval->status == SMALL && ((sortasort *)(transval->storage))->num_vals < MINVALS) { int len = ExtractDatumLen(inval, transval->typLen, transval->typByVal, -1); retval = PointerGetDatum(fmsketch_sortasort_insert( transblob, inval, len)); PG_RETURN_DATUM(retval); } /* * if we've seen exactly MINVALS distinct values, create FM bitmaps * and load the contents of the sortasort into the FM sketch */ else if (transval->status == SMALL && ((sortasort *)(transval->storage))->num_vals == MINVALS) { int i; sortasort *s = (sortasort *)(transval->storage); bytea *newblob = fm_new(transval); transval = (fmtransval *)VARDATA(newblob); /* * "catch up" on the past as if we were doing FM from the beginning: * apply the FM sketching algorithm to each value previously stored in the sortasort */ for (i = 0; i < MINVALS; i++) __fmsketch_trans_c(newblob, PointerExtractDatum(sortasort_getval(s,i), s->typByVal)); /* * XXXX would like to pfree the old transblob, but the memory allocator doesn't like it * XXXX Meanwhile we know that this memory "leak" is of fixed size and will get * XXXX deallocated "soon" when the memory context is destroyed. */ /* drop through to insert the current datum in "BIG" mode */ transblob = newblob; } /* * if we're here we've seen >=MINVALS distinct values and are in BIG mode. * Just for sanity, let's check. */ if (transval->status != BIG) elog( ERROR, "FM sketch failed internal sanity check"); /* Apply FM algorithm to this datum */ retval = __fmsketch_trans_c(transblob, inval); PG_RETURN_DATUM(retval); } else PG_RETURN_NULL(); }
/* * Handle CTIDs of views. * CTID should be defined in the view and it must * correspond to the CTID of a base relation. */ static Datum currtid_for_view(Relation viewrel, ItemPointer tid) { TupleDesc att = RelationGetDescr(viewrel); RuleLock *rulelock; RewriteRule *rewrite; int i, natts = att->natts, tididx = -1; for (i = 0; i < natts; i++) { Form_pg_attribute attr = TupleDescAttr(att, i); if (strcmp(NameStr(attr->attname), "ctid") == 0) { if (attr->atttypid != TIDOID) elog(ERROR, "ctid isn't of type TID"); tididx = i; break; } } if (tididx < 0) elog(ERROR, "currtid cannot handle views with no CTID"); rulelock = viewrel->rd_rules; if (!rulelock) elog(ERROR, "the view has no rules"); for (i = 0; i < rulelock->numLocks; i++) { rewrite = rulelock->rules[i]; if (rewrite->event == CMD_SELECT) { Query *query; TargetEntry *tle; if (list_length(rewrite->actions) != 1) elog(ERROR, "only one select rule is allowed in views"); query = (Query *) linitial(rewrite->actions); tle = get_tle_by_resno(query->targetList, tididx + 1); if (tle && tle->expr && IsA(tle->expr, Var)) { Var *var = (Var *) tle->expr; RangeTblEntry *rte; if (!IS_SPECIAL_VARNO(var->varno) && var->varattno == SelfItemPointerAttributeNumber) { rte = rt_fetch(var->varno, query->rtable); if (rte) { heap_close(viewrel, AccessShareLock); return DirectFunctionCall2(currtid_byreloid, ObjectIdGetDatum(rte->relid), PointerGetDatum(tid)); } } } break; } } elog(ERROR, "currtid cannot handle this view"); return (Datum) 0; }
/* * Selectivity estimation for the subnet inclusion/overlap operators */ Datum networksel(PG_FUNCTION_ARGS) { PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0); Oid operator = PG_GETARG_OID(1); List *args = (List *) PG_GETARG_POINTER(2); int varRelid = PG_GETARG_INT32(3); VariableStatData vardata; Node *other; bool varonleft; Selectivity selec, mcv_selec, non_mcv_selec; Datum constvalue, *hist_values; int hist_nvalues; Form_pg_statistic stats; double sumcommon, nullfrac; FmgrInfo proc; /* * If expression is not (variable op something) or (something op * variable), then punt and return a default estimate. */ if (!get_restriction_variable(root, args, varRelid, &vardata, &other, &varonleft)) PG_RETURN_FLOAT8(DEFAULT_SEL(operator)); /* * Can't do anything useful if the something is not a constant, either. */ if (!IsA(other, Const)) { ReleaseVariableStats(vardata); PG_RETURN_FLOAT8(DEFAULT_SEL(operator)); } /* All of the operators handled here are strict. */ if (((Const *) other)->constisnull) { ReleaseVariableStats(vardata); PG_RETURN_FLOAT8(0.0); } constvalue = ((Const *) other)->constvalue; /* Otherwise, we need stats in order to produce a non-default estimate. */ if (!HeapTupleIsValid(vardata.statsTuple)) { ReleaseVariableStats(vardata); PG_RETURN_FLOAT8(DEFAULT_SEL(operator)); } stats = (Form_pg_statistic) GETSTRUCT(vardata.statsTuple); nullfrac = stats->stanullfrac; /* * If we have most-common-values info, add up the fractions of the MCV * entries that satisfy MCV OP CONST. These fractions contribute directly * to the result selectivity. Also add up the total fraction represented * by MCV entries. */ fmgr_info(get_opcode(operator), &proc); mcv_selec = mcv_selectivity(&vardata, &proc, constvalue, varonleft, &sumcommon); /* * If we have a histogram, use it to estimate the proportion of the * non-MCV population that satisfies the clause. If we don't, apply the * default selectivity to that population. */ if (get_attstatsslot(vardata.statsTuple, vardata.atttype, vardata.atttypmod, STATISTIC_KIND_HISTOGRAM, InvalidOid, NULL, &hist_values, &hist_nvalues, NULL, NULL)) { int opr_codenum = inet_opr_codenum(operator); /* Commute if needed, so we can consider histogram to be on the left */ if (!varonleft) opr_codenum = -opr_codenum; non_mcv_selec = inet_hist_value_sel(hist_values, hist_nvalues, constvalue, opr_codenum); free_attstatsslot(vardata.atttype, hist_values, hist_nvalues, NULL, 0); } else non_mcv_selec = DEFAULT_SEL(operator); /* Combine selectivities for MCV and non-MCV populations */ selec = mcv_selec + (1.0 - nullfrac - sumcommon) * non_mcv_selec; /* Result should be in range, but make sure... */ CLAMP_PROBABILITY(selec); ReleaseVariableStats(vardata); PG_RETURN_FLOAT8(selec); }
{ return static_cast<T*>(object); } } return nullptr; })"), PredefinedMethod::Inline(R"( static UClass* FindClass(const std::string& name) { return FindObject<UClass>(name); })"), PredefinedMethod::Inline(R"( template<typename T> static T* GetObjectCasted(std::size_t index) { return static_cast<T*>(GetGlobalObjects().GetByIndex(index)); })"), PredefinedMethod::Default("bool IsA(UClass* cmp) const", R"(bool UObject::IsA(UClass* cmp) const { for (auto super = Class; super; super = static_cast<UClass*>(super->SuperField)) { if (super == cmp) { return true; } } return false; })") }; predefinedMethods["Class Core.Class"] = { PredefinedMethod::Inline(R"( template<typename T> inline T* CreateDefaultObject()
/*---------- * predicate_implied_by_recurse * Does the predicate implication test for non-NULL restriction and * predicate clauses. * * The logic followed here is ("=>" means "implies"): * atom A => atom B iff: predicate_implied_by_simple_clause says so * atom A => AND-expr B iff: A => each of B's components * atom A => OR-expr B iff: A => any of B's components * AND-expr A => atom B iff: any of A's components => B * AND-expr A => AND-expr B iff: A => each of B's components * AND-expr A => OR-expr B iff: A => any of B's components, * *or* any of A's components => B * OR-expr A => atom B iff: each of A's components => B * OR-expr A => AND-expr B iff: A => each of B's components * OR-expr A => OR-expr B iff: each of A's components => any of B's * * An "atom" is anything other than an AND or OR node. Notice that we don't * have any special logic to handle NOT nodes; these should have been pushed * down or eliminated where feasible by prepqual.c. * * We can't recursively expand either side first, but have to interleave * the expansions per the above rules, to be sure we handle all of these * examples: * (x OR y) => (x OR y OR z) * (x AND y AND z) => (x AND y) * (x AND y) => ((x AND y) OR z) * ((x OR y) AND z) => (x OR y) * This is still not an exhaustive test, but it handles most normal cases * under the assumption that both inputs have been AND/OR flattened. * * We have to be prepared to handle RestrictInfo nodes in the restrictinfo * tree, though not in the predicate tree. *---------- */ static bool predicate_implied_by_recurse(Node *clause, Node *predicate) { PredIterInfoData clause_info; PredIterInfoData pred_info; PredClass pclass; bool result; /* skip through RestrictInfo */ Assert(clause != NULL); if (IsA(clause, RestrictInfo)) clause = (Node *) ((RestrictInfo *) clause)->clause; pclass = predicate_classify(predicate, &pred_info); switch (predicate_classify(clause, &clause_info)) { case CLASS_AND: switch (pclass) { case CLASS_AND: /* * AND-clause => AND-clause if A implies each of B's items */ result = true; iterate_begin(pitem, predicate, pred_info) { if (!predicate_implied_by_recurse(clause, pitem)) { result = false; break; } } iterate_end(pred_info); return result; case CLASS_OR: /* * AND-clause => OR-clause if A implies any of B's items * * Needed to handle (x AND y) => ((x AND y) OR z) */ result = false; iterate_begin(pitem, predicate, pred_info) { if (predicate_implied_by_recurse(clause, pitem)) { result = true; break; } } iterate_end(pred_info); if (result) return result; /* * Also check if any of A's items implies B * * Needed to handle ((x OR y) AND z) => (x OR y) */ iterate_begin(citem, clause, clause_info) { if (predicate_implied_by_recurse(citem, predicate)) { result = true; break; } } iterate_end(clause_info); return result; case CLASS_ATOM: /* * AND-clause => atom if any of A's items implies B */ result = false; iterate_begin(citem, clause, clause_info) { if (predicate_implied_by_recurse(citem, predicate)) { result = true; break; } } iterate_end(clause_info); return result; } break; case CLASS_OR: switch (pclass) { case CLASS_OR: /* * OR-clause => OR-clause if each of A's items implies any * of B's items. Messy but can't do it any more simply. */ result = true; iterate_begin(citem, clause, clause_info) { bool presult = false; iterate_begin(pitem, predicate, pred_info) { if (predicate_implied_by_recurse(citem, pitem)) { presult = true; break; } } iterate_end(pred_info); if (!presult) { result = false; /* doesn't imply any of B's */ break; } } iterate_end(clause_info); return result; case CLASS_AND: case CLASS_ATOM: /* * OR-clause => AND-clause if each of A's items implies B * * OR-clause => atom if each of A's items implies B */ result = true; iterate_begin(citem, clause, clause_info) { if (!predicate_implied_by_recurse(citem, predicate)) { result = false; break; } } iterate_end(clause_info); return result; } break; case CLASS_ATOM: switch (pclass) { case CLASS_AND: /* * atom => AND-clause if A implies each of B's items */ result = true; iterate_begin(pitem, predicate, pred_info) { if (!predicate_implied_by_recurse(clause, pitem)) { result = false; break; } } iterate_end(pred_info); return result; case CLASS_OR: /* * atom => OR-clause if A implies any of B's items */ result = false; iterate_begin(pitem, predicate, pred_info) { if (predicate_implied_by_recurse(clause, pitem)) { result = true; break; } } iterate_end(pred_info); return result; case CLASS_ATOM: /* * atom => atom is the base case */ return predicate_implied_by_simple_clause((Expr *) predicate, clause); } break; }
/* * rangesel -- restriction selectivity for range operators */ Datum rangesel(PG_FUNCTION_ARGS) { PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0); Oid operator___ = PG_GETARG_OID(1); List *args = (List *) PG_GETARG_POINTER(2); int varRelid = PG_GETARG_INT32(3); VariableStatData vardata; Node *other; bool varonleft; Selectivity selec; TypeCacheEntry *typcache = NULL; RangeType *constrange = NULL; /* * If expression is not (variable op something) or (something op * variable), then punt and return a default estimate. */ if (!get_restriction_variable(root, args, varRelid, &vardata, &other, &varonleft)) PG_RETURN_FLOAT8(default_range_selectivity(operator___)); /* * Can't do anything useful if the something is not a constant, either. */ if (!IsA(other, Const)) { ReleaseVariableStats(vardata); PG_RETURN_FLOAT8(default_range_selectivity(operator___)); } /* * All the range operators are strict, so we can cope with a NULL constant * right away. */ if (((Const *) other)->constisnull) { ReleaseVariableStats(vardata); PG_RETURN_FLOAT8(0.0); } /* * If var is on the right, commute the operator___, so that we can assume the * var is on the left in what follows. */ if (!varonleft) { /* we have other Op var, commute to make var Op other */ operator___ = get_commutator(operator___); if (!operator___) { /* Use default selectivity (should we raise an error instead?) */ ReleaseVariableStats(vardata); PG_RETURN_FLOAT8(default_range_selectivity(operator___)); } } /* * OK, there's a Var and a Const we're dealing with here. We need the * Const to be of same range type as the column, else we can't do anything * useful. (Such cases will likely fail at runtime, but here we'd rather * just return a default estimate.) * * If the operator___ is "range @> element", the constant should be of the * element type of the range column. Convert it to a range that includes * only that single point, so that we don't need special handling for that * in what follows. */ if (operator___ == OID_RANGE_CONTAINS_ELEM_OP) { typcache = range_get_typcache(fcinfo, vardata.vartype); if (((Const *) other)->consttype == typcache->rngelemtype->type_id) { RangeBound lower, upper; lower.inclusive = true; lower.val = ((Const *) other)->constvalue; lower.infinite = false; lower.lower = true; upper.inclusive = true; upper.val = ((Const *) other)->constvalue; upper.infinite = false; upper.lower = false; constrange = range_serialize(typcache, &lower, &upper, false); } } else if (operator___ == OID_RANGE_ELEM_CONTAINED_OP) { /* * Here, the Var is the elem, not the range. For now we just punt and * return the default estimate. In future we could disassemble the * range constant and apply scalarineqsel ... */ } else if (((Const *) other)->consttype == vardata.vartype) { /* Both sides are the same range type */ typcache = range_get_typcache(fcinfo, vardata.vartype); constrange = DatumGetRangeType(((Const *) other)->constvalue); } /* * If we got a valid constant on one side of the operator___, proceed to * estimate using statistics. Otherwise punt and return a default constant * estimate. Note that calc_rangesel need not handle * OID_RANGE_ELEM_CONTAINED_OP. */ if (constrange) selec = calc_rangesel(typcache, &vardata, constrange, operator___); else selec = default_range_selectivity(operator___); ReleaseVariableStats(vardata); CLAMP_PROBABILITY(selec); PG_RETURN_FLOAT8((float8) selec); }
Datum xpath_table(PG_FUNCTION_ARGS) { /* Function parameters */ char *pkeyfield = text_to_cstring(PG_GETARG_TEXT_PP(0)); char *xmlfield = text_to_cstring(PG_GETARG_TEXT_PP(1)); char *relname = text_to_cstring(PG_GETARG_TEXT_PP(2)); char *xpathset = text_to_cstring(PG_GETARG_TEXT_PP(3)); char *condition = text_to_cstring(PG_GETARG_TEXT_PP(4)); /* SPI (input tuple) support */ SPITupleTable *tuptable; HeapTuple spi_tuple; TupleDesc spi_tupdesc; /* Output tuple (tuplestore) support */ Tuplestorestate *tupstore = NULL; TupleDesc ret_tupdesc; HeapTuple ret_tuple; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; AttInMetadata *attinmeta; MemoryContext per_query_ctx; MemoryContext oldcontext; char **values; xmlChar **xpaths; char *pos; const char *pathsep = "|"; int numpaths; int ret; int proc; int i; int j; int rownr; /* For issuing multiple rows from one original * document */ bool had_values; /* To determine end of nodeset results */ StringInfoData query_buf; /* We only have a valid tuple description in table function mode */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (rsinfo->expectedDesc == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("xpath_table must be called as a table function"))); /* * We want to materialise because it means that we don't have to carry * libxml2 parser state between invocations of this function */ if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("xpath_table requires Materialize mode, but it is not " "allowed in this context"))); /* * The tuplestore must exist in a higher context than this function call * (per_query_ctx is used) */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* * Create the tuplestore - work_mem is the max in-memory size before a * file is created on disk to hold it. */ tupstore = tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, false, work_mem); MemoryContextSwitchTo(oldcontext); /* get the requested return tuple description */ ret_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); /* must have at least one output column (for the pkey) */ if (ret_tupdesc->natts < 1) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("xpath_table must have at least one output column"))); /* * At the moment we assume that the returned attributes make sense for the * XPath specififed (i.e. we trust the caller). It's not fatal if they get * it wrong - the input function for the column type will raise an error * if the path result can't be converted into the correct binary * representation. */ attinmeta = TupleDescGetAttInMetadata(ret_tupdesc); /* Set return mode and allocate value space. */ rsinfo->returnMode = SFRM_Materialize; rsinfo->setDesc = ret_tupdesc; values = (char **) palloc(ret_tupdesc->natts * sizeof(char *)); xpaths = (xmlChar **) palloc(ret_tupdesc->natts * sizeof(xmlChar *)); /* * Split XPaths. xpathset is a writable CString. * * Note that we stop splitting once we've done all needed for tupdesc */ numpaths = 0; pos = xpathset; while (numpaths < (ret_tupdesc->natts - 1)) { xpaths[numpaths++] = (xmlChar *) pos; pos = strstr(pos, pathsep); if (pos != NULL) { *pos = '\0'; pos++; } else break; } /* Now build query */ initStringInfo(&query_buf); /* Build initial sql statement */ appendStringInfo(&query_buf, "SELECT %s, %s FROM %s WHERE %s", pkeyfield, xmlfield, relname, condition); if ((ret = SPI_connect()) < 0) elog(ERROR, "xpath_table: SPI_connect returned %d", ret); if ((ret = SPI_exec(query_buf.data, 0)) != SPI_OK_SELECT) elog(ERROR, "xpath_table: SPI execution failed for query %s", query_buf.data); proc = SPI_processed; /* elog(DEBUG1,"xpath_table: SPI returned %d rows",proc); */ tuptable = SPI_tuptable; spi_tupdesc = tuptable->tupdesc; /* Switch out of SPI context */ MemoryContextSwitchTo(oldcontext); /* * Check that SPI returned correct result. If you put a comma into one of * the function parameters, this will catch it when the SPI query returns * e.g. 3 columns. */ if (spi_tupdesc->natts != 2) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("expression returning multiple columns is not valid in parameter list"), errdetail("Expected two columns in SPI result, got %d.", spi_tupdesc->natts))); } /* * Setup the parser. This should happen after we are done evaluating the * query, in case it calls functions that set up libxml differently. */ pgxml_parser_init(); /* For each row i.e. document returned from SPI */ for (i = 0; i < proc; i++) { char *pkey; char *xmldoc; xmlDocPtr doctree; xmlXPathContextPtr ctxt; xmlXPathObjectPtr res; xmlChar *resstr; xmlXPathCompExprPtr comppath; /* Extract the row data as C Strings */ spi_tuple = tuptable->vals[i]; pkey = SPI_getvalue(spi_tuple, spi_tupdesc, 1); xmldoc = SPI_getvalue(spi_tuple, spi_tupdesc, 2); /* * Clear the values array, so that not-well-formed documents return * NULL in all columns. Note that this also means that spare columns * will be NULL. */ for (j = 0; j < ret_tupdesc->natts; j++) values[j] = NULL; /* Insert primary key */ values[0] = pkey; /* Parse the document */ if (xmldoc) doctree = xmlParseMemory(xmldoc, strlen(xmldoc)); else /* treat NULL as not well-formed */ doctree = NULL; if (doctree == NULL) { /* not well-formed, so output all-NULL tuple */ ret_tuple = BuildTupleFromCStrings(attinmeta, values); tuplestore_puttuple(tupstore, ret_tuple); heap_freetuple(ret_tuple); } else { /* New loop here - we have to deal with nodeset results */ rownr = 0; do { /* Now evaluate the set of xpaths. */ had_values = false; for (j = 0; j < numpaths; j++) { ctxt = xmlXPathNewContext(doctree); ctxt->node = xmlDocGetRootElement(doctree); /* compile the path */ comppath = xmlXPathCompile(xpaths[j]); if (comppath == NULL) { xmlFreeDoc(doctree); xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, "XPath Syntax Error"); } /* Now evaluate the path expression. */ res = xmlXPathCompiledEval(comppath, ctxt); xmlXPathFreeCompExpr(comppath); if (res != NULL) { switch (res->type) { case XPATH_NODESET: /* We see if this nodeset has enough nodes */ if (res->nodesetval != NULL && rownr < res->nodesetval->nodeNr) { resstr = xmlXPathCastNodeToString(res->nodesetval->nodeTab[rownr]); had_values = true; } else resstr = NULL; break; case XPATH_STRING: resstr = xmlStrdup(res->stringval); break; default: elog(NOTICE, "unsupported XQuery result: %d", res->type); resstr = xmlStrdup((const xmlChar *) "<unsupported/>"); } /* * Insert this into the appropriate column in the * result tuple. */ values[j + 1] = (char *) resstr; } xmlXPathFreeContext(ctxt); } /* Now add the tuple to the output, if there is one. */ if (had_values) { ret_tuple = BuildTupleFromCStrings(attinmeta, values); tuplestore_puttuple(tupstore, ret_tuple); heap_freetuple(ret_tuple); } rownr++; } while (had_values); } xmlFreeDoc(doctree); if (pkey) pfree(pkey); if (xmldoc) pfree(xmldoc); } tuplestore_donestoring(tupstore); SPI_finish(); rsinfo->setResult = tupstore; /* * SFRM_Materialize mode expects us to return a NULL Datum. The actual * tuples are in our tuplestore and passed back through rsinfo->setResult. * rsinfo->setDesc is set to the tuple description that we actually used * to build our tuples with, so the caller can verify we did what it was * expecting. */ return (Datum) 0; }
/* * Parse a function call * * For historical reasons, Postgres tries to treat the notations tab.col * and col(tab) as equivalent: if a single-argument function call has an * argument of complex type and the (unqualified) function name matches * any attribute of the type, we take it as a column projection. Conversely * a function of a single complex-type argument can be written like a * column reference, allowing functions to act like computed columns. * * Hence, both cases come through here. The is_column parameter tells us * which syntactic construct is actually being dealt with, but this is * intended to be used only to deliver an appropriate error message, * not to affect the semantics. When is_column is true, we should have * a single argument (the putative table), unqualified function name * equal to the column name, and no aggregate or variadic decoration. * Also, when is_column is true, we return NULL on failure rather than * reporting a no-such-function error. * * The argument expressions (in fargs) must have been transformed already. * But the agg_order expressions, if any, have not been. */ Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, List *agg_order, bool agg_star, bool agg_distinct, bool func_variadic, WindowDef *over, bool is_column, int location) { Oid rettype; Oid funcid; ListCell *l; ListCell *nextl; Node *first_arg = NULL; int nargs; int nargsplusdefs; Oid actual_arg_types[FUNC_MAX_ARGS]; Oid *declared_arg_types; List *argnames; List *argdefaults; Node *retval; bool retset; int nvargs; FuncDetailCode fdresult; /* * Most of the rest of the parser just assumes that functions do not have * more than FUNC_MAX_ARGS parameters. We have to test here to protect * against array overruns, etc. Of course, this may not be a function, * but the test doesn't hurt. */ if (list_length(fargs) > 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), parser_errposition(pstate, location))); /* * Extract arg type info in preparation for function lookup. * * If any arguments are Param markers of type VOID, we discard them from * the parameter list. This is a hack to allow the JDBC driver to not * have to distinguish "input" and "output" parameter symbols while * parsing function-call constructs. We can't use foreach() because we * may modify the list ... */ nargs = 0; for (l = list_head(fargs); l != NULL; l = nextl) { Node *arg = lfirst(l); Oid argtype = exprType(arg); nextl = lnext(l); if (argtype == VOIDOID && IsA(arg, Param) &&!is_column) { fargs = list_delete_ptr(fargs, arg); continue; } actual_arg_types[nargs++] = argtype; } /* * Check for named arguments; if there are any, build a list of names. * * We allow mixed notation (some named and some not), but only with all * the named parameters after all the unnamed ones. So the name list * corresponds to the last N actual parameters and we don't need any extra * bookkeeping to match things up. */ argnames = NIL; foreach(l, fargs) { Node *arg = lfirst(l); if (IsA(arg, NamedArgExpr)) { NamedArgExpr *na = (NamedArgExpr *) arg; ListCell *lc; /* Reject duplicate arg names */ foreach(lc, argnames) { if (strcmp(na->name, (char *) lfirst(lc)) == 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("argument name \"%s\" used more than once", na->name), parser_errposition(pstate, na->location))); } argnames = lappend(argnames, na->name); } else { if (argnames != NIL)
/* ---------------------------------------------------------------- * ProcedureCreate * * Note: allParameterTypes, parameterModes, parameterNames, and proconfig * are either arrays of the proper types or NULL. We declare them Datum, * not "ArrayType *", to avoid importing array.h into pg_proc_fn.h. * ---------------------------------------------------------------- */ Oid ProcedureCreate(const char *procedureName, Oid procNamespace, bool replace, bool returnsSet, Oid returnType, Oid languageObjectId, Oid languageValidator, const char *prosrc, const char *probin, bool isAgg, bool isWindowFunc, bool security_definer, bool isStrict, char volatility, oidvector *parameterTypes, Datum allParameterTypes, Datum parameterModes, Datum parameterNames, List *parameterDefaults, Datum proconfig, float4 procost, float4 prorows) { Oid retval; int parameterCount; int allParamCount; Oid *allParams; bool genericInParam = false; bool genericOutParam = false; bool internalInParam = false; bool internalOutParam = false; Oid variadicType = InvalidOid; Oid proowner = GetUserId(); Relation rel; HeapTuple tup; HeapTuple oldtup; bool nulls[Natts_pg_proc]; Datum values[Natts_pg_proc]; bool replaces[Natts_pg_proc]; Oid relid; NameData procname; TupleDesc tupDesc; bool is_update; ObjectAddress myself, referenced; int i; /* * sanity checks */ Assert(PointerIsValid(prosrc)); parameterCount = parameterTypes->dim1; if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg_plural("functions cannot have more than %d argument", "functions cannot have more than %d arguments", FUNC_MAX_ARGS, FUNC_MAX_ARGS))); /* note: the above is correct, we do NOT count output arguments */ if (allParameterTypes != PointerGetDatum(NULL)) { /* * We expect the array to be a 1-D OID array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of OID values. */ ArrayType *allParamArray = (ArrayType *) DatumGetPointer(allParameterTypes); allParamCount = ARR_DIMS(allParamArray)[0]; if (ARR_NDIM(allParamArray) != 1 || allParamCount <= 0 || ARR_HASNULL(allParamArray) || ARR_ELEMTYPE(allParamArray) != OIDOID) elog(ERROR, "allParameterTypes is not a 1-D Oid array"); allParams = (Oid *) ARR_DATA_PTR(allParamArray); Assert(allParamCount >= parameterCount); /* we assume caller got the contents right */ } else { allParamCount = parameterCount; allParams = parameterTypes->values; } /* * Do not allow polymorphic return type unless at least one input argument * is polymorphic. Also, do not allow return type INTERNAL unless at * least one input argument is INTERNAL. */ for (i = 0; i < parameterCount; i++) { switch (parameterTypes->values[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericInParam = true; break; case INTERNALOID: internalInParam = true; break; } } if (allParameterTypes != PointerGetDatum(NULL)) { for (i = 0; i < allParamCount; i++) { /* * We don't bother to distinguish input and output params here, so * if there is, say, just an input INTERNAL param then we will * still set internalOutParam. This is OK since we don't really * care. */ switch (allParams[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericOutParam = true; break; case INTERNALOID: internalOutParam = true; break; } } } if ((IsPolymorphicType(returnType) || genericOutParam) && !genericInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning a polymorphic type must have at least one polymorphic argument."))); if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); /* * don't allow functions of complex types that have the same name as * existing attributes of the type */ if (parameterCount == 1 && OidIsValid(parameterTypes->values[0]) && (relid = typeidTypeRelid(parameterTypes->values[0])) != InvalidOid && get_attnum(relid, procedureName) != InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("\"%s\" is already an attribute of type %s", procedureName, format_type_be(parameterTypes->values[0])))); if (parameterModes != PointerGetDatum(NULL)) { /* * We expect the array to be a 1-D CHAR array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of char values. */ ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes); char *modes; if (ARR_NDIM(modesArray) != 1 || ARR_DIMS(modesArray)[0] != allParamCount || ARR_HASNULL(modesArray) || ARR_ELEMTYPE(modesArray) != CHAROID) elog(ERROR, "parameterModes is not a 1-D char array"); modes = (char *) ARR_DATA_PTR(modesArray); /* * Only the last input parameter can be variadic; if it is, save its * element type. Errors here are just elog since caller should have * checked this already. */ for (i = 0; i < allParamCount; i++) { switch (modes[i]) { case PROARGMODE_IN: case PROARGMODE_INOUT: if (OidIsValid(variadicType)) elog(ERROR, "variadic parameter must be last"); break; case PROARGMODE_OUT: case PROARGMODE_TABLE: /* okay */ break; case PROARGMODE_VARIADIC: if (OidIsValid(variadicType)) elog(ERROR, "variadic parameter must be last"); switch (allParams[i]) { case ANYOID: variadicType = ANYOID; break; case ANYARRAYOID: variadicType = ANYELEMENTOID; break; default: variadicType = get_element_type(allParams[i]); if (!OidIsValid(variadicType)) elog(ERROR, "variadic parameter is not an array"); break; } break; default: elog(ERROR, "invalid parameter mode '%c'", modes[i]); break; } } } /* * All seems OK; prepare the data to be inserted into pg_proc. */ for (i = 0; i < Natts_pg_proc; ++i) { nulls[i] = false; values[i] = (Datum) 0; replaces[i] = true; } namestrcpy(&procname, procedureName); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId); values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost); values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows); values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType); values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg); values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet); values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility); values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount); values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults)); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes); if (allParameterTypes != PointerGetDatum(NULL)) values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes; else nulls[Anum_pg_proc_proallargtypes - 1] = true; if (parameterModes != PointerGetDatum(NULL)) values[Anum_pg_proc_proargmodes - 1] = parameterModes; else nulls[Anum_pg_proc_proargmodes - 1] = true; if (parameterNames != PointerGetDatum(NULL)) values[Anum_pg_proc_proargnames - 1] = parameterNames; else nulls[Anum_pg_proc_proargnames - 1] = true; if (parameterDefaults != NIL) values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(parameterDefaults)); else nulls[Anum_pg_proc_proargdefaults - 1] = true; values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc); if (probin) values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin); else nulls[Anum_pg_proc_probin - 1] = true; if (proconfig != PointerGetDatum(NULL)) values[Anum_pg_proc_proconfig - 1] = proconfig; else nulls[Anum_pg_proc_proconfig - 1] = true; /* start out with empty permissions */ nulls[Anum_pg_proc_proacl - 1] = true; rel = heap_open(ProcedureRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); /* Check for pre-existing definition */ oldtup = SearchSysCache(PROCNAMEARGSNSP, PointerGetDatum(procedureName), PointerGetDatum(parameterTypes), ObjectIdGetDatum(procNamespace), 0); if (HeapTupleIsValid(oldtup)) { /* There is one; okay to replace it? */ Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup); if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("function \"%s\" already exists with same argument types", procedureName))); if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), proowner)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, procedureName); /* * Not okay to change the return type of the existing proc, since * existing rules, views, etc may depend on the return type. */ if (returnType != oldproc->prorettype || returnsSet != oldproc->proretset) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errhint("Use DROP FUNCTION first."))); /* * If it returns RECORD, check for possible change of record type * implied by OUT parameters */ if (returnType == RECORDOID) { TupleDesc olddesc; TupleDesc newdesc; olddesc = build_function_result_tupdesc_t(oldtup); newdesc = build_function_result_tupdesc_d(allParameterTypes, parameterModes, parameterNames); if (olddesc == NULL && newdesc == NULL) /* ok, both are runtime-defined RECORDs */ ; else if (olddesc == NULL || newdesc == NULL || !equalTupleDescs(olddesc, newdesc)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errdetail("Row type defined by OUT parameters is different."), errhint("Use DROP FUNCTION first."))); } /* * If there are existing defaults, check compatibility: redefinition * must not remove any defaults nor change their types. (Removing a * default might cause a function to fail to satisfy an existing call. * Changing type would only be possible if the associated parameter is * polymorphic, and in such cases a change of default type might alter * the resolved output type of existing calls.) */ if (oldproc->pronargdefaults != 0) { Datum proargdefaults; bool isnull; List *oldDefaults; ListCell *oldlc; ListCell *newlc; if (list_length(parameterDefaults) < oldproc->pronargdefaults) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot remove parameter defaults from existing function"), errhint("Use DROP FUNCTION first."))); proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargdefaults, &isnull); Assert(!isnull); oldDefaults = (List *) stringToNode(TextDatumGetCString(proargdefaults)); Assert(IsA(oldDefaults, List)); Assert(list_length(oldDefaults) == oldproc->pronargdefaults); /* new list can have more defaults than old, advance over 'em */ newlc = list_head(parameterDefaults); for (i = list_length(parameterDefaults) - oldproc->pronargdefaults; i > 0; i--) newlc = lnext(newlc); foreach(oldlc, oldDefaults) { Node *oldDef = (Node *) lfirst(oldlc); Node *newDef = (Node *) lfirst(newlc); if (exprType(oldDef) != exprType(newDef)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change data type of existing parameter default value"), errhint("Use DROP FUNCTION first."))); newlc = lnext(newlc); } }
/* * init_MultiFuncCall * Create an empty FuncCallContext data structure * and do some other basic Multi-function call setup * and error checking */ FuncCallContext * init_MultiFuncCall(PG_FUNCTION_ARGS) { FuncCallContext *retval; /* * Bail if we're called in the wrong context */ if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (fcinfo->flinfo->fn_extra == NULL) { /* * First call */ ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; MemoryContext multi_call_ctx; /* * Create a suitably long-lived context to hold cross-call data */ multi_call_ctx = AllocSetContextCreate(fcinfo->flinfo->fn_mcxt, "SRF multi-call context", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE); /* * Allocate suitably long-lived space and zero it */ retval = (FuncCallContext *) MemoryContextAllocZero(multi_call_ctx, sizeof(FuncCallContext)); /* * initialize the elements */ retval->call_cntr = 0; retval->max_calls = 0; retval->slot = NULL; retval->user_fctx = NULL; retval->attinmeta = NULL; retval->tuple_desc = NULL; retval->multi_call_memory_ctx = multi_call_ctx; /* * save the pointer for cross-call use */ fcinfo->flinfo->fn_extra = retval; /* * Ensure we will get shut down cleanly if the exprcontext is not run * to completion. */ RegisterExprContextCallback(rsi->econtext, shutdown_MultiFuncCall, PointerGetDatum(fcinfo->flinfo)); } else { /* second and subsequent calls */ elog(ERROR, "init_MultiFuncCall cannot be called more than once"); /* never reached, but keep compiler happy */ retval = NULL; } return retval; }
/* * ExecIndexBuildScanKeys * Build the index scan keys from the index qualification expressions * * The index quals are passed to the index AM in the form of a ScanKey array. * This routine sets up the ScanKeys, fills in all constant fields of the * ScanKeys, and prepares information about the keys that have non-constant * comparison values. We divide index qual expressions into five types: * * 1. Simple operator with constant comparison value ("indexkey op constant"). * For these, we just fill in a ScanKey containing the constant value. * * 2. Simple operator with non-constant value ("indexkey op expression"). * For these, we create a ScanKey with everything filled in except the * expression value, and set up an IndexRuntimeKeyInfo struct to drive * evaluation of the expression at the right times. * * 3. RowCompareExpr ("(indexkey, indexkey, ...) op (expr, expr, ...)"). * For these, we create a header ScanKey plus a subsidiary ScanKey array, * as specified in access/skey.h. The elements of the row comparison * can have either constant or non-constant comparison values. * * 4. ScalarArrayOpExpr ("indexkey op ANY (array-expression)"). For these, * we create a ScanKey with everything filled in except the comparison value, * and set up an IndexArrayKeyInfo struct to drive processing of the qual. * (Note that we treat all array-expressions as requiring runtime evaluation, * even if they happen to be constants.) * * 5. NullTest ("indexkey IS NULL"). We just fill in the ScanKey properly. * * Input params are: * * planstate: executor state node we are working for * index: the index we are building scan keys for * scanrelid: varno of the index's relation within current query * quals: indexquals expressions * * Output params are: * * *scanKeys: receives ptr to array of ScanKeys * *numScanKeys: receives number of scankeys * *runtimeKeys: receives ptr to array of IndexRuntimeKeyInfos, or NULL if none * *numRuntimeKeys: receives number of runtime keys * *arrayKeys: receives ptr to array of IndexArrayKeyInfos, or NULL if none * *numArrayKeys: receives number of array keys * * Caller may pass NULL for arrayKeys and numArrayKeys to indicate that * ScalarArrayOpExpr quals are not supported. */ void ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, List *quals, ScanKey *scanKeys, int *numScanKeys, IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys, IndexArrayKeyInfo **arrayKeys, int *numArrayKeys) { ListCell *qual_cell; ScanKey scan_keys; IndexRuntimeKeyInfo *runtime_keys; IndexArrayKeyInfo *array_keys; int n_scan_keys; int extra_scan_keys; int n_runtime_keys; int n_array_keys; int j; /* * If there are any RowCompareExpr quals, we need extra ScanKey entries * for them, and possibly extra runtime-key entries. Count up what's * needed. (The subsidiary ScanKey arrays for the RowCompareExprs could * be allocated as separate chunks, but we have to count anyway to make * runtime_keys large enough, so might as well just do one palloc.) */ n_scan_keys = list_length(quals); extra_scan_keys = 0; foreach(qual_cell, quals) { if (IsA(lfirst(qual_cell), RowCompareExpr)) extra_scan_keys += list_length(((RowCompareExpr *) lfirst(qual_cell))->opnos); } scan_keys = (ScanKey) palloc((n_scan_keys + extra_scan_keys) * sizeof(ScanKeyData)); /* Allocate these arrays as large as they could possibly need to be */ runtime_keys = (IndexRuntimeKeyInfo *) palloc((n_scan_keys + extra_scan_keys) * sizeof(IndexRuntimeKeyInfo)); array_keys = (IndexArrayKeyInfo *) palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo)); n_runtime_keys = 0; n_array_keys = 0; /* * Below here, extra_scan_keys is index of first cell to use for next * RowCompareExpr */ extra_scan_keys = n_scan_keys; /* * for each opclause in the given qual, convert the opclause into a single * scan key */ j = 0; foreach(qual_cell, quals) { Expr *clause = (Expr *) lfirst(qual_cell); ScanKey this_scan_key = &scan_keys[j++]; Oid opno; /* operator's OID */ RegProcedure opfuncid; /* operator proc id used in scan */ Oid opfamily; /* opfamily of index column */ int op_strategy; /* operator's strategy number */ Oid op_lefttype; /* operator's declared input types */ Oid op_righttype; Expr *leftop; /* expr on lhs of operator */ Expr *rightop; /* expr on rhs ... */ AttrNumber varattno; /* att number used in scan */ if (IsA(clause, OpExpr)) { /* indexkey op const or indexkey op expression */ int flags = 0; Datum scanvalue; opno = ((OpExpr *) clause)->opno; opfuncid = ((OpExpr *) clause)->opfuncid; /* * leftop should be the index key Var, possibly relabeled */ leftop = (Expr *) get_leftop(clause); if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == scanrelid)) elog(ERROR, "indexqual doesn't have key on left side"); varattno = ((Var *) leftop)->varattno; if (varattno < 1 || varattno > index->rd_index->indnatts) elog(ERROR, "bogus index qualification"); /* * We have to look up the operator's strategy number. This * provides a cross-check that the operator does match the index. */ opfamily = index->rd_opfamily[varattno - 1]; get_op_opfamily_properties(opno, opfamily, &op_strategy, &op_lefttype, &op_righttype); /* * rightop is the constant or variable comparison value */ rightop = (Expr *) get_rightop(clause); if (rightop && IsA(rightop, RelabelType)) rightop = ((RelabelType *) rightop)->arg; Assert(rightop != NULL); if (IsA(rightop, Const)) { /* OK, simple constant comparison value */ scanvalue = ((Const *) rightop)->constvalue; if (((Const *) rightop)->constisnull) flags |= SK_ISNULL; } else { /* Need to treat this one as a runtime key */ runtime_keys[n_runtime_keys].scan_key = this_scan_key; runtime_keys[n_runtime_keys].key_expr = ExecInitExpr(rightop, planstate); n_runtime_keys++; scanvalue = (Datum) 0; } /* * initialize the scan key's fields appropriately */ ScanKeyEntryInitialize(this_scan_key, flags, varattno, /* attribute number to scan */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ } else if (IsA(clause, RowCompareExpr)) { /* (indexkey, indexkey, ...) op (expression, expression, ...) */ RowCompareExpr *rc = (RowCompareExpr *) clause; ListCell *largs_cell = list_head(rc->largs); ListCell *rargs_cell = list_head(rc->rargs); ListCell *opnos_cell = list_head(rc->opnos); ScanKey first_sub_key = &scan_keys[extra_scan_keys]; /* Scan RowCompare columns and generate subsidiary ScanKey items */ while (opnos_cell != NULL) { ScanKey this_sub_key = &scan_keys[extra_scan_keys]; int flags = SK_ROW_MEMBER; Datum scanvalue; /* * leftop should be the index key Var, possibly relabeled */ leftop = (Expr *) lfirst(largs_cell); largs_cell = lnext(largs_cell); if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == scanrelid)) elog(ERROR, "indexqual doesn't have key on left side"); varattno = ((Var *) leftop)->varattno; /* * rightop is the constant or variable comparison value */ rightop = (Expr *) lfirst(rargs_cell); rargs_cell = lnext(rargs_cell); if (rightop && IsA(rightop, RelabelType)) rightop = ((RelabelType *) rightop)->arg; Assert(rightop != NULL); if (IsA(rightop, Const)) { /* OK, simple constant comparison value */ scanvalue = ((Const *) rightop)->constvalue; if (((Const *) rightop)->constisnull) flags |= SK_ISNULL; } else { /* Need to treat this one as a runtime key */ runtime_keys[n_runtime_keys].scan_key = this_sub_key; runtime_keys[n_runtime_keys].key_expr = ExecInitExpr(rightop, planstate); n_runtime_keys++; scanvalue = (Datum) 0; } /* * We have to look up the operator's associated btree support * function */ opno = lfirst_oid(opnos_cell); opnos_cell = lnext(opnos_cell); if (index->rd_rel->relam != BTREE_AM_OID || varattno < 1 || varattno > index->rd_index->indnatts) elog(ERROR, "bogus RowCompare index qualification"); opfamily = index->rd_opfamily[varattno - 1]; get_op_opfamily_properties(opno, opfamily, &op_strategy, &op_lefttype, &op_righttype); if (op_strategy != rc->rctype) elog(ERROR, "RowCompare index qualification contains wrong operator"); opfuncid = get_opfamily_proc(opfamily, op_lefttype, op_righttype, BTORDER_PROC); /* * initialize the subsidiary scan key's fields appropriately */ ScanKeyEntryInitialize(this_sub_key, flags, varattno, /* attribute number */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ extra_scan_keys++; } /* Mark the last subsidiary scankey correctly */ scan_keys[extra_scan_keys - 1].sk_flags |= SK_ROW_END; /* * We don't use ScanKeyEntryInitialize for the header because it * isn't going to contain a valid sk_func pointer. */ MemSet(this_scan_key, 0, sizeof(ScanKeyData)); this_scan_key->sk_flags = SK_ROW_HEADER; this_scan_key->sk_attno = first_sub_key->sk_attno; this_scan_key->sk_strategy = rc->rctype; /* sk_subtype, sk_func not used in a header */ this_scan_key->sk_argument = PointerGetDatum(first_sub_key); } else if (IsA(clause, ScalarArrayOpExpr)) { /* indexkey op ANY (array-expression) */ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; Assert(saop->useOr); opno = saop->opno; opfuncid = saop->opfuncid; /* * leftop should be the index key Var, possibly relabeled */ leftop = (Expr *) linitial(saop->args); if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == scanrelid)) elog(ERROR, "indexqual doesn't have key on left side"); varattno = ((Var *) leftop)->varattno; if (varattno < 1 || varattno > index->rd_index->indnatts) elog(ERROR, "bogus index qualification"); /* * We have to look up the operator's strategy number. This * provides a cross-check that the operator does match the index. */ opfamily = index->rd_opfamily[varattno - 1]; get_op_opfamily_properties(opno, opfamily, &op_strategy, &op_lefttype, &op_righttype); /* * rightop is the constant or variable array value */ rightop = (Expr *) lsecond(saop->args); if (rightop && IsA(rightop, RelabelType)) rightop = ((RelabelType *) rightop)->arg; Assert(rightop != NULL); array_keys[n_array_keys].scan_key = this_scan_key; array_keys[n_array_keys].array_expr = ExecInitExpr(rightop, planstate); /* the remaining fields were zeroed by palloc0 */ n_array_keys++; /* * initialize the scan key's fields appropriately */ ScanKeyEntryInitialize(this_scan_key, 0, /* flags */ varattno, /* attribute number to scan */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ opfuncid, /* reg proc to use */ (Datum) 0); /* constant */ } else if (IsA(clause, NullTest)) { /* indexkey IS NULL */ Assert(((NullTest *) clause)->nulltesttype == IS_NULL); /* * argument should be the index key Var, possibly relabeled */ leftop = ((NullTest *) clause)->arg; if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == scanrelid)) elog(ERROR, "NullTest indexqual has wrong key"); varattno = ((Var *) leftop)->varattno; /* * initialize the scan key's fields appropriately */ ScanKeyEntryInitialize(this_scan_key, SK_ISNULL | SK_SEARCHNULL, varattno, /* attribute number to scan */ InvalidStrategy, /* no strategy */ InvalidOid, /* no strategy subtype */ InvalidOid, /* no reg proc for this */ (Datum) 0); /* constant */ } else elog(ERROR, "unsupported indexqual type: %d", (int) nodeTag(clause)); }
Datum plpgsql_inline_handler(PG_FUNCTION_ARGS) { InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0)); PLpgSQL_function *func; FunctionCallInfoData fake_fcinfo; FmgrInfo flinfo; EState *simple_eval_estate; Datum retval; int rc; Assert(IsA(codeblock, InlineCodeBlock)); /* * Connect to SPI manager */ if ((rc = SPI_connect()) != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc)); /* Compile the anonymous code block */ func = plpgsql_compile_inline(codeblock->source_text); /* Mark the function as busy, just pro forma */ func->use_count++; /* * Set up a fake fcinfo with just enough info to satisfy * plpgsql_exec_function(). In particular note that this sets things up * with no arguments passed. */ MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo)); MemSet(&flinfo, 0, sizeof(flinfo)); fake_fcinfo.flinfo = &flinfo; flinfo.fn_oid = InvalidOid; flinfo.fn_mcxt = CurrentMemoryContext; /* Create a private EState for simple-expression execution */ simple_eval_estate = CreateExecutorState(); /* And run the function */ PG_TRY(); { retval = plpgsql_exec_function(func, &fake_fcinfo, simple_eval_estate); } PG_CATCH(); { /* * We need to clean up what would otherwise be long-lived resources * accumulated by the failed DO block, principally cached plans for * statements (which can be flushed with plpgsql_free_function_memory) * and execution trees for simple expressions, which are in the * private EState. * * Before releasing the private EState, we must clean up any * simple_econtext_stack entries pointing into it, which we can do by * invoking the subxact callback. (It will be called again later if * some outer control level does a subtransaction abort, but no harm * is done.) We cheat a bit knowing that plpgsql_subxact_cb does not * pay attention to its parentSubid argument. */ plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB, GetCurrentSubTransactionId(), 0, NULL); /* Clean up the private EState */ FreeExecutorState(simple_eval_estate); /* Function should now have no remaining use-counts ... */ func->use_count--; Assert(func->use_count == 0); /* ... so we can free subsidiary storage */ plpgsql_free_function_memory(func); /* And propagate the error */ PG_RE_THROW(); } PG_END_TRY(); /* Clean up the private EState */ FreeExecutorState(simple_eval_estate); /* Function should now have no remaining use-counts ... */ func->use_count--; Assert(func->use_count == 0); /* ... so we can free subsidiary storage */ plpgsql_free_function_memory(func); /* * Disconnect from SPI manager */ if ((rc = SPI_finish()) != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc)); return retval; }
/* * recurse_set_operations * Recursively handle one step in a tree of set operations * * colTypes: list of type OIDs of expected output columns * junkOK: if true, child resjunk columns may be left in the result * flag: if >= 0, add a resjunk output column indicating value of flag * refnames_tlist: targetlist to take column names from */ static Plan * recurse_set_operations(Node *setOp, Query *parse, List *colTypes, bool junkOK, int flag, List *refnames_tlist) { if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; RangeTblEntry *rte = rt_fetch(rtr->rtindex, parse->rtable); Query *subquery = rte->subquery; Plan *subplan, *plan; Assert(subquery != NULL); /* * Generate plan for primitive subquery */ subplan = subquery_planner(subquery, 0.0 /* default case */ ); /* * Add a SubqueryScan with the caller-requested targetlist */ plan = (Plan *) make_subqueryscan(generate_setop_tlist(colTypes, flag, true, subplan->targetlist, refnames_tlist), NIL, rtr->rtindex, subplan); return plan; } else if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; Plan *plan; /* UNIONs are much different from INTERSECT/EXCEPT */ if (op->op == SETOP_UNION) plan = generate_union_plan(op, parse, refnames_tlist); else plan = generate_nonunion_plan(op, parse, refnames_tlist); /* * If necessary, add a Result node to project the caller-requested * output columns. * * XXX you don't really want to know about this: setrefs.c will apply * replace_vars_with_subplan_refs() to the Result node's tlist. * This would fail if the Vars generated by generate_setop_tlist() * were not exactly equal() to the corresponding tlist entries of * the subplan. However, since the subplan was generated by * generate_union_plan() or generate_nonunion_plan(), and hence * its tlist was generated by generate_append_tlist(), this will * work. */ if (flag >= 0 || !tlist_same_datatypes(plan->targetlist, colTypes, junkOK)) { plan = (Plan *) make_result(generate_setop_tlist(colTypes, flag, false, plan->targetlist, refnames_tlist), NULL, plan); } return plan; } else { elog(ERROR, "unrecognized node type: %d", (int) nodeTag(setOp)); return NULL; /* keep compiler quiet */ } }
/* * preprocess_minmax_aggregates - preprocess MIN/MAX aggregates * * Check to see whether the query contains MIN/MAX aggregate functions that * might be optimizable via indexscans. If it does, and all the aggregates * are potentially optimizable, then set up root->minmax_aggs with a list of * these aggregates. * * Note: we are passed the preprocessed targetlist separately, because it's * not necessarily equal to root->parse->targetList. */ void preprocess_minmax_aggregates(PlannerInfo *root, List *tlist) { Query *parse = root->parse; FromExpr *jtnode; RangeTblRef *rtr; RangeTblEntry *rte; List *aggs_list; ListCell *lc; /* minmax_aggs list should be empty at this point */ Assert(root->minmax_aggs == NIL); /* Nothing to do if query has no aggregates */ if (!parse->hasAggs) return; Assert(!parse->setOperations); /* shouldn't get here if a setop */ Assert(parse->rowMarks == NIL); /* nor if FOR UPDATE */ /* * Reject unoptimizable cases. * * We don't handle GROUP BY or windowing, because our current * implementations of grouping require looking at all the rows anyway, and * so there's not much point in optimizing MIN/MAX. (Note: relaxing this * would likely require some restructuring in grouping_planner(), since it * performs assorted processing related to these features between calling * preprocess_minmax_aggregates and optimize_minmax_aggregates.) */ if (parse->groupClause || parse->hasWindowFuncs) return; /* * We also restrict the query to reference exactly one table, since join * conditions can't be handled reasonably. (We could perhaps handle a * query containing cartesian-product joins, but it hardly seems worth the * trouble.) However, the single table could be buried in several levels * of FromExpr due to subqueries. Note the "single" table could be an * inheritance parent, too, including the case of a UNION ALL subquery * that's been flattened to an appendrel. */ jtnode = parse->jointree; while (IsA(jtnode, FromExpr)) { if (list_length(jtnode->fromlist) != 1) return; jtnode = linitial(jtnode->fromlist); } if (!IsA(jtnode, RangeTblRef)) return; rtr = (RangeTblRef *) jtnode; rte = planner_rt_fetch(rtr->rtindex, root); if (rte->rtekind == RTE_RELATION) /* ordinary relation, ok */ ; else if (rte->rtekind == RTE_SUBQUERY && rte->inh) /* flattened UNION ALL subquery, ok */ ; else return; /* * Scan the tlist and HAVING qual to find all the aggregates and verify * all are MIN/MAX aggregates. Stop as soon as we find one that isn't. */ aggs_list = NIL; if (find_minmax_aggs_walker((Node *) tlist, &aggs_list)) return; if (find_minmax_aggs_walker(parse->havingQual, &aggs_list)) return; /* * OK, there is at least the possibility of performing the optimization. * Build an access path for each aggregate. (We must do this now because * we need to call query_planner with a pristine copy of the current query * tree; it'll be too late when optimize_minmax_aggregates gets called.) * If any of the aggregates prove to be non-indexable, give up; there is * no point in optimizing just some of them. */ foreach(lc, aggs_list) { MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc); Oid eqop; bool reverse; /* * We'll need the equality operator that goes with the aggregate's * ordering operator. */ eqop = get_equality_op_for_ordering_op(mminfo->aggsortop, &reverse); if (!OidIsValid(eqop)) /* shouldn't happen */ elog(ERROR, "could not find equality operator for ordering operator %u", mminfo->aggsortop); /* * We can use either an ordering that gives NULLS FIRST or one that * gives NULLS LAST; furthermore there's unlikely to be much * performance difference between them, so it doesn't seem worth * costing out both ways if we get a hit on the first one. NULLS * FIRST is more likely to be available if the operator is a * reverse-sort operator, so try that first if reverse. */ if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, reverse)) continue; if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, !reverse)) continue; /* No indexable path for this aggregate, so fail */ return; }
CustomScanState * ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) { CustomScanState *css; Relation scan_rel = NULL; Index scanrelid = cscan->scan.scanrelid; Index tlistvarno; /* * Allocate the CustomScanState object. We let the custom scan provider * do the palloc, in case it wants to make a larger object that embeds * CustomScanState as the first field. It must set the node tag and the * methods field correctly at this time. Other standard fields should be * set to zero. */ css = (CustomScanState *) cscan->methods->CreateCustomScanState(cscan); Assert(IsA(css, CustomScanState)); /* ensure flags is filled correctly */ css->flags = cscan->flags; /* fill up fields of ScanState */ css->ss.ps.plan = &cscan->scan.plan; css->ss.ps.state = estate; /* create expression context for node */ ExecAssignExprContext(estate, &css->ss.ps); #ifdef PG95 css->ss.ps.ps_TupFromTlist = false; #endif /* initialize child expressions */ css->ss.ps.targetlist = (List *) ExecInitExpr((Expr *) cscan->scan.plan.targetlist, (PlanState *) css); css->ss.ps.qual = (List *) ExecInitExpr((Expr *) cscan->scan.plan.qual, (PlanState *) css); /* tuple table initialization */ #ifndef PG95 if (((Scan*)css->ss.ps.plan)->scanrelid) { // For table scans ExecInitScanTupleSlot(estate, &css->ss); } else { // For tuple-table-slot scans (typically over Motion nodes) css->ss.ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable); } #else ExecInitScanTupleSlot(estate, &css->ss); #endif ExecInitResultTupleSlot(estate, &css->ss.ps); /* * open the base relation, if any, and acquire an appropriate lock on it */ if (scanrelid > 0) { scan_rel = ExecOpenScanRelation(estate , scanrelid #ifdef PG95 , eflags #endif ); css->ss.ss_currentRelation = scan_rel; } /* * Determine the scan tuple type. If the custom scan provider provided a * targetlist describing the scan tuples, use that; else use base * relation's rowtype. */ if (cscan->custom_scan_tlist != NIL || scan_rel == NULL) { // index-only scan? GP doesn't define INDEX_VAR TupleDesc scan_tupdesc; scan_tupdesc = ExecTypeFromTL(cscan->custom_scan_tlist, false); ExecAssignScanType(&css->ss, scan_tupdesc); #ifdef PG95 /* Node's targetlist will contain Vars with varno = INDEX_VAR */ tlistvarno = INDEX_VAR; #endif } else { ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel)); /* Node's targetlist will contain Vars with varno = scanrelid */ tlistvarno = scanrelid; } /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&css->ss.ps); #ifdef PG95 ExecAssignScanProjectionInfoWithVarno(&css->ss, tlistvarno); #else ExecAssignScanProjectionInfo(&css->ss); #endif /* * The callback of custom-scan provider applies the final initialization * of the custom-scan-state node according to its logic. */ css->methods->BeginCustomScan(css, estate, eflags); return css; }