Datum gp_deprecated(PG_FUNCTION_ARGS) { /* Lookup the function that was called in the catalog */ Oid procOid = fcinfo->flinfo->fn_oid; char *procName = format_procedure(procOid); /* Return error that the function is deprecated */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function %s has been deprecated", procName))); /* unreachable */ PG_RETURN_NULL(); }
Datum randomTopics(PG_FUNCTION_ARGS) { int32 doclen = PG_GETARG_INT32(0); int32 num_topics = PG_GETARG_INT32(1); ArrayType * ret_topics_arr, * ret_topic_d_arr; int32 * ret_topics, * ret_topic_d; Datum * arr1 = palloc0(doclen * sizeof(Datum)); ret_topics_arr = construct_array(arr1,doclen,INT4OID,4,true,'i'); ret_topics = (int32 *)ARR_DATA_PTR(ret_topics_arr); Datum * arr2 = palloc0(num_topics * sizeof(Datum)); ret_topic_d_arr = construct_array(arr2,num_topics,INT4OID,4,true,'i'); ret_topic_d = (int32 *)ARR_DATA_PTR(ret_topic_d_arr); /* Sample topics */ int i, rtopic; for (i=0; i!=doclen; i++) { rtopic = random() % num_topics + 1; ret_topics[i] = rtopic; ret_topic_d[rtopic-1]++; } /* Package up the return arrays */ Datum values[2]; values[0] = PointerGetDatum(ret_topics_arr); values[1] = PointerGetDatum(ret_topic_d_arr); TupleDesc tuple; if (get_call_result_type(fcinfo, NULL, &tuple) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode( ERRCODE_FEATURE_NOT_SUPPORTED ), errmsg( "function returning record called in context " "that cannot accept type record" ))); tuple = BlessTupleDesc(tuple); bool * isnulls = palloc0(2 * sizeof(bool)); HeapTuple ret = heap_form_tuple(tuple, values, isnulls); if (isnulls[0] || isnulls[1]) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\" produced null results", format_procedure(fcinfo->flinfo->fn_oid),i))); PG_RETURN_DATUM(HeapTupleGetDatum(ret)); }
Datum internal_kmeans_agg_centroid_merge(PG_FUNCTION_ARGS) { /* This function is declared as strict. No checking null here. */ ArrayType *array = NULL; ArrayType *array2 = NULL; if (fcinfo->context && IsA(fcinfo->context, AggState)) array = PG_GETARG_ARRAYTYPE_P(0); else array = PG_GETARG_ARRAYTYPE_P_COPY(0); int array_dim = ARR_NDIM(array); int *p_array_dim = ARR_DIMS(array); int array_length = ArrayGetNItems(array_dim, p_array_dim); array2 = PG_GETARG_ARRAYTYPE_P(1); array_dim = ARR_NDIM(array2); p_array_dim = ARR_DIMS(array2); int array2_length = ArrayGetNItems(array_dim, p_array_dim); if (array_length != array2_length) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\", Inconsistent array length. " "first: %d, second:%d", format_procedure(fcinfo->flinfo->fn_oid), array_length, array2_length))); } float8* c_array = (float8 *)ARR_DATA_PTR(array); float8* c_array2 = (float8 *)ARR_DATA_PTR(array2); for(int i=0; i<array_length; i++) { c_array[i]+= c_array2[i]; } PG_RETURN_ARRAYTYPE_P(array); }
Datum alpine_miner_lr_combine(PG_FUNCTION_ARGS) { ArrayType *state1, *state2, *result; float8 *state1Data, *state2Data, *resultData; int i, size; int statelen; if (PG_ARGISNULL(0)) { if (PG_ARGISNULL(1)) PG_RETURN_NULL(); PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P(1)); } if (PG_ARGISNULL(1)) PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P(0)); state1 = PG_GETARG_ARRAYTYPE_P(0); state2 = PG_GETARG_ARRAYTYPE_P(1); if (ARR_NULLBITMAP(state1) || ARR_NULLBITMAP(state2) || ARR_NDIM(state1) != 1 || ARR_NDIM(state2) != 1 || ARR_ELEMTYPE(state1) != FLOAT8OID || ARR_ELEMTYPE(state2) != FLOAT8OID) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("preliminary segment-level calculation function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); } if (ARR_DIMS(state1)[0] == 1) PG_RETURN_ARRAYTYPE_P(state2); if (ARR_DIMS(state2)[0] == 1) PG_RETURN_ARRAYTYPE_P(state1); state1Data = (float8*) ARR_DATA_PTR(state1); state2Data = (float8*) ARR_DATA_PTR(state2); if (ARR_DIMS(state1)[0] != ARR_DIMS(state2)[0]) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("preliminary segment-level calculation function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)), errdetail("The independent-variable array is not of constant width."))); } statelen = ARR_DIMS(state1)[0]; size = statelen * sizeof(float8) + ARR_OVERHEAD_NONULLS(1); result = (ArrayType *) palloc(size); SET_VARSIZE(result, size); result->ndim = 1; result->dataoffset = 0; result->elemtype = FLOAT8OID; ARR_DIMS(result)[0] = statelen; ARR_LBOUND(result)[0] = 1; resultData = (float8*) ARR_DATA_PTR(result); memset(resultData, 0, statelen * sizeof(float8)); for (i = 0; i < statelen; i++){ resultData[i] = state1Data[i] + state2Data[i]; } PG_RETURN_ARRAYTYPE_P(result); }
Datum sampleNewTopics(PG_FUNCTION_ARGS) { int32 i, widx, wtopic, rtopic; ArrayType * doc_arr = PG_GETARG_ARRAYTYPE_P(0); ArrayType * topics_arr = PG_GETARG_ARRAYTYPE_P(1); ArrayType * topic_d_arr = PG_GETARG_ARRAYTYPE_P(2); ArrayType * global_count_arr = PG_GETARG_ARRAYTYPE_P(3); ArrayType * topic_counts_arr = PG_GETARG_ARRAYTYPE_P(4); int32 num_topics = PG_GETARG_INT32(5); int32 dsize = PG_GETARG_INT32(6); float8 alpha = PG_GETARG_FLOAT8(7); float8 eta = PG_GETARG_FLOAT8(8); if (ARR_NULLBITMAP(doc_arr) || ARR_NDIM(doc_arr) != 1 || ARR_ELEMTYPE(doc_arr) != INT4OID || ARR_NDIM(topics_arr) != 1 || ARR_ELEMTYPE(topics_arr) != INT4OID || ARR_NULLBITMAP(topic_d_arr) || ARR_NDIM(topic_d_arr) != 1 || ARR_ELEMTYPE(topic_d_arr) != INT4OID || ARR_NULLBITMAP(global_count_arr) || ARR_NDIM(global_count_arr) != 1 || ARR_ELEMTYPE(global_count_arr) != INT4OID || ARR_NULLBITMAP(topic_counts_arr) || ARR_NDIM(topic_counts_arr) != 1 || ARR_ELEMTYPE(topic_counts_arr) != INT4OID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); // the document array int32 * doc = (int32 *)ARR_DATA_PTR(doc_arr); int32 len = ARR_DIMS(doc_arr)[0]; // array giving topic assignment to each word in document int32 * topics = (int32 *)ARR_DATA_PTR(topics_arr); // distribution of topics in document int32 * topic_d = (int32 *)ARR_DATA_PTR(topic_d_arr); // the word-topic count matrix int32 * global_count = (int32 *)ARR_DATA_PTR(global_count_arr); // total number of words assigned to each topic in the whole corpus int32 * topic_counts = (int32 *)ARR_DATA_PTR(topic_counts_arr); ArrayType * ret_topics_arr, * ret_topic_d_arr; int32 * ret_topics, * ret_topic_d; Datum * arr1 = palloc0(len * sizeof(Datum)); ret_topics_arr = construct_array(arr1,len,INT4OID,4,true,'i'); ret_topics = (int32 *)ARR_DATA_PTR(ret_topics_arr); Datum * arr2 = palloc0(num_topics * sizeof(Datum)); ret_topic_d_arr = construct_array(arr2,num_topics,INT4OID,4,true,'i'); ret_topic_d = (int32 *)ARR_DATA_PTR(ret_topic_d_arr); for (i=0; i!=len; i++) { widx = doc[i]; if (widx < 1 || widx > dsize) ereport (ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); wtopic = topics[i]; rtopic = sampleTopic(num_topics,widx,wtopic,global_count, topic_d,topic_counts,alpha,eta); // <sampleNewTopics error checking> ret_topics[i] = rtopic; ret_topic_d[rtopic-1]++; } Datum values[2]; values[0] = PointerGetDatum(ret_topics_arr); values[1] = PointerGetDatum(ret_topic_d_arr); TupleDesc tuple; if (get_call_result_type(fcinfo, NULL, &tuple) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode( ERRCODE_FEATURE_NOT_SUPPORTED ), errmsg( "function returning record called in context " "that cannot accept type record" ))); tuple = BlessTupleDesc(tuple); bool * isnulls = palloc0(2 * sizeof(bool)); HeapTuple ret = heap_form_tuple(tuple, values, isnulls); if (isnulls[0] || isnulls[1]) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\" produced null results", format_procedure(fcinfo->flinfo->fn_oid),i))); PG_RETURN_DATUM(HeapTupleGetDatum(ret)); }
/* * Validator for a bloom opclass. */ bool blvalidate(Oid opclassoid) { bool result = true; HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; Oid opckeytype; char *opclassname; HeapTuple familytup; Form_pg_opfamily familyform; char *opfamilyname; CatCList *proclist, *oprlist; List *grouplist; OpFamilyOpFuncGroup *opclassgroup; int i; ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; opckeytype = classform->opckeytype; if (!OidIsValid(opckeytype)) opckeytype = opcintype; opclassname = NameStr(classform->opcname); /* Fetch opfamily information */ familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid)); if (!HeapTupleIsValid(familytup)) elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid); familyform = (Form_pg_opfamily) GETSTRUCT(familytup); opfamilyname = NameStr(familyform->opfname); /* Fetch all operators and support functions of the opfamily */ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid)); proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid)); /* Check individual support functions */ for (i = 0; i < proclist->n_members; i++) { HeapTuple proctup = &proclist->members[i]->tuple; Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup); bool ok; /* * All bloom support functions should be registered with matching * left/right types */ if (procform->amproclefttype != procform->amprocrighttype) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("bloom opfamily %s contains support procedure %s with cross-type registration", opfamilyname, format_procedure(procform->amproc)))); result = false; } /* * We can't check signatures except within the specific opclass, since * we need to know the associated opckeytype in many cases. */ if (procform->amproclefttype != opcintype) continue; /* Check procedure numbers and function signatures */ switch (procform->amprocnum) { case BLOOM_HASH_PROC: ok = check_amproc_signature(procform->amproc, INT4OID, false, 1, 1, opckeytype); break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("bloom opfamily %s contains function %s with invalid support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; continue; /* don't want additional message */ } if (!ok) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("gist opfamily %s contains function %s with wrong signature for support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; } } /* Check individual operators */ for (i = 0; i < oprlist->n_members; i++) { HeapTuple oprtup = &oprlist->members[i]->tuple; Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup); /* Check it's allowed strategy for bloom */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > BLOOM_NSTRATEGIES) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d", opfamilyname, format_operator(oprform->amopopr), oprform->amopstrategy))); result = false; } /* bloom doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* Check operator signature --- same for all bloom strategies */ if (!check_amop_signature(oprform->amopopr, BOOLOID, oprform->amoplefttype, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("bloom opfamily %s contains operator %s with wrong signature", opfamilyname, format_operator(oprform->amopopr)))); result = false; } } /* Now check for inconsistent groups of operators/functions */ grouplist = identify_opfamily_groups(oprlist, proclist); opclassgroup = NULL; foreach(lc, grouplist) { OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc); /* Remember the group exactly matching the test opclass */ if (thisgroup->lefttype == opcintype && thisgroup->righttype == opcintype) opclassgroup = thisgroup; /* * There is not a lot we can do to check the operator sets, since each * bloom opclass is more or less a law unto itself, and some contain * only operators that are binary-compatible with the opclass datatype * (meaning that empty operator sets can be OK). That case also means * that we shouldn't insist on nonempty function sets except for the * opclass's own group. */ }
static bool alpine_miner_float8_mregr_accum_get_state(PG_FUNCTION_ARGS, MRegrAccumState *outState) { float8 *stateData; int len, statelen; /* We should be strict, but it doesn't hurt to be paranoid */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2)) return false; outState->stateAsArray = PG_GETARG_ARRAYTYPE_P(0); outState->newX = PG_GETARG_ARRAYTYPE_P(2); /* Ensure that both arrays are single dimensional float8[] arrays */ if (ARR_NULLBITMAP(outState->stateAsArray) || ARR_NDIM(outState->stateAsArray) != 1 || ARR_ELEMTYPE(outState->stateAsArray) != FLOAT8OID || ARR_NDIM(outState->newX) != 1 || ARR_ELEMTYPE(outState->newX) != FLOAT8OID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); /* Only callable as a transition function */ if (!(fcinfo->context && IsA(fcinfo->context, AggState))) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" not called from aggregate", format_procedure(fcinfo->flinfo->fn_oid)))); /* newX with nulls will be ignored */ if (ARR_NULLBITMAP(outState->newX)) return false; /* * If length(state) == 1 then it is an unitialized state, extend as * needed, we use this instead of NULL so that we can declare the * function as strict. */ len = ARR_DIMS(outState->newX)[0]; statelen = 1 + (3*len + len*len)/2; if (ARR_DIMS(outState->stateAsArray)[0] == 1) { int size = statelen * sizeof(float8) + ARR_OVERHEAD_NONULLS(1); outState->stateAsArray = (ArrayType *) palloc(size); SET_VARSIZE(outState->stateAsArray, size); outState->stateAsArray->ndim = 1; outState->stateAsArray->dataoffset = 0; outState->stateAsArray->elemtype = FLOAT8OID; ARR_DIMS(outState->stateAsArray)[0] = statelen; ARR_LBOUND(outState->stateAsArray)[0] = 1; stateData = (float8*) ARR_DATA_PTR(outState->stateAsArray); memset(stateData, 0, statelen * sizeof(float8)); stateData[0] = len; } /* * Contents of 'state' are as follows: * [0] = len(X[]) * [1] = count * [2] = sum(y) * [3] = sum(y*y) * [4:N] = sum(X'[] * y) * [N+1:M] = sum(X[] * X'[]) * N = 3 + len(X) * M = N + len(X)*len(X) */ outState->len = (float8*) ARR_DATA_PTR(outState->stateAsArray); outState->Xty = outState->len + 1; outState->XtX = outState->len + 1 + len; outState->newXData = (float8*) ARR_DATA_PTR(outState->newX); /* It is an error if the number of indepent variables is not constant */ if (*outState->len != len) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)), errdetail("The independent-variable array is not of constant width."))); } /* Something is seriously fishy if our state has the wrong length */ if (ARR_DIMS(outState->stateAsArray)[0] != statelen) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); } /* Okay... All's good now do the work */ return true; }
/* ---------------------------------------------------------------- * ProcedureCreate * * Note: allParameterTypes, parameterModes, parameterNames, trftypes, 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.h. * ---------------------------------------------------------------- */ ObjectAddress ProcedureCreate(const char *procedureName, Oid procNamespace, bool replace, bool returnsSet, Oid returnType, Oid proowner, Oid languageObjectId, Oid languageValidator, const char *prosrc, const char *probin, char prokind, bool security_definer, bool isLeakProof, bool isStrict, char volatility, char parallel, oidvector *parameterTypes, Datum allParameterTypes, Datum parameterModes, Datum parameterNames, List *parameterDefaults, Datum trftypes, Datum proconfig, float4 procost, float4 prorows) { Oid retval; int parameterCount; int allParamCount; Oid *allParams; char *paramModes = NULL; bool genericInParam = false; bool genericOutParam = false; bool anyrangeInParam = false; bool anyrangeOutParam = false; bool internalInParam = false; bool internalOutParam = false; Oid variadicType = InvalidOid; Acl *proacl = NULL; Relation rel; HeapTuple tup; HeapTuple oldtup; bool nulls[Natts_pg_proc]; Datum values[Natts_pg_proc]; bool replaces[Natts_pg_proc]; NameData procname; TupleDesc tupDesc; bool is_update; ObjectAddress myself, referenced; int i; Oid trfid; /* * 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 */ /* Deconstruct array inputs */ 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; } 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); 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"); paramModes = (char *) ARR_DATA_PTR(modesArray); } /* * Detect whether we have polymorphic or INTERNAL arguments. The first * loop checks input arguments, the second output arguments. */ for (i = 0; i < parameterCount; i++) { switch (parameterTypes->values[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericInParam = true; break; case ANYRANGEOID: genericInParam = true; anyrangeInParam = true; break; case INTERNALOID: internalInParam = true; break; } } if (allParameterTypes != PointerGetDatum(NULL)) { for (i = 0; i < allParamCount; i++) { if (paramModes == NULL || paramModes[i] == PROARGMODE_IN || paramModes[i] == PROARGMODE_VARIADIC) continue; /* ignore input-only params */ switch (allParams[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericOutParam = true; break; case ANYRANGEOID: genericOutParam = true; anyrangeOutParam = true; break; case INTERNALOID: internalOutParam = true; break; } } } /* * Do not allow polymorphic return type unless at least one input argument * is polymorphic. ANYRANGE return type is even stricter: must have an * ANYRANGE input (since we can't deduce the specific range type from * ANYELEMENT). Also, do not allow return type INTERNAL unless at least * one input argument is INTERNAL. */ 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 == ANYRANGEOID || anyrangeOutParam) && !anyrangeInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" 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."))); if (paramModes != NULL) { /* * 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 (paramModes[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'", paramModes[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_protransform - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_proc_prokind - 1] = CharGetDatum(prokind); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer); values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof); 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_proparallel - 1] = CharGetDatum(parallel); 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; if (trftypes != PointerGetDatum(NULL)) values[Anum_pg_proc_protrftypes - 1] = trftypes; else nulls[Anum_pg_proc_protrftypes - 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; /* proacl will be determined later */ rel = table_open(ProcedureRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); /* Check for pre-existing definition */ oldtup = SearchSysCache3(PROCNAMEARGSNSP, PointerGetDatum(procedureName), PointerGetDatum(parameterTypes), ObjectIdGetDatum(procNamespace)); if (HeapTupleIsValid(oldtup)) { /* There is one; okay to replace it? */ Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup); Datum proargnames; bool isnull; const char *dropcmd; if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("function \"%s\" already exists with same argument types", procedureName))); if (!pg_proc_ownercheck(oldproc->oid, proowner)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, procedureName); /* Not okay to change routine kind */ if (oldproc->prokind != prokind) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change routine kind"), (oldproc->prokind == PROKIND_AGGREGATE ? errdetail("\"%s\" is an aggregate function.", procedureName) : oldproc->prokind == PROKIND_FUNCTION ? errdetail("\"%s\" is a function.", procedureName) : oldproc->prokind == PROKIND_PROCEDURE ? errdetail("\"%s\" is a procedure.", procedureName) : oldproc->prokind == PROKIND_WINDOW ? errdetail("\"%s\" is a window function.", procedureName) : 0))); dropcmd = (prokind == PROKIND_PROCEDURE ? "DROP PROCEDURE" : "DROP FUNCTION"); /* * Not okay to change the return type of the existing proc, since * existing rules, views, etc may depend on the return type. * * In case of a procedure, a changing return type means that whether * the procedure has output parameters was changed. Since there is no * user visible return type, we produce a more specific error message. */ if (returnType != oldproc->prorettype || returnsSet != oldproc->proretset) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), prokind == PROKIND_PROCEDURE ? errmsg("cannot change whether a procedure has output parameters") : errmsg("cannot change return type of existing function"), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); /* * 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(prokind, 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."), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); } /* * If there were any named input parameters, check to make sure the * names have not been changed, as this could break existing calls. We * allow adding names to formerly unnamed parameters, though. */ proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargnames, &isnull); if (!isnull) { Datum proargmodes; char **old_arg_names; char **new_arg_names; int n_old_arg_names; int n_new_arg_names; int j; proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargmodes, &isnull); if (isnull) proargmodes = PointerGetDatum(NULL); /* just to be sure */ n_old_arg_names = get_func_input_arg_names(proargnames, proargmodes, &old_arg_names); n_new_arg_names = get_func_input_arg_names(parameterNames, parameterModes, &new_arg_names); for (j = 0; j < n_old_arg_names; j++) { if (old_arg_names[j] == NULL) continue; if (j >= n_new_arg_names || new_arg_names[j] == NULL || strcmp(old_arg_names[j], new_arg_names[j]) != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change name of input parameter \"%s\"", old_arg_names[j]), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); } } /* * 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; 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"), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargdefaults, &isnull); Assert(!isnull); oldDefaults = castNode(List, stringToNode(TextDatumGetCString(proargdefaults))); 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"), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); newlc = lnext(newlc); } }
/* * 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 and maintain ThisTimeLineID. * RecoveryInProgress() will update ThisTimeLineID on promotion. */ if (!RecoveryInProgress()) end_of_wal = GetFlushRecPtr(); else end_of_wal = GetXLogReplayRecPtr(&ThisTimeLineID); ReplicationSlotAcquire(NameStr(*name), true); PG_TRY(); { /* restart at slot's confirmed_flush */ ctx = CreateDecodingContext(InvalidXLogRecPtr, options, false, logical_read_local_xlog_page, LogicalOutputPrepareWrite, LogicalOutputWrite, NULL); 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(); /* Decode until we run out of records */ 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; }
Datum alpine_plda_gene(PG_FUNCTION_ARGS) { ArrayType *assign; ArrayType *allassign; ArrayType *wordtopic; ArrayType *lastinfo; ArrayType *topiccount; ArrayType * column_array; ArrayType * lastassign; int32 * assign_array_data; int32 * wordtopic_data; int32 * lastinfo_data; int32 * lastassign_data; int32 * allassign_data; int32 * topiccount_array_data; int32 * column_array_data; Datum values[2]; int32 column_size,topicnumber,wordnumber; int32 temptopic; int32 k; float8 alpha,beta; bool * isnulls ; TupleDesc tuple; HeapTuple ret; Datum * arr1; Datum * arr2 ; if (PG_ARGISNULL(0)){ PG_RETURN_NULL(); } column_array=PG_GETARG_ARRAYTYPE_P(0); allassign=PG_GETARG_ARRAYTYPE_P(1); wordtopic=PG_GETARG_ARRAYTYPE_P(2); lastassign=PG_GETARG_ARRAYTYPE_P(3); lastinfo=PG_GETARG_ARRAYTYPE_P(4); alpha=PG_GETARG_FLOAT8(5); beta=PG_GETARG_FLOAT8(6); wordnumber=PG_GETARG_INT32(7); topicnumber=PG_GETARG_INT32(8); column_array_data = (int32*) ARR_DATA_PTR(column_array); allassign_data = (int32*) ARR_DATA_PTR(allassign); lastinfo_data= (int32*) ARR_DATA_PTR(lastinfo); lastassign_data= (int32*) ARR_DATA_PTR(lastassign); wordtopic_data=(int32*) ARR_DATA_PTR(wordtopic); column_size=ARR_DIMS(column_array)[0]; arr1 = palloc0(column_size * sizeof(Datum));//Datum * assign = construct_array(arr1,column_size,INT4OID,4,true,'i'); assign_array_data = (int32 *)ARR_DATA_PTR(assign); arr2 = palloc0(topicnumber * sizeof(Datum));//Datum * topiccount = construct_array(arr2,topicnumber,INT4OID,4,true,'i'); topiccount_array_data = (int32 *)ARR_DATA_PTR(topiccount); for ( k = 0; k < column_size; k++){ temptopic =getgrandomtopic (topicnumber, column_array_data[k], lastassign_data[k], wordtopic_data, lastinfo_data, allassign_data, alpha, beta , wordnumber) ; assign_array_data[k] = temptopic; topiccount_array_data[temptopic-1]++; } values[0] = PointerGetDatum(assign); values[1] = PointerGetDatum(topiccount); if (get_call_result_type(fcinfo, NULL, &tuple) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode( ERRCODE_FEATURE_NOT_SUPPORTED ), errmsg( "function returning record called in context " "that cannot accept type record" ))); tuple = BlessTupleDesc(tuple); isnulls = palloc0(2 * sizeof(bool)); ret = heap_form_tuple(tuple, values, isnulls); if (isnulls[0] || isnulls[1]) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\" produced null results", format_procedure(fcinfo->flinfo->fn_oid)))); PG_RETURN_DATUM(HeapTupleGetDatum(ret)); }
Datum gp_add_persistent_relation_node_entry(PG_FUNCTION_ARGS) { Datum values[Natts_gp_persistent_relation_node]; ItemPointerData persistentTid; int64 persistentSerialNum; int i; /* Must be super user */ if (!superuser()) elog(ERROR, "permission denied"); /* Check input arguments */ /* * First parameter is the tid, remaining parameters should match the column * types in gp_persistent_relation_node. */ if (PG_NARGS() != Natts_gp_persistent_relation_node + 1) { Oid procOid = fcinfo->flinfo->fn_oid; char *procName = format_procedure(procOid); elog(ERROR, "function '%s' received unexpected number of arguments", procName); } /* * For the moment we don't support inserting at particular tids, * initial argument MUST be null. */ if (!PG_ARGISNULL(0)) elog(ERROR, "direct tid assignment to %s is not yet supported", "gp_persistent_relation_node"); /* * validate that datatypes match expected, e.g. no one went and changed * the catalog without updating this function. */ /* Build up the tuple we want to add */ memset(&persistentTid, 0, sizeof(persistentTid)); for (i = 0; i < Natts_gp_persistent_relation_node; i++) { if (PG_ARGISNULL(i+1)) elog(ERROR, "null arguments not supported"); values[i] = PG_GETARG_DATUM(i+1); } /* * TODO: Validate the tuple * - Specified database exists * - Specified tablespace exists * - Specified relfile is in the filesystem * - etc. */ /* Add it to the table */ PersistentFileSysObj_AddTuple(PersistentFsObjType_RelationFile, values, true, /* flushToXlog */ &persistentTid, &persistentSerialNum); /* explain how we re-wrote that tuple */ elog(NOTICE, "inserted 1 row (TID %s, persistent_serial_num " INT64_FORMAT ")", ItemPointerToString(&persistentTid), persistentSerialNum); PG_RETURN_BOOL(true); }
static PyObj func_new_from_oid(PyTypeObject *subtype, Oid fn_oid, PyObj fn_oid_int, PyObj fn_oid_str) { volatile HeapTuple ht = NULL; volatile PyObj rob = NULL; Assert(OidIsValid(fn_oid)); Assert(fn_oid_int != NULL); Assert(fn_oid_str != NULL); rob = subtype->tp_alloc(subtype, 0); if (rob == NULL) return(NULL); PyPgFunction_SetOid(rob, fn_oid); PyPgFunction_SetStateful(rob, false); Py_INCREF(fn_oid_int); Py_INCREF(fn_oid_str); PyPgFunction_SetPyLongOid(rob, fn_oid_int); PyPgFunction_SetPyUnicodeOid(rob, fn_oid_str); /* * Collect the Function information from the system cache */ PG_TRY(); { Form_pg_proc ps; Form_pg_namespace ns; FmgrInfo flinfo; text *prosrc; Datum prosrc_datum; bool isnull = true; const char *filename = NULL, *nspname, *q_nspname; TupleDesc argdesc = NULL, result_desc = NULL; Oid prorettype = InvalidOid; PyObj id_str_ob = NULL, nspname_str_ob = NULL; PyObj filename_str_ob = NULL, q_nspname_str_ob = NULL; PyObj output = NULL, src = NULL; PyObj input; ht = SearchSysCache(PROCOID, fn_oid, 0, 0, 0); if (!HeapTupleIsValid(ht)) { ereport(ERROR,( errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("failed to find function at oid %d", fn_oid) )); } PyPgFunction_SetXMin(rob, HeapTupleHeaderGetXmin(ht->t_data)); PyPgFunction_SetItemPointer(rob, &(ht->t_self)); ps = (Form_pg_proc) GETSTRUCT(ht); PyPgFunction_SetNamespace(rob, ps->pronamespace); PyPgFunction_SetLanguage(rob, ps->prolang); PyPgFunction_SetReturnsSet(rob, ps->proretset); PyPgFunction_SetVolatile(rob, ps->provolatile); prorettype = ps->prorettype; prosrc_datum = SysCacheGetAttr( PROCOID, ht, Anum_pg_proc_prosrc, &isnull); if (!isnull) { prosrc = DatumGetTextPCopy(prosrc_datum); src = PyUnicode_FromTEXT(prosrc); PyPgFunction_SetSource(rob, src); pfree(prosrc); prosrc = NULL; } else { src = Py_None; Py_INCREF(src); PyPgFunction_SetSource(rob, src); } if (src == NULL) PyErr_RelayException(); /* * Get the function's address. */ fmgr_info(fn_oid, &flinfo); PyPgFunction_SetPGFunction(rob, flinfo.fn_addr); /* * Build function parameters TupleDesc */ if (ps->pronargs > 0) { argdesc = TupleDesc_From_pg_proc_arginfo(ht); input = PyPgTupleDesc_FromCopy(argdesc); if (input == NULL) PyErr_RelayException(); PyPgFunction_SetInput(rob, input); FreeTupleDesc(argdesc); } else { Py_INCREF(EmptyPyPgTupleDesc); PyPgFunction_SetInput(rob, EmptyPyPgTupleDesc); } /* * If it's a registered composite, * PyPgType_FromOid will resolve that below. */ if (prorettype == RECORDOID) { /* * Otherwise, build out a function result tupdesc. */ result_desc = build_function_result_tupdesc_t(ht); if (result_desc != NULL) { /* * Anonymous composite returned by function. */ output = PyPgType_FromTupleDesc(result_desc); PyPgFunction_SetOutput(rob, output); FreeTupleDesc(result_desc); /* * We will certainly be using it, so bless it right now iff * it's *not* polymorphic. */ if (output && !PyPgType_IsPolymorphic(output)) BlessTupleDesc(PyPgType_GetTupleDesc(output)); } else { /* * ew.. */ goto lookup_output_type; } } else { lookup_output_type: output = PyPgType_FromOid(prorettype); if (output == NULL) PyErr_RelayException(); PyPgFunction_SetOutput(rob, output); } RELEASESYSCACHE(&ht); /* * Don't worry *too* much about leaking memory. */ filename = format_procedure(fn_oid); Assert(filename != NULL); ht = SearchSysCache(NAMESPACEOID, PyPgFunction_GetNamespace(rob), 0, 0, 0); if (!HeapTupleIsValid(ht)) { pfree((char *) filename); elog(ERROR, "function %u namespace %u does not exist", fn_oid, PyPgFunction_GetNamespace(rob)); } ns = (Form_pg_namespace) GETSTRUCT(ht); nspname = pstrdup(NameStr(ns->nspname)); RELEASESYSCACHE(&ht); /* * Build the filename string. */ q_nspname = quote_identifier(nspname); nspname_str_ob = PyUnicode_FromCString(nspname); PyPgFunction_SetNamespaceName(rob, nspname_str_ob); if (nspname_str_ob == NULL) { /* * Invalid encoded string? */ if (nspname != q_nspname) pfree((char *) q_nspname); pfree((char *) nspname); PyErr_RelayException(); } q_nspname_str_ob = PyUnicode_FromCString(q_nspname); if (nspname != q_nspname) pfree((char *) q_nspname); pfree((char *) nspname); /* * Ignore the potential exception for a moment. */ id_str_ob = PyUnicode_FromCString(filename); /* * Skip the filename_str_ob if either of the above failed. */ if (id_str_ob != NULL && q_nspname_str_ob != NULL) { if (FunctionIsVisible(fn_oid)) filename_str_ob = PyUnicode_FromFormat("%U.%U", q_nspname_str_ob, id_str_ob); else { filename_str_ob = id_str_ob; Py_INCREF(id_str_ob); } } PyPgFunction_SetFilename(rob, filename_str_ob); Py_XDECREF(q_nspname_str_ob); Py_XDECREF(id_str_ob); pfree((char *) filename); if (filename_str_ob == NULL) PyErr_RelayException(); } PG_CATCH(); { Py_XDECREF(rob); rob = NULL; PyErr_SetPgError(false); if (ht != NULL) ReleaseSysCache(ht); } PG_END_TRY(); return(rob); }
static bool float8_mregr_accum_get_args(FunctionCallInfo fcinfo, MRegrAccumArgs *outArgs) { float8 *stateData; uint32 len, i; uint64 statelen; /* We should be strict, but it doesn't hurt to be paranoid */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2)) return false; outArgs->stateAsArray = PG_GETARG_ARRAYTYPE_P(0); outArgs->newY = PG_GETARG_FLOAT8(1); outArgs->newXAsArray = PG_GETARG_ARRAYTYPE_P(2); outArgs->newX = (float8*) ARR_DATA_PTR(outArgs->newXAsArray); /* Ensure that both arrays are single dimensional float8[] arrays */ if (ARR_NULLBITMAP(outArgs->stateAsArray) || ARR_NDIM(outArgs->stateAsArray) != 1 || ARR_ELEMTYPE(outArgs->stateAsArray) != FLOAT8OID || ARR_NDIM(outArgs->newXAsArray) != 1 || ARR_ELEMTYPE(outArgs->newXAsArray) != FLOAT8OID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); /* Only callable as a transition function */ if (!(fcinfo->context && IsA(fcinfo->context, AggState))) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" not called from aggregate", format_procedure(fcinfo->flinfo->fn_oid)))); /* newXAsArray with nulls will be ignored */ if (ARR_NULLBITMAP(outArgs->newXAsArray)) return false; /* See MPP-14102. Avoid overflow while initializing len */ if (ARR_DIMS(outArgs->newXAsArray)[0] > UINT32_MAX) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("number of independent variables cannot exceed %lu", (unsigned long) UINT32_MAX))); len = ARR_DIMS(outArgs->newXAsArray)[0]; /* * See MPP-13580. At least on certain platforms and with certain versions, * LAPACK will run into an infinite loop if pinv() is called for non-finite * matrices. We extend the check also to the dependent variables. */ for (i = 0; i < len; i++) if (!isfinite(outArgs->newX[i])) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("design matrix is not finite"))); if (!isfinite(outArgs->newY)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("dependent variables are not finite"))); /* * See MPP-14102. We want to avoid (a) long int overflows and (b) making * oversized allocation requests. * We could compute the maximum number of variables so that the transition- * state length still fits into MaxAllocSize, but (assuming MaxAllocSize may * change in the future) this calculation requires taking the root out of a * 64-bit long int. Since there is no standard library function for that, and * displaying this number of merely of theoretical interest (the actual * limit is a lot lower), we simply report that the number of independent * variables is too large. * Precondition: * len < 2^32. */ statelen = STATE_LEN(len); if (!IS_FEASIBLE_STATE_LEN(statelen)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("number of independent variables is too large"))); /* * If length(outArgs->stateAsArray) == 1 then it is an unitialized state. * We extend as needed. */ if (ARR_DIMS(outArgs->stateAsArray)[0] == 1) { /* * Precondition: * IS_FEASIBLE_STATE_LEN(statelen) */ Size size = statelen * sizeof(float8) + ARR_OVERHEAD_NONULLS(1); outArgs->stateAsArray = (ArrayType *) palloc(size); SET_VARSIZE(outArgs->stateAsArray, size); outArgs->stateAsArray->ndim = 1; outArgs->stateAsArray->dataoffset = 0; outArgs->stateAsArray->elemtype = FLOAT8OID; ARR_DIMS(outArgs->stateAsArray)[0] = statelen; ARR_LBOUND(outArgs->stateAsArray)[0] = 1; stateData = (float8*) ARR_DATA_PTR(outArgs->stateAsArray); memset(stateData, 0, statelen * sizeof(float8)); stateData[0] = len; } /* * Contents of 'state' are as follows: * [0] = len(X[]) * [1] = count * [2] = sum(y) * [3] = sum(y*y) * [4:N] = sum(X'[] * y) * [N+1:M] = sum(X[] * X'[]) * N = 3 + len(X) * M = N + len(X)*len(X) */ outArgs->len = (float8*) ARR_DATA_PTR(outArgs->stateAsArray); outArgs->count = outArgs->len + 1; outArgs->sumy = outArgs->len + 2; outArgs->sumy2 = outArgs->len + 3; outArgs->Xty = outArgs->len + 4; outArgs->XtX = outArgs->len + 4 + len; /* It is an error if the number of indepent variables is not constant */ if (*outArgs->len != len) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)), errdetail("The independent-variable array is not of constant width."))); } /* Something is seriously fishy if our state has the wrong length */ if ((uint64) ARR_DIMS(outArgs->stateAsArray)[0] != statelen) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); } /* Okay... All's good now do the work */ return true; }
/* * Preliminary segment-level calculation function for multi-linear regression * aggregates. */ Datum float8_mregr_combine(PG_FUNCTION_ARGS) { ArrayType *state1, *state2, *result; float8 *state1Data, *state2Data, *resultData; uint32 len; uint64 statelen, i; Size size; /* We should be strict, but it doesn't hurt to be paranoid */ if (PG_ARGISNULL(0)) { if (PG_ARGISNULL(1)) PG_RETURN_NULL(); PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P(1)); } if (PG_ARGISNULL(1)) PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P(0)); state1 = PG_GETARG_ARRAYTYPE_P(0); state2 = PG_GETARG_ARRAYTYPE_P(1); /* Ensure that both arrays are single dimensional float8[] arrays */ if (ARR_NULLBITMAP(state1) || ARR_NULLBITMAP(state2) || ARR_NDIM(state1) != 1 || ARR_NDIM(state2) != 1 || ARR_ELEMTYPE(state1) != FLOAT8OID || ARR_ELEMTYPE(state2) != FLOAT8OID) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("preliminary segment-level calculation function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); } /* * Remember that we initialized to {0}, so if either array is still at * the initial value then just return the other one */ if (ARR_DIMS(state1)[0] == 1) PG_RETURN_ARRAYTYPE_P(state2); if (ARR_DIMS(state2)[0] == 1) PG_RETURN_ARRAYTYPE_P(state1); state1Data = (float8*) ARR_DATA_PTR(state1); state2Data = (float8*) ARR_DATA_PTR(state2); if (ARR_DIMS(state1)[0] != ARR_DIMS(state2)[0] || state1Data[0] != state2Data[0]) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("preliminary segment-level calculation function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)), errdetail("The independent-variable array is not of constant width."))); } len = state1Data[0]; statelen = STATE_LEN(len); /* * Violation of any of the following conditions indicates bogus inputs. */ if (state1Data[0] > UINT32_MAX || (uint64) ARR_DIMS(state1)[0] != statelen || !IS_FEASIBLE_STATE_LEN(statelen)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("preliminary segment-level calculation function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); } /* Validations pass, allocate memory for result and do work */ /* * Precondition: * IS_FEASIBLE_STATE_LEN(statelen) */ size = statelen * sizeof(float8) + ARR_OVERHEAD_NONULLS(1); result = (ArrayType *) palloc(size); SET_VARSIZE(result, size); result->ndim = 1; result->dataoffset = 0; result->elemtype = FLOAT8OID; ARR_DIMS(result)[0] = statelen; ARR_LBOUND(result)[0] = 1; resultData = (float8*) ARR_DATA_PTR(result); memset(resultData, 0, statelen * sizeof(float8)); /* * Contents of 'state' are as follows: * [0] = len(X[]) * [1] = count * [2] = sum(y) * [3] = sum(y*y) * [4:N] = sum(X'[] * y) * [N+1:M] = sum(X[] * X'[]) * N = 3 + len(X) * M = N + len(X)*len(X) */ resultData[0] = len; for (i = 1; i < statelen; i++) resultData[i] = state1Data[i] + state2Data[i]; PG_RETURN_ARRAYTYPE_P(result); }
/* * Validator for a BRIN opclass. * * Some of the checks done here cover the whole opfamily, and therefore are * redundant when checking each opclass in a family. But they don't run long * enough to be much of a problem, so we accept the duplication rather than * complicate the amvalidate API. */ bool brinvalidate(Oid opclassoid) { bool result = true; HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; char *opclassname; HeapTuple familytup; Form_pg_opfamily familyform; char *opfamilyname; CatCList *proclist, *oprlist; uint64 allfuncs = 0; uint64 allops = 0; List *grouplist; OpFamilyOpFuncGroup *opclassgroup; int i; ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; opclassname = NameStr(classform->opcname); /* Fetch opfamily information */ familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid)); if (!HeapTupleIsValid(familytup)) elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid); familyform = (Form_pg_opfamily) GETSTRUCT(familytup); opfamilyname = NameStr(familyform->opfname); /* Fetch all operators and support functions of the opfamily */ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid)); proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid)); /* Check individual support functions */ for (i = 0; i < proclist->n_members; i++) { HeapTuple proctup = &proclist->members[i]->tuple; Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup); bool ok; /* Check procedure numbers and function signatures */ switch (procform->amprocnum) { case BRIN_PROCNUM_OPCINFO: ok = check_amproc_signature(procform->amproc, INTERNALOID, true, 1, 1, INTERNALOID); break; case BRIN_PROCNUM_ADDVALUE: ok = check_amproc_signature(procform->amproc, BOOLOID, true, 4, 4, INTERNALOID, INTERNALOID, INTERNALOID, INTERNALOID); break; case BRIN_PROCNUM_CONSISTENT: ok = check_amproc_signature(procform->amproc, BOOLOID, true, 3, 3, INTERNALOID, INTERNALOID, INTERNALOID); break; case BRIN_PROCNUM_UNION: ok = check_amproc_signature(procform->amproc, BOOLOID, true, 3, 3, INTERNALOID, INTERNALOID, INTERNALOID); break; default: /* Complain if it's not a valid optional proc number */ if (procform->amprocnum < BRIN_FIRST_OPTIONAL_PROCNUM || procform->amprocnum > BRIN_LAST_OPTIONAL_PROCNUM) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin operator family \"%s\" contains function %s with invalid support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; continue; /* omit bad proc numbers from allfuncs */ } /* Can't check signatures of optional procs, so assume OK */ ok = true; break; } if (!ok) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin operator family \"%s\" contains function %s with wrong signature for support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; } /* Track all valid procedure numbers seen in opfamily */ allfuncs |= ((uint64) 1) << procform->amprocnum; } /* Check individual operators */ for (i = 0; i < oprlist->n_members; i++) { HeapTuple oprtup = &oprlist->members[i]->tuple; Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup); /* Check that only allowed strategy numbers exist */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin operator family \"%s\" contains operator %s with invalid strategy number %d", opfamilyname, format_operator(oprform->amopopr), oprform->amopstrategy))); result = false; } else { /* * The set of operators supplied varies across BRIN opfamilies. * Our plan is to identify all operator strategy numbers used in * the opfamily and then complain about datatype combinations that * are missing any operator(s). However, consider only numbers * that appear in some non-cross-type case, since cross-type * operators may have unique strategies. (This is not a great * heuristic, in particular an erroneous number used in a * cross-type operator will not get noticed; but the core BRIN * opfamilies are messy enough to make it necessary.) */ if (oprform->amoplefttype == oprform->amoprighttype) allops |= ((uint64) 1) << oprform->amopstrategy; } /* brin doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin operator family \"%s\" contains invalid ORDER BY specification for operator %s", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* Check operator signature --- same for all brin strategies */ if (!check_amop_signature(oprform->amopopr, BOOLOID, oprform->amoplefttype, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin operator family \"%s\" contains operator %s with wrong signature", opfamilyname, format_operator(oprform->amopopr)))); result = false; } } /* Now check for inconsistent groups of operators/functions */ grouplist = identify_opfamily_groups(oprlist, proclist); opclassgroup = NULL; foreach(lc, grouplist) { OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc); /* Remember the group exactly matching the test opclass */ if (thisgroup->lefttype == opcintype && thisgroup->righttype == opcintype) opclassgroup = thisgroup; /* * Some BRIN opfamilies expect cross-type support functions to exist, * and some don't. We don't know exactly which are which, so if we * find a cross-type operator for which there are no support functions * at all, let it pass. (Don't expect that all operators exist for * such cross-type cases, either.) */ if (thisgroup->functionset == 0 && thisgroup->lefttype != thisgroup->righttype) continue; /* * Else complain if there seems to be an incomplete set of either * operators or support functions for this datatype pair. */ if (thisgroup->operatorset != allops) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin operator family \"%s\" is missing operator(s) for types %s and %s", opfamilyname, format_type_be(thisgroup->lefttype), format_type_be(thisgroup->righttype)))); result = false; } if (thisgroup->functionset != allfuncs) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin operator family \"%s\" is missing support function(s) for types %s and %s", opfamilyname, format_type_be(thisgroup->lefttype), format_type_be(thisgroup->righttype)))); result = false; } }
Datum internal_kmeans_closest_centroid(PG_FUNCTION_ARGS) { ArrayType *point_array; ArrayType *centroids_array; float8 distance, min_distance = INFINITY; int closest_centroid = 0; int cid; point_array = PG_GETARG_ARRAYTYPE_P(verify_arg_nonnull(fcinfo, 0)); float8* c_point_array = (float8 *)ARR_DATA_PTR(point_array); centroids_array = PG_GETARG_ARRAYTYPE_P(verify_arg_nonnull(fcinfo, 1)); float8* c_centroids_array = (float8 *)ARR_DATA_PTR(centroids_array); int dimension = PG_GETARG_INT32(verify_arg_nonnull(fcinfo, 2)); int num_of_centroids = PG_GETARG_INT32(verify_arg_nonnull(fcinfo, 3)); int centroids_array_len = num_of_centroids*dimension; int dist_metric = PG_GETARG_INT32(verify_arg_nonnull(fcinfo, 4)); ArrayType *canopy_ids_arr = NULL; int4 *canopy_ids = NULL; bool indirect; if (PG_ARGISNULL(5)) { indirect = false; } else { indirect = true; canopy_ids_arr = PG_GETARG_ARRAYTYPE_P(5); /* There should always be a close canopy, but let's be on the safe side. */ if (ARR_NDIM(canopy_ids_arr) == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("internal error: array of close canopies cannot be empty"))); canopy_ids = (int4*) ARR_DATA_PTR(canopy_ids_arr); num_of_centroids = ARR_DIMS(canopy_ids_arr)[0]; } if (dimension < 1) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\", Invalid dimension:%d", format_procedure(fcinfo->flinfo->fn_oid), dimension))); } if (num_of_centroids < 1) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\", Invalid num_of_centroids:%d", format_procedure(fcinfo->flinfo->fn_oid), num_of_centroids))); } int array_dim = ARR_NDIM(point_array); int *p_array_dim = ARR_DIMS(point_array); int array_length = ArrayGetNItems(array_dim, p_array_dim); if (array_length != dimension) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\", Invalid point array length. " "Expected: %d, Actual:%d", format_procedure(fcinfo->flinfo->fn_oid), dimension, array_length))); } array_dim = ARR_NDIM(centroids_array); p_array_dim = ARR_DIMS(centroids_array); array_length = ArrayGetNItems(array_dim, p_array_dim); if (array_length != centroids_array_len) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\", Invalid centroids array length. " "Expected: %d, Actual:%d", format_procedure(fcinfo->flinfo->fn_oid), centroids_array_len, array_length))); } for (int i = 0; i< num_of_centroids; i++) { cid = indirect ? canopy_ids[i] - ARR_LBOUND(canopy_ids_arr)[0] : i; double * centroid = c_centroids_array+cid*dimension; MetricFunc func = get_metric_fn_for_array(dist_metric); distance = (*func)(centroid, c_point_array, dimension); if (distance < min_distance) { closest_centroid = cid; min_distance = distance; } } PG_RETURN_INT32(closest_centroid+ARR_LBOUND(centroids_array)[0]); }
Datum internal_kmeans_agg_centroid_trans(PG_FUNCTION_ARGS) { ArrayType *array = NULL; ArrayType *cent_array = NULL; int32 dimension; int32 num_of_centroids; int32 centroid_index; bool rebuild_array = false; int32 expected_array_len; float8 *c_array = NULL; cent_array = PG_GETARG_ARRAYTYPE_P(verify_arg_nonnull(fcinfo, 1)); int array_dim = ARR_NDIM(cent_array); int *p_array_dim = ARR_DIMS(cent_array); int array_length = ArrayGetNItems(array_dim, p_array_dim); float8* c_cent_array = (float8 *)ARR_DATA_PTR(cent_array); dimension = PG_GETARG_INT32(verify_arg_nonnull(fcinfo, 2)); num_of_centroids = PG_GETARG_INT32(verify_arg_nonnull(fcinfo, 3)); centroid_index = PG_GETARG_INT32(verify_arg_nonnull(fcinfo, 4)); expected_array_len = num_of_centroids*dimension; if (dimension < 1) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\", Invalid dimension:%d", format_procedure(fcinfo->flinfo->fn_oid), dimension))); } if (array_length != dimension) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\", Inconsistent Dimension. " "Expected:%d, Actual:%d", format_procedure(fcinfo->flinfo->fn_oid), dimension, array_length))); } if (num_of_centroids < 1) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\", Invalid num_of_centroids:%d", format_procedure(fcinfo->flinfo->fn_oid), num_of_centroids))); } if (centroid_index < 1 || centroid_index>num_of_centroids) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\", Invalid centroid_index:%d", format_procedure(fcinfo->flinfo->fn_oid), centroid_index))); } if (PG_ARGISNULL(0)) { c_array = palloc0(expected_array_len*sizeof(float8)); rebuild_array = true; } else { if (fcinfo->context && IsA(fcinfo->context, AggState)) array = PG_GETARG_ARRAYTYPE_P(0); else array = PG_GETARG_ARRAYTYPE_P_COPY(0); array_dim = ARR_NDIM(array); p_array_dim = ARR_DIMS(array); array_length = ArrayGetNItems(array_dim, p_array_dim); if (array_length != expected_array_len) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function \"%s\", Invalid array length. " "Expected: %d, Actual:%d", format_procedure(fcinfo->flinfo->fn_oid), expected_array_len, array_length))); } c_array = (float8 *)ARR_DATA_PTR(array); } float8 * data_ptr = c_array+(centroid_index-1)*dimension; for(int index=0; index<dimension; index++) { data_ptr[index] = c_cent_array[index]; } if (rebuild_array) { /* construct a new array to keep the aggr states. */ array = construct_array( (Datum *)c_array, expected_array_len, FLOAT8OID, sizeof(float8), true, 'd' ); } PG_RETURN_ARRAYTYPE_P(array); }
/* * Validator for an SP-GiST opclass. * * Some of the checks done here cover the whole opfamily, and therefore are * redundant when checking each opclass in a family. But they don't run long * enough to be much of a problem, so we accept the duplication rather than * complicate the amvalidate API. */ bool spgvalidate(Oid opclassoid) { bool result = true; HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; char *opclassname; HeapTuple familytup; Form_pg_opfamily familyform; char *opfamilyname; CatCList *proclist, *oprlist; List *grouplist; OpFamilyOpFuncGroup *opclassgroup; int i; ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; opclassname = NameStr(classform->opcname); /* Fetch opfamily information */ familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid)); if (!HeapTupleIsValid(familytup)) elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid); familyform = (Form_pg_opfamily) GETSTRUCT(familytup); opfamilyname = NameStr(familyform->opfname); /* Fetch all operators and support functions of the opfamily */ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid)); proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid)); /* Check individual support functions */ for (i = 0; i < proclist->n_members; i++) { HeapTuple proctup = &proclist->members[i]->tuple; Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup); bool ok; /* * All SP-GiST support functions should be registered with matching * left/right types */ if (procform->amproclefttype != procform->amprocrighttype) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s contains support procedure %s with cross-type registration", opfamilyname, format_procedure(procform->amproc)))); result = false; } /* Check procedure numbers and function signatures */ switch (procform->amprocnum) { case SPGIST_CONFIG_PROC: case SPGIST_CHOOSE_PROC: case SPGIST_PICKSPLIT_PROC: case SPGIST_INNER_CONSISTENT_PROC: ok = check_amproc_signature(procform->amproc, VOIDOID, true, 2, 2, INTERNALOID, INTERNALOID); break; case SPGIST_LEAF_CONSISTENT_PROC: ok = check_amproc_signature(procform->amproc, BOOLOID, true, 2, 2, INTERNALOID, INTERNALOID); break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s contains function %s with invalid support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; continue; /* don't want additional message */ } if (!ok) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s contains function %s with wrong signature for support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; } } /* Check individual operators */ for (i = 0; i < oprlist->n_members; i++) { HeapTuple oprtup = &oprlist->members[i]->tuple; Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup); /* TODO: Check that only allowed strategy numbers exist */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s contains operator %s with invalid strategy number %d", opfamilyname, format_operator(oprform->amopopr), oprform->amopstrategy))); result = false; } /* spgist doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s contains invalid ORDER BY specification for operator %s", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* Check operator signature --- same for all spgist strategies */ if (!check_amop_signature(oprform->amopopr, BOOLOID, oprform->amoplefttype, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s contains operator %s with wrong signature", opfamilyname, format_operator(oprform->amopopr)))); result = false; } } /* Now check for inconsistent groups of operators/functions */ grouplist = identify_opfamily_groups(oprlist, proclist); opclassgroup = NULL; foreach(lc, grouplist) { OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc); /* Remember the group exactly matching the test opclass */ if (thisgroup->lefttype == opcintype && thisgroup->righttype == opcintype) opclassgroup = thisgroup; /* * Complain if there are any datatype pairs with functions but no * operators. This is about the best we can do for now to detect * missing operators. */ if (thisgroup->operatorset == 0) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s is missing operator(s) for types %s and %s", opfamilyname, format_type_be(thisgroup->lefttype), format_type_be(thisgroup->righttype)))); result = false; } /* * Complain if we're missing functions for any datatype, remembering * that SP-GiST doesn't use cross-type support functions. */ if (thisgroup->lefttype != thisgroup->righttype) continue; for (i = 1; i <= SPGISTNProc; i++) { if ((thisgroup->functionset & (((uint64) 1) << i)) != 0) continue; /* got it */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s is missing support function %d for type %s", opfamilyname, i, format_type_be(thisgroup->lefttype)))); result = false; } }
/* * Validator for a hash opclass. * * Some of the checks done here cover the whole opfamily, and therefore are * redundant when checking each opclass in a family. But they don't run long * enough to be much of a problem, so we accept the duplication rather than * complicate the amvalidate API. */ bool hashvalidate(Oid opclassoid) { bool result = true; HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; char *opclassname; HeapTuple familytup; Form_pg_opfamily familyform; char *opfamilyname; CatCList *proclist, *oprlist; List *grouplist; OpFamilyOpFuncGroup *opclassgroup; List *hashabletypes = NIL; int i; ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; opclassname = NameStr(classform->opcname); /* Fetch opfamily information */ familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid)); if (!HeapTupleIsValid(familytup)) elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid); familyform = (Form_pg_opfamily) GETSTRUCT(familytup); opfamilyname = NameStr(familyform->opfname); /* Fetch all operators and support functions of the opfamily */ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid)); proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid)); /* Check individual support functions */ for (i = 0; i < proclist->n_members; i++) { HeapTuple proctup = &proclist->members[i]->tuple; Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup); /* * All hash functions should be registered with matching left/right * types */ if (procform->amproclefttype != procform->amprocrighttype) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains support procedure %s with cross-type registration", opfamilyname, format_procedure(procform->amproc)))); result = false; } /* Check procedure numbers and function signatures */ switch (procform->amprocnum) { case HASHPROC: if (!check_hash_func_signature(procform->amproc, INT4OID, procform->amproclefttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains function %s with wrong signature for support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; } else { /* Remember which types we can hash */ hashabletypes = list_append_unique_oid(hashabletypes, procform->amproclefttype); } break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains function %s with invalid support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; break; } } /* Check individual operators */ for (i = 0; i < oprlist->n_members; i++) { HeapTuple oprtup = &oprlist->members[i]->tuple; Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup); /* Check that only allowed strategy numbers exist */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > HTMaxStrategyNumber) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains operator %s with invalid strategy number %d", opfamilyname, format_operator(oprform->amopopr), oprform->amopstrategy))); result = false; } /* hash doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains invalid ORDER BY specification for operator %s", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* Check operator signature --- same for all hash strategies */ if (!check_amop_signature(oprform->amopopr, BOOLOID, oprform->amoplefttype, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains operator %s with wrong signature", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* There should be relevant hash procedures for each datatype */ if (!list_member_oid(hashabletypes, oprform->amoplefttype) || !list_member_oid(hashabletypes, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" lacks support function for operator %s", opfamilyname, format_operator(oprform->amopopr)))); result = false; } } /* Now check for inconsistent groups of operators/functions */ grouplist = identify_opfamily_groups(oprlist, proclist); opclassgroup = NULL; foreach(lc, grouplist) { OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc); /* Remember the group exactly matching the test opclass */ if (thisgroup->lefttype == opcintype && thisgroup->righttype == opcintype) opclassgroup = thisgroup; /* * Complain if there seems to be an incomplete set of operators for * this datatype pair (implying that we have a hash function but no * operator). */ if (thisgroup->operatorset != (1 << HTEqualStrategyNumber)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" is missing operator(s) for types %s and %s", opfamilyname, format_type_be(thisgroup->lefttype), format_type_be(thisgroup->righttype)))); result = false; } }
Datum exec_sql_using(PG_FUNCTION_ARGS) { HeapTuple procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fcinfo->flinfo->fn_oid)); if (!HeapTupleIsValid(procedureTuple)) ereport(ERROR, (errmsg("cache lookup failed for function %u", fcinfo->flinfo->fn_oid))); Oid* types = NULL; char** names = NULL; char* modes = NULL; int nargs = get_func_arg_info(procedureTuple, &types, &names, &modes); Oid resultTypeOid; TupleDesc tupleDesc; TypeFuncClass resultType = get_call_result_type(fcinfo, &resultTypeOid, &tupleDesc); bool returnTypeIsByValue; int16 returnTypeLen; get_typlenbyval(resultTypeOid, &returnTypeLen, &returnTypeIsByValue); if (resultType != TYPEFUNC_SCALAR && resultType != TYPEFUNC_COMPOSITE) ereport(ERROR, ( errmsg("function \"%s\" has indeterminable result type", format_procedure(fcinfo->flinfo->fn_oid)) )); bool returnVoid = resultTypeOid == VOIDOID; ReleaseSysCache(procedureTuple); if (nargs < 2) ereport(ERROR, ( errmsg("function \"%s\" has less than 2 arguments", format_procedure(fcinfo->flinfo->fn_oid)) )); else if (modes != NULL) for (int i = 0; i < nargs; i++) { if (modes[i] != PROARGMODE_IN) ereport(ERROR, ( errmsg("function \"%s\" has non-IN arguments", format_procedure(fcinfo->flinfo->fn_oid)) )); } else if (PG_ARGISNULL(0)) ereport(ERROR, ( errmsg("function \"%s\" called with NULL as first argument", format_procedure(fcinfo->flinfo->fn_oid)) )); char* stmt = NULL; if (types[0] == TEXTOID) stmt = DatumGetCString( DirectFunctionCall1(textout, PG_GETARG_DATUM(0))); else if (types[0] == VARCHAROID) stmt = DatumGetCString( DirectFunctionCall1(varcharout, PG_GETARG_DATUM(0))); else ereport(ERROR, ( errmsg("function \"%s\" does not have a leading VARCHAR/TEXT " "argument", format_procedure(fcinfo->flinfo->fn_oid)) )); char* nulls = NULL; for (int i = 1; i < nargs; i++) if (PG_ARGISNULL(i)) { if (nulls == NULL) { nulls = palloc0(sizeof(char) * (nargs - 1)); memset(nulls, ' ', nargs - 1); } nulls[i - 1] = 'n'; } SPI_connect(); SPIPlanPtr plan = SPI_prepare(stmt, nargs - 1, &types[1]); if (plan == NULL) ereport(ERROR, ( errmsg("function \"%s\" could not obtain execution plan for " "SQL statement", format_procedure(fcinfo->flinfo->fn_oid)) )); int result = SPI_execute_plan(plan, &fcinfo->arg[1], nulls, false, returnVoid ? 0 : 1); Datum returnValue = 0; bool returnNull = false; if (!returnVoid) { if (result != SPI_OK_SELECT && result != SPI_OK_INSERT_RETURNING && result != SPI_OK_DELETE_RETURNING && result == SPI_OK_UPDATE_RETURNING) ereport(ERROR, ( errmsg("function \"%s\" could not obtain result from query", format_procedure(fcinfo->flinfo->fn_oid)) )); else if (SPI_tuptable->tupdesc->natts != 1) ereport(ERROR, ( errmsg("function \"%s\" retrieved more than one column from " "query", format_procedure(fcinfo->flinfo->fn_oid)) )); else if (resultTypeOid != SPI_gettypeid(SPI_tuptable->tupdesc, 1)) ereport(ERROR, ( errmsg("function \"%s\" has different return type OID than " "what query returned", format_procedure(fcinfo->flinfo->fn_oid)) )); /* It is important to copy the value into the upper executor context, * i.e., the memory context that was current when SPI_connect was * called */ returnValue = SPI_getbinval(SPI_copytuple(SPI_tuptable->vals[0]), SPI_tuptable->tupdesc, 1, &returnNull); } SPI_freeplan(plan); if (nulls) pfree(nulls); SPI_finish(); if (result < 0) ereport(ERROR, ( errmsg("function \"%s\" encountered error %d during SQL execution", format_procedure(fcinfo->flinfo->fn_oid), result) )); if (returnVoid) PG_RETURN_VOID(); else if (returnNull) PG_RETURN_NULL(); else return returnValue; }
/* * Validator for a btree opclass. * * Some of the checks done here cover the whole opfamily, and therefore are * redundant when checking each opclass in a family. But they don't run long * enough to be much of a problem, so we accept the duplication rather than * complicate the amvalidate API. */ bool btvalidate(Oid opclassoid) { bool result = true; HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; char *opclassname; HeapTuple familytup; Form_pg_opfamily familyform; char *opfamilyname; CatCList *proclist, *oprlist; List *grouplist; OpFamilyOpFuncGroup *opclassgroup; List *familytypes; int i; ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; opclassname = NameStr(classform->opcname); /* Fetch opfamily information */ familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid)); if (!HeapTupleIsValid(familytup)) elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid); familyform = (Form_pg_opfamily) GETSTRUCT(familytup); opfamilyname = NameStr(familyform->opfname); /* Fetch all operators and support functions of the opfamily */ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid)); proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid)); /* Check individual support functions */ for (i = 0; i < proclist->n_members; i++) { HeapTuple proctup = &proclist->members[i]->tuple; Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup); bool ok; /* Check procedure numbers and function signatures */ switch (procform->amprocnum) { case BTORDER_PROC: ok = check_amproc_signature(procform->amproc, INT4OID, true, 2, 2, procform->amproclefttype, procform->amprocrighttype); break; case BTSORTSUPPORT_PROC: ok = check_amproc_signature(procform->amproc, VOIDOID, true, 1, 1, INTERNALOID); break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" contains function %s with invalid support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; continue; /* don't want additional message */ } if (!ok) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" contains function %s with wrong signature for support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; } } /* Check individual operators */ for (i = 0; i < oprlist->n_members; i++) { HeapTuple oprtup = &oprlist->members[i]->tuple; Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup); /* Check that only allowed strategy numbers exist */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > BTMaxStrategyNumber) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" contains operator %s with invalid strategy number %d", opfamilyname, format_operator(oprform->amopopr), oprform->amopstrategy))); result = false; } /* btree doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" contains invalid ORDER BY specification for operator %s", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* Check operator signature --- same for all btree strategies */ if (!check_amop_signature(oprform->amopopr, BOOLOID, oprform->amoplefttype, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" contains operator %s with wrong signature", opfamilyname, format_operator(oprform->amopopr)))); result = false; } } /* Now check for inconsistent groups of operators/functions */ grouplist = identify_opfamily_groups(oprlist, proclist); opclassgroup = NULL; familytypes = NIL; foreach(lc, grouplist) { OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc); /* Remember the group exactly matching the test opclass */ if (thisgroup->lefttype == opcintype && thisgroup->righttype == opcintype) opclassgroup = thisgroup; /* * Identify all distinct data types handled in this opfamily. This * implementation is O(N^2), but there aren't likely to be enough * types in the family for it to matter. */ familytypes = list_append_unique_oid(familytypes, thisgroup->lefttype); familytypes = list_append_unique_oid(familytypes, thisgroup->righttype); /* * Complain if there seems to be an incomplete set of either operators * or support functions for this datatype pair. The only thing that * is considered optional is the sortsupport function. */ if (thisgroup->operatorset != ((1 << BTLessStrategyNumber) | (1 << BTLessEqualStrategyNumber) | (1 << BTEqualStrategyNumber) | (1 << BTGreaterEqualStrategyNumber) | (1 << BTGreaterStrategyNumber))) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" is missing operator(s) for types %s and %s", opfamilyname, format_type_be(thisgroup->lefttype), format_type_be(thisgroup->righttype)))); result = false; } if ((thisgroup->functionset & (1 << BTORDER_PROC)) == 0) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" is missing support function for types %s and %s", opfamilyname, format_type_be(thisgroup->lefttype), format_type_be(thisgroup->righttype)))); result = false; } }
/* * 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 = PG_GETARG_NAME(0); 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; 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); /* 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"); check_permissions(); CheckLogicalDecodingRequirements(); arr = PG_GETARG_ARRAYTYPE_P(3); ndim = ARR_NDIM(arr); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); 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))); } } 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); CheckLogicalDecodingRequirements(); ReplicationSlotAcquire(NameStr(*name)); PG_TRY(); { 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 \"%s\" expects textual data", NameStr(MyReplicationSlot->data.plugin), format_procedure(fcinfo->flinfo->fn_oid)))); ctx->output_writer_private = p; 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 && ctx->reader->EndRecPtr < end_of_wal)) { XLogRecord *record; char *errm = NULL; record = XLogReadRecord(ctx->reader, startptr, &errm); if (errm) elog(ERROR, "%s", errm); startptr = InvalidXLogRecPtr; /* * The {begin_txn,change,commit_txn}_wrapper callbacks above will * store the description into our tuplestore. */ if (record != NULL) LogicalDecodingProcessRecord(ctx, record); /* 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(); } } PG_CATCH(); { /* clear all timetravel entries */ InvalidateSystemCaches(); PG_RE_THROW(); } PG_END_TRY(); 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); /* free context, call shutdown callback */ FreeDecodingContext(ctx); ReplicationSlotRelease(); InvalidateSystemCaches(); return (Datum) 0; }