/** * Given the oid of a relation, this method calculates reltuples, relpages. This only looks up * local information (on master or segments). It produces meaningful values for AO and * heap tables and returns [0.0,0.0] for all other relations. * Input: * relationoid * Output: * array of two values [reltuples,relpages] */ Datum gp_statistics_estimate_reltuples_relpages_oid(PG_FUNCTION_ARGS) { float4 relpages = 0.0; float4 reltuples = 0.0; Oid relOid = PG_GETARG_OID(0); Datum values[2]; ArrayType *result; Relation rel = try_relation_open(relOid, AccessShareLock, false); if (rel != NULL) { if (rel->rd_rel->relkind == RELKIND_RELATION) { if (RelationIsHeap(rel)) { gp_statistics_estimate_reltuples_relpages_heap(rel, &reltuples, &relpages); } else if (RelationIsAoRows(rel)) { gp_statistics_estimate_reltuples_relpages_ao_rows(rel, &reltuples, &relpages); } else if (RelationIsAoCols(rel)) { gp_statistics_estimate_reltuples_relpages_ao_cs(rel, &reltuples, &relpages); } } else if (rel->rd_rel->relkind == RELKIND_INDEX) { reltuples = 1.0; relpages = RelationGetNumberOfBlocks(rel); } else { /** * Should we silently return [0.0,0.0] or error out? Currently, we choose option 1. */ } relation_close(rel, AccessShareLock); } else { /** * Should we silently return [0.0,0.0] or error out? Currently, we choose option 1. */ } values[0] = Float4GetDatum(reltuples); values[1] = Float4GetDatum(relpages); result = construct_array(values, 2, FLOAT4OID, sizeof(float4), true, 'i'); PG_RETURN_ARRAYTYPE_P(result); }
Datum HASHAPI_Hash_1_float4(PG_FUNCTION_ARGS) { int32 num_segs; /* number of segments */ float4 val1; /* varchar value */ unsigned int targetbucket; /* 0-based */ int16 algorithm; /* hashing algorithm */ Datum d1; Oid oid; /* Get number of segments */ num_segs = PG_GETARG_INT32(0); /* Get hashing algoriithm */ algorithm = PG_GETARG_INT16(1); /* Get the value to hash */ val1 = PG_GETARG_FLOAT4(2); d1 = Float4GetDatum(val1); /* create a CdbHash for this hash test. */ h = makeCdbHash(num_segs, algorithm); /* init cdb hash */ cdbhashinit(h); oid = FLOAT4OID; cdbhash(h, d1, oid); /* reduce the result hash value */ targetbucket = cdbhashreduce(h); PG_RETURN_INT32(targetbucket); /* return target bucket (segID) */ }
/* ---------------- * index_store_float8_orderby_distances * * Convert AM distance function's results (that can be inexact) * to ORDER BY types and save them into xs_orderbyvals/xs_orderbynulls * for a possible recheck. * ---------------- */ void index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes, double *distances, bool recheckOrderBy) { int i; scan->xs_recheckorderby = recheckOrderBy; if (!distances) { Assert(!scan->xs_recheckorderby); for (i = 0; i < scan->numberOfOrderBys; i++) { scan->xs_orderbyvals[i] = (Datum) 0; scan->xs_orderbynulls[i] = true; } return; } for (i = 0; i < scan->numberOfOrderBys; i++) { if (orderByTypes[i] == FLOAT8OID) { #ifndef USE_FLOAT8_BYVAL /* must free any old value to avoid memory leakage */ if (!scan->xs_orderbynulls[i]) pfree(DatumGetPointer(scan->xs_orderbyvals[i])); #endif scan->xs_orderbyvals[i] = Float8GetDatum(distances[i]); scan->xs_orderbynulls[i] = false; } else if (orderByTypes[i] == FLOAT4OID) { /* convert distance function's result to ORDER BY type */ #ifndef USE_FLOAT4_BYVAL /* must free any old value to avoid memory leakage */ if (!scan->xs_orderbynulls[i]) pfree(DatumGetPointer(scan->xs_orderbyvals[i])); #endif scan->xs_orderbyvals[i] = Float4GetDatum((float4) distances[i]); scan->xs_orderbynulls[i] = false; } else { /* * If the ordering operator's return value is anything else, we * don't know how to convert the float8 bound calculated by the * distance function to that. The executor won't actually need * the order by values we return here, if there are no lossy * results, so only insist on converting if the *recheck flag is * set. */ if (scan->xs_recheckorderby) elog(ERROR, "ORDER BY operator must return float8 or float4 if the distance function is lossy"); scan->xs_orderbynulls[i] = true; } } }
/* * Convert a compressed leaf item back to the original type, for index-only * scans. */ GISTENTRY * gbt_num_fetch(GISTENTRY *entry, const gbtree_ninfo *tinfo) { GISTENTRY *retval; Datum datum; Assert(tinfo->indexsize >= 2 * tinfo->size); /* * Get the original Datum from the stored datum. On leaf entries, the * lower and upper bound are the same. We just grab the lower bound and * return it. */ switch (tinfo->t) { case gbt_t_int2: datum = Int16GetDatum(*(int16 *) entry->key); break; case gbt_t_int4: datum = Int32GetDatum(*(int32 *) entry->key); break; case gbt_t_int8: datum = Int64GetDatum(*(int64 *) entry->key); break; case gbt_t_oid: case gbt_t_enum: datum = ObjectIdGetDatum(*(Oid *) entry->key); break; case gbt_t_float4: datum = Float4GetDatum(*(float4 *) entry->key); break; case gbt_t_float8: datum = Float8GetDatum(*(float8 *) entry->key); break; case gbt_t_date: datum = DateADTGetDatum(*(DateADT *) entry->key); break; case gbt_t_time: datum = TimeADTGetDatum(*(TimeADT *) entry->key); break; case gbt_t_ts: datum = TimestampGetDatum(*(Timestamp *) entry->key); break; case gbt_t_cash: datum = CashGetDatum(*(Cash *) entry->key); break; default: datum = PointerGetDatum(entry->key); } retval = palloc(sizeof(GISTENTRY)); gistentryinit(*retval, datum, entry->rel, entry->page, entry->offset, false); return retval; }
/* * Deprecated function. * Use "pg_trgm.similarity_threshold" GUC variable instead of this function. */ Datum set_limit(PG_FUNCTION_ARGS) { float4 nlimit = PG_GETARG_FLOAT4(0); char *nlimit_str; Oid func_out_oid; bool is_varlena; getTypeOutputInfo(FLOAT4OID, &func_out_oid, &is_varlena); nlimit_str = OidOutputFunctionCall(func_out_oid, Float4GetDatum(nlimit)); SetConfigOption("pg_trgm.similarity_threshold", nlimit_str, PGC_USERSET, PGC_S_SESSION); PG_RETURN_FLOAT4(similarity_threshold); }
/* * float4_text - converts a float4 number to a text string */ Datum float4_text(PG_FUNCTION_ARGS) { float4 num = PG_GETARG_FLOAT4(0); text *result; int len; char *str; str = DatumGetCString(DirectFunctionCall1(float4out, Float4GetDatum(num))); len = strlen(str) + VARHDRSZ; result = (text *) palloc(len); VARATT_SIZEP(result) = len; memcpy(VARDATA(result), str, (len - VARHDRSZ)); pfree(str); PG_RETURN_TEXT_P(result); }
/* ---------------------------------------------------------------- * 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); } }
/* * EnumValuesCreate * Create an entry in pg_enum for each of the supplied enum values. * * vals is a list of Value strings. */ void EnumValuesCreate(Oid enumTypeOid, List *vals) { Relation pg_enum; NameData enumlabel; Oid *oids; int elemno, num_elems; Datum values[Natts_pg_enum]; bool nulls[Natts_pg_enum]; ListCell *lc; HeapTuple tup; num_elems = list_length(vals); /* * We do not bother to check the list of values for duplicates --- if you * have any, you'll get a less-than-friendly unique-index violation. It is * probably not worth trying harder. */ pg_enum = heap_open(EnumRelationId, RowExclusiveLock); /* * Allocate OIDs for the enum's members. * * While this method does not absolutely guarantee that we generate no * duplicate OIDs (since we haven't entered each oid into the table before * allocating the next), trouble could only occur if the OID counter wraps * all the way around before we finish. Which seems unlikely. */ oids = (Oid *) palloc(num_elems * sizeof(Oid)); for (elemno = 0; elemno < num_elems; elemno++) { /* * We assign even-numbered OIDs to all the new enum labels. This * tells the comparison functions the OIDs are in the correct sort * order and can be compared directly. */ Oid new_oid; do { new_oid = GetNewOid(pg_enum); } while (new_oid & 1); oids[elemno] = new_oid; } /* sort them, just in case OID counter wrapped from high to low */ qsort(oids, num_elems, sizeof(Oid), oid_cmp); /* and make the entries */ memset(nulls, false, sizeof(nulls)); elemno = 0; foreach(lc, vals) { char *lab = strVal(lfirst(lc)); /* * labels are stored in a name field, for easier syscache lookup, so * check the length to make sure it's within range. */ if (strlen(lab) > (NAMEDATALEN - 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid enum label \"%s\"", lab), errdetail("Labels must be %d characters or less.", NAMEDATALEN - 1))); values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid); values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(elemno + 1); namestrcpy(&enumlabel, lab); values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel); tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls); HeapTupleSetOid(tup, oids[elemno]); simple_heap_insert(pg_enum, tup); CatalogUpdateIndexes(pg_enum, tup); heap_freetuple(tup); elemno++; }
Datum payment(PG_FUNCTION_ARGS) { /* Input variables. */ int32 w_id = PG_GETARG_INT32(0); int32 d_id = PG_GETARG_INT32(1); int32 c_id = PG_GETARG_INT32(2); int32 c_w_id = PG_GETARG_INT32(3); int32 c_d_id = PG_GETARG_INT32(4); text *c_last = PG_GETARG_TEXT_P(5); float4 h_amount = PG_GETARG_FLOAT4(6); TupleDesc tupdesc; SPITupleTable *tuptable; HeapTuple tuple; int ret; char *w_name = NULL; char *w_street_1 = NULL; char *w_street_2 = NULL; char *w_city = NULL; char *w_state = NULL; char *w_zip = NULL; char *d_name = NULL; char *d_street_1 = NULL; char *d_street_2 = NULL; char *d_city = NULL; char *d_state = NULL; char *d_zip = NULL; char *tmp_c_id = NULL; int my_c_id = 0; int count; char *c_first = NULL; char *c_middle = NULL; char *my_c_last = NULL; char *c_street_1 = NULL; char *c_street_2 = NULL; char *c_city = NULL; char *c_state = NULL; char *c_zip = NULL; char *c_phone = NULL; char *c_since = NULL; char *c_credit = NULL; char *c_credit_lim = NULL; char *c_discount = NULL; char *c_balance = NULL; char *c_data = NULL; char *c_ytd_payment = NULL; Datum args[8]; char nulls[8] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; elog(DEBUG1, "w_id = %d", w_id); elog(DEBUG1, "d_id = %d", d_id); elog(DEBUG1, "c_id = %d", c_id); elog(DEBUG1, "c_w_id = %d", c_w_id); elog(DEBUG1, "c_d_id = %d", c_d_id); elog(DEBUG1, "c_last = %s", DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(c_last)))); elog(DEBUG1, "h_amount = %f", h_amount); SPI_connect(); plan_queries(statements); args[0] = Int32GetDatum(w_id); ret = SPI_execute_plan(PAYMENT_1, args, nulls, true, 0); if (ret == SPI_OK_SELECT && SPI_processed > 0) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; tuple = tuptable->vals[0]; w_name = SPI_getvalue(tuple, tupdesc, 1); w_street_1 = SPI_getvalue(tuple, tupdesc, 2); w_street_2 = SPI_getvalue(tuple, tupdesc, 3); w_city = SPI_getvalue(tuple, tupdesc, 4); w_state = SPI_getvalue(tuple, tupdesc, 5); w_zip = SPI_getvalue(tuple, tupdesc, 6); elog(DEBUG1, "w_name = %s", w_name); elog(DEBUG1, "w_street_1 = %s", w_street_1); elog(DEBUG1, "w_street_2 = %s", w_street_2); elog(DEBUG1, "w_city = %s", w_city); elog(DEBUG1, "w_state = %s", w_state); elog(DEBUG1, "w_zip = %s", w_zip); } else { SPI_finish(); PG_RETURN_INT32(-1); } args[0] = Float4GetDatum(h_amount); args[1] = Int32GetDatum(w_id); ret = SPI_execute_plan(PAYMENT_2, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { SPI_finish(); PG_RETURN_INT32(-1); } args[0] = Int32GetDatum(d_id); args[1] = Int32GetDatum(w_id); ret = SPI_execute_plan(PAYMENT_3, args, nulls, true, 0); if (ret == SPI_OK_SELECT && SPI_processed > 0) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; tuple = tuptable->vals[0]; d_name = SPI_getvalue(tuple, tupdesc, 1); d_street_1 = SPI_getvalue(tuple, tupdesc, 2); d_street_2 = SPI_getvalue(tuple, tupdesc, 3); d_city = SPI_getvalue(tuple, tupdesc, 4); d_state = SPI_getvalue(tuple, tupdesc, 5); d_zip = SPI_getvalue(tuple, tupdesc, 6); elog(DEBUG1, "d_name = %s", d_name); elog(DEBUG1, "d_street_1 = %s", d_street_1); elog(DEBUG1, "d_street_2 = %s", d_street_2); elog(DEBUG1, "d_city = %s", d_city); elog(DEBUG1, "d_state = %s", d_state); elog(DEBUG1, "d_zip = %s", d_zip); } else { SPI_finish(); PG_RETURN_INT32(-1); } args[0] = Float4GetDatum(h_amount); args[1] = Int32GetDatum(d_id); args[2] = Int32GetDatum(w_id); ret = SPI_execute_plan(PAYMENT_4, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { SPI_finish(); PG_RETURN_INT32(-1); } if (c_id == 0) { args[0] = Int32GetDatum(w_id); args[1] = Int32GetDatum(d_id); args[2] = PointerGetDatum(c_last); ret = SPI_execute_plan(PAYMENT_5, args, nulls, true, 0); count = SPI_processed; if (ret == SPI_OK_SELECT && SPI_processed > 0) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; tuple = tuptable->vals[count / 2]; tmp_c_id = SPI_getvalue(tuple, tupdesc, 1); elog(DEBUG1, "c_id = %s, %d total, selected %d", tmp_c_id, count, count / 2); my_c_id = atoi(tmp_c_id); } else { SPI_finish(); PG_RETURN_INT32(-1); } } else { my_c_id = c_id; } args[0] = Int32GetDatum(c_w_id); args[1] = Int32GetDatum(c_d_id); args[2] = Int32GetDatum(my_c_id); ret = SPI_execute_plan(PAYMENT_6, args, nulls, true, 0); if (ret == SPI_OK_SELECT && SPI_processed > 0) { tupdesc = SPI_tuptable->tupdesc; tuptable = SPI_tuptable; tuple = tuptable->vals[0]; c_first = SPI_getvalue(tuple, tupdesc, 1); c_middle = SPI_getvalue(tuple, tupdesc, 2); my_c_last = SPI_getvalue(tuple, tupdesc, 3); c_street_1 = SPI_getvalue(tuple, tupdesc, 4); c_street_2 = SPI_getvalue(tuple, tupdesc, 5); c_city = SPI_getvalue(tuple, tupdesc, 6); c_state = SPI_getvalue(tuple, tupdesc, 7); c_zip = SPI_getvalue(tuple, tupdesc, 8); c_phone = SPI_getvalue(tuple, tupdesc, 9); c_since = SPI_getvalue(tuple, tupdesc, 10); c_credit = SPI_getvalue(tuple, tupdesc, 11); c_credit_lim = SPI_getvalue(tuple, tupdesc, 12); c_discount = SPI_getvalue(tuple, tupdesc, 13); c_balance = SPI_getvalue(tuple, tupdesc, 14); c_data = SPI_getvalue(tuple, tupdesc, 15); c_ytd_payment = SPI_getvalue(tuple, tupdesc, 16); elog(DEBUG1, "c_first = %s", c_first); elog(DEBUG1, "c_middle = %s", c_middle); elog(DEBUG1, "c_last = %s", my_c_last); elog(DEBUG1, "c_street_1 = %s", c_street_1); elog(DEBUG1, "c_street_2 = %s", c_street_2); elog(DEBUG1, "c_city = %s", c_city); elog(DEBUG1, "c_state = %s", c_state); elog(DEBUG1, "c_zip = %s", c_zip); elog(DEBUG1, "c_phone = %s", c_phone); elog(DEBUG1, "c_since = %s", c_since); elog(DEBUG1, "c_credit = %s", c_credit); elog(DEBUG1, "c_credit_lim = %s", c_credit_lim); elog(DEBUG1, "c_discount = %s", c_discount); elog(DEBUG1, "c_balance = %s", c_balance); elog(DEBUG1, "c_data = %s", c_data); elog(DEBUG1, "c_ytd_payment = %s", c_ytd_payment); } else { SPI_finish(); PG_RETURN_INT32(-1); } /* It's either "BC" or "GC". */ if (c_credit[0] == 'G') { args[0] = Float4GetDatum(h_amount); args[1] = Int32GetDatum(my_c_id); args[2] = Int32GetDatum(c_w_id); args[3] = Int32GetDatum(c_d_id); ret = SPI_execute_plan(PAYMENT_7_GC, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { SPI_finish(); PG_RETURN_INT32(-1); } } else { char my_c_data[1000]; sprintf(my_c_data, "%d %d %d %d %d %f ", my_c_id, c_d_id, c_w_id, d_id, w_id, h_amount); args[0] = Float4GetDatum(h_amount); args[1] = CStringGetTextDatum(my_c_data); args[2] = Int32GetDatum(my_c_id); args[3] = Int32GetDatum(c_w_id); args[4] = Int32GetDatum(c_d_id); ret = SPI_execute_plan(PAYMENT_7_BC, args, nulls, false, 0); if (ret != SPI_OK_UPDATE) { SPI_finish(); PG_RETURN_INT32(-1); } } args[0] = Int32GetDatum(my_c_id); args[1] = Int32GetDatum(c_d_id); args[2] = Int32GetDatum(c_w_id); args[3] = Int32GetDatum(d_id); args[4] = Int32GetDatum(w_id); args[5] = Float4GetDatum(h_amount); args[6] = CStringGetTextDatum(w_name); args[7] = CStringGetTextDatum(d_name); ret = SPI_execute_plan(PAYMENT_8, args, nulls, false, 0); if (ret != SPI_OK_INSERT) { SPI_finish(); PG_RETURN_INT32(-1); } SPI_finish(); PG_RETURN_INT32(1); }
Datum query_histogram(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; TupleDesc tupdesc; AttInMetadata *attinmeta; histogram_data* data; /* init on the first call */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); data = query_hist_get_data(PG_GETARG_BOOL(0)); /* init (open file, etc.), maybe read all the data in memory * so that the file is not kept open for a long time */ funcctx->user_fctx = data; funcctx->max_calls = data->bins_count; if (data->bins_count > 0) { funcctx->max_calls = data->bins_count + 1; } /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); /* * generate attribute metadata needed later to produce tuples from raw * C strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; funcctx->tuple_desc = tupdesc; /* switch back to the old context */ MemoryContextSwitchTo(oldcontext); } /* init the context */ funcctx = SRF_PERCALL_SETUP(); /* check if we have more data */ if (funcctx->max_calls > funcctx->call_cntr) { HeapTuple tuple; Datum result; Datum values[6]; bool nulls[6]; int binIdx; binIdx = funcctx->call_cntr; data = (histogram_data*)funcctx->user_fctx; memset(nulls, 0, sizeof(nulls)); if (data->histogram_type == HISTOGRAM_LINEAR) { values[0] = UInt32GetDatum(binIdx * data->bins_width); if (funcctx->max_calls - 1 == funcctx->call_cntr) { values[1] = UInt32GetDatum(0); nulls[1] = TRUE; } else { values[1] = UInt32GetDatum((binIdx+1)* data->bins_width); } } else { if (funcctx->call_cntr == 0) { values[0] = UInt32GetDatum(0); } else { values[0] = UInt32GetDatum(pow(2,binIdx-1) * data->bins_width); } if (funcctx->max_calls - 1 == funcctx->call_cntr) { values[1] = UInt32GetDatum(0); nulls[1] = TRUE; } else { values[1] = UInt32GetDatum(pow(2,binIdx) * data->bins_width); } } values[2] = Int64GetDatum(data->count_data[binIdx]); if (data->total_count > 0) { values[3] = Float4GetDatum(100.0*data->count_data[binIdx] / data->total_count); } else { values[3] = Float4GetDatum(0); } values[4] = Float8GetDatum(data->time_data[binIdx]); if (data->total_time > 0) { values[5] = Float4GetDatum(100*data->time_data[binIdx] / data->total_time); } else { values[5] = Float4GetDatum(0); } /* Build and return the tuple. */ tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); /* make the tuple into a datum */ result = HeapTupleGetDatum(tuple); /* Here we want to return another item: */ SRF_RETURN_NEXT(funcctx, result); } else { /* Here we are done returning items and just need to clean up: */ SRF_RETURN_DONE(funcctx); } }
Datum summarize_variant( PG_FUNCTION_ARGS ) { if( PG_ARGISNULL(0) ) { ereport( ERROR, (errmsg("summarize_variant: array of values must be non-null")) ); } TupleDesc tuple_desc; if( get_call_result_type( fcinfo, NULL, &tuple_desc ) != TYPEFUNC_COMPOSITE ) { ereport( ERROR, (errmsg("summarize_variant: function returning composite type called in context that cannot accept composite type")) ); } tuple_desc = BlessTupleDesc( tuple_desc ); ArrayType* values = PG_GETARG_ARRAYTYPE_P( 0 ); Oid values_type = ARR_ELEMTYPE( values ); int16 values_width; bool values_passbyvalue; char values_alignmentcode; Datum* values_content; bool* values_nullflags; int values_length; get_typlenbyvalalign( values_type, &values_width, &values_passbyvalue, &values_alignmentcode ); deconstruct_array( values, values_type, values_width, values_passbyvalue, values_alignmentcode, &values_content, &values_nullflags, &values_length ); size_t composite_type_elements = 9; Datum* results_content = (Datum*)palloc0( sizeof(Datum) * composite_type_elements ); bool* results_nullflags = (bool*)palloc0( sizeof(bool) * composite_type_elements ); // FORMAT // [0] - call rate // [1] - subset call rate // [2] - alternate allele frequency // [3] - sample alternate allele frequency if( !PG_ARGISNULL(1) ) { ArrayType* indices = PG_GETARG_ARRAYTYPE_P(1); Oid indices_type = ARR_ELEMTYPE( indices ); int16 indices_width; bool indices_passbyvalue; char indices_alignmentcode; Datum* indices_content; bool* indices_nullflags; int indices_length; get_typlenbyvalalign( indices_type, &indices_width, &indices_passbyvalue, &indices_alignmentcode ); deconstruct_array( indices, indices_type, indices_width, indices_passbyvalue, indices_alignmentcode, &indices_content, &indices_nullflags, &indices_length ); int count = 0; int nonnull_count = 0; int alternate_count = 0; int i; for( i = 0; i < indices_length; ++i ) { if( !indices_nullflags[i] && indices_content[i] - 1 < (long unsigned)values_length ) { ++count; if( !values_nullflags[ indices_content[i] - 1 ] ) { ++nonnull_count; alternate_count += values_content[ indices_content[i] - 1 ]; } } } results_content[1] = Float4GetDatum( nonnull_count / (float4)count ); results_content[3] = Float4GetDatum( nonnull_count == 0 ? 0 : alternate_count/(2.0*nonnull_count) ); results_nullflags[3] = nonnull_count == 0; } else { results_nullflags[1] = true; results_nullflags[3] = true; } int count = values_length; unsigned int nonnull_count = 0; unsigned int alternate_count = 0; int i; for( i = 0; i < values_length; ++i ) { if( !values_nullflags[i] ) { ++nonnull_count; alternate_count += values_content[i]; } } results_content[0] = Float4GetDatum( nonnull_count / (float4)count ); results_content[2] = Float4GetDatum( nonnull_count == 0 ? 0 : alternate_count/(2.0*nonnull_count) ); results_nullflags[2] = nonnull_count == 0; HeapTuple tuple = heap_form_tuple( tuple_desc, results_content, results_nullflags ); Datum result = HeapTupleGetDatum( tuple ); PG_RETURN_DATUM( result ); }
/* * Fetch next heap tuple in an ordered search */ static bool getNextNearest(IndexScanDesc scan) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; bool res = false; int i; if (scan->xs_itup) { /* free previously returned tuple */ pfree(scan->xs_itup); scan->xs_itup = NULL; } do { GISTSearchItem *item = getNextGISTSearchItem(so); if (!item) break; if (GISTSearchItemIsHeap(*item)) { /* found a heap item at currently minimal distance */ scan->xs_ctup.t_self = item->data.heap.heapPtr; scan->xs_recheck = item->data.heap.recheck; scan->xs_recheckorderby = item->data.heap.recheckDistances; for (i = 0; i < scan->numberOfOrderBys; i++) { if (so->orderByTypes[i] == FLOAT8OID) { #ifndef USE_FLOAT8_BYVAL /* must free any old value to avoid memory leakage */ if (!scan->xs_orderbynulls[i]) pfree(DatumGetPointer(scan->xs_orderbyvals[i])); #endif scan->xs_orderbyvals[i] = Float8GetDatum(item->distances[i]); scan->xs_orderbynulls[i] = false; } else if (so->orderByTypes[i] == FLOAT4OID) { /* convert distance function's result to ORDER BY type */ #ifndef USE_FLOAT4_BYVAL /* must free any old value to avoid memory leakage */ if (!scan->xs_orderbynulls[i]) pfree(DatumGetPointer(scan->xs_orderbyvals[i])); #endif scan->xs_orderbyvals[i] = Float4GetDatum((float4) item->distances[i]); scan->xs_orderbynulls[i] = false; } else { /* * If the ordering operator's return value is anything * else, we don't know how to convert the float8 bound * calculated by the distance function to that. The * executor won't actually need the order by values we * return here, if there are no lossy results, so only * insist on converting if the *recheck flag is set. */ if (scan->xs_recheckorderby) elog(ERROR, "GiST operator family's FOR ORDER BY operator must return float8 or float4 if the distance function is lossy"); scan->xs_orderbynulls[i] = true; } } /* in an index-only scan, also return the reconstructed tuple. */ if (scan->xs_want_itup) scan->xs_itup = item->data.heap.ftup; res = true; } else { /* visit an index page, extract its items into queue */ CHECK_FOR_INTERRUPTS(); gistScanPage(scan, item, item->distances, NULL, NULL); } pfree(item); } while (!res); return res; }
static Datum leftmostvalue_float4(void) { return Float4GetDatum(-get_float4_infinity()); }
/* ---------------------------------------------------------------- * 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); } }