/* * build_function_result_tupdesc_t * * Given a pg_proc row for a function, return a tuple descriptor for the * result rowtype, or NULL if the function does not have OUT parameters. * * Note that this does not handle resolution of polymorphic types; * that is deliberate. */ TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple) { Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple); Datum proallargtypes; Datum proargmodes; Datum proargnames; bool isnull; /* Return NULL if the function isn't declared to return RECORD */ if (procform->prorettype != RECORDOID) return NULL; /* If there are no OUT parameters, return NULL */ if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes, NULL) || heap_attisnull(procTuple, Anum_pg_proc_proargmodes, NULL)) return NULL; /* Get the data out of the tuple */ proallargtypes = SysCacheGetAttr(PROCOID, procTuple, Anum_pg_proc_proallargtypes, &isnull); Assert(!isnull); proargmodes = SysCacheGetAttr(PROCOID, procTuple, Anum_pg_proc_proargmodes, &isnull); Assert(!isnull); proargnames = SysCacheGetAttr(PROCOID, procTuple, Anum_pg_proc_proargnames, &isnull); if (isnull) proargnames = PointerGetDatum(NULL); /* just to be sure */ return build_function_result_tupdesc_d(procform->prokind, proallargtypes, proargmodes, proargnames); }
/* * get_func_result_name * * If the function has exactly one output parameter, and that parameter * is named, return the name (as a palloc'd string). Else return NULL. * * This is used to determine the default output column name for functions * returning scalar types. */ char * get_func_result_name(Oid functionId) { char *result; HeapTuple procTuple; Datum proargmodes; Datum proargnames; bool isnull; ArrayType *arr; int numargs; char *argmodes; Datum *argnames; int numoutargs; int nargnames; int i; /* First fetch the function's pg_proc row */ procTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId)); if (!HeapTupleIsValid(procTuple)) elog(ERROR, "cache lookup failed for function %u", functionId); /* If there are no named OUT parameters, return NULL */ if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes) || heap_attisnull(procTuple, Anum_pg_proc_proargnames)) result = NULL; else { /* Get the data out of the tuple */ proargmodes = SysCacheGetAttr(PROCOID, procTuple, Anum_pg_proc_proargmodes, &isnull); Assert(!isnull); proargnames = SysCacheGetAttr(PROCOID, procTuple, Anum_pg_proc_proargnames, &isnull); Assert(!isnull); /* * We expect the arrays to be 1-D arrays of the right types; verify * that. For the char array, we don't need to use deconstruct_array() * since the array data is just going to look like a C array of * values. */ arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ numargs = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || numargs < 0 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != CHAROID) elog(ERROR, "proargmodes is not a 1-D char array"); argmodes = (char *) ARR_DATA_PTR(arr); arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */ if (ARR_NDIM(arr) != 1 || ARR_DIMS(arr)[0] != numargs || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != TEXTOID) elog(ERROR, "proargnames is not a 1-D text array"); deconstruct_array(arr, TEXTOID, -1, false, 'i', &argnames, NULL, &nargnames); Assert(nargnames == numargs); /* scan for output argument(s) */ result = NULL; numoutargs = 0; for (i = 0; i < numargs; i++) { if (argmodes[i] == PROARGMODE_IN || argmodes[i] == PROARGMODE_VARIADIC) continue; Assert(argmodes[i] == PROARGMODE_OUT || argmodes[i] == PROARGMODE_INOUT || argmodes[i] == PROARGMODE_TABLE); if (++numoutargs > 1) { /* multiple out args, so forget it */ result = NULL; break; } result = TextDatumGetCString(argnames[i]); if (result == NULL || result[0] == '\0') { /* Parameter is not named, so forget it */ result = NULL; break; } } } ReleaseSysCache(procTuple); return result; }
int32 RelationPurge(char *relationName, char *absoluteTimeString, char *relativeTimeString) { register i; AbsoluteTime absoluteTime = INVALID_ABSTIME; RelativeTime relativeTime = INVALID_RELTIME; bits8 dateTag; Relation relation; HeapScanDesc scan; static ScanKeyData key[1] = { { 0, Anum_pg_class_relname, F_NAMEEQ } }; Buffer buffer; HeapTuple newTuple, oldTuple; AbsoluteTime currentTime; char *values[Natts_pg_class]; char nulls[Natts_pg_class]; char replace[Natts_pg_class]; Relation idescs[Num_pg_class_indices]; /* * XXX for some reason getmyrelids (in inval.c) barfs when * you heap_replace tuples from these classes. i thought * setheapoverride would fix it but it didn't. for now, * just disallow purge on these classes. */ if (strcmp(RelationRelationName, relationName) == 0 || strcmp(AttributeRelationName, relationName) == 0 || strcmp(AccessMethodRelationName, relationName) == 0 || strcmp(AccessMethodOperatorRelationName, relationName) == 0) { elog(WARN, "%s: cannot purge catalog \"%s\"", cmdname, relationName); } if (PointerIsValid(absoluteTimeString)) { absoluteTime = (int32) nabstimein(absoluteTimeString); absoluteTimeString[0] = '\0'; if (absoluteTime == INVALID_ABSTIME) { elog(NOTICE, "%s: bad absolute time string \"%s\"", cmdname, absoluteTimeString); elog(WARN, "purge not executed"); } } #ifdef PURGEDEBUG elog(DEBUG, "%s: absolute time `%s' is %d.", cmdname, absoluteTimeString, absoluteTime); #endif /* defined(PURGEDEBUG) */ if (PointerIsValid(relativeTimeString)) { if (isreltime(relativeTimeString, NULL, NULL, NULL) != 1) { elog(WARN, "%s: bad relative time string \"%s\"", cmdname, relativeTimeString); } relativeTime = reltimein(relativeTimeString); #ifdef PURGEDEBUG elog(DEBUG, "%s: relative time `%s' is %d.", cmdname, relativeTimeString, relativeTime); #endif /* defined(PURGEDEBUG) */ } /* * Find the RELATION relation tuple for the given relation. */ relation = heap_openr(RelationRelationName); key[0].sk_argument = PointerGetDatum(relationName); fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs); scan = heap_beginscan(relation, 0, NowTimeQual, 1, key); oldTuple = heap_getnext(scan, 0, &buffer); if (!HeapTupleIsValid(oldTuple)) { heap_endscan(scan); heap_close(relation); elog(WARN, "%s: no such relation: %s", cmdname, relationName); return(0); } /* * Dig around in the tuple. */ currentTime = GetCurrentTransactionStartTime(); if (!RelativeTimeIsValid(relativeTime)) { dateTag = ABSOLUTE; if (!AbsoluteTimeIsValid(absoluteTime)) absoluteTime = currentTime; } else if (!AbsoluteTimeIsValid(absoluteTime)) dateTag = RELATIVE; else dateTag = ABSOLUTE | RELATIVE; for (i = 0; i < Natts_pg_class; ++i) { nulls[i] = heap_attisnull(oldTuple, i+1) ? 'n' : ' '; values[i] = NULL; replace[i] = ' '; } if (dateTag & ABSOLUTE) { values[Anum_pg_class_relexpires-1] = (char *) UInt32GetDatum(absoluteTime); replace[Anum_pg_class_relexpires-1] = 'r'; } if (dateTag & RELATIVE) { values[Anum_pg_class_relpreserved-1] = (char *) UInt32GetDatum(relativeTime); replace[Anum_pg_class_relpreserved-1] = 'r'; } /* * Change the RELATION relation tuple for the given relation. */ newTuple = heap_modifytuple(oldTuple, buffer, relation, (Datum*)values, nulls, replace); /* XXX How do you detect an insertion error?? */ (void) heap_replace(relation, &newTuple->t_ctid, newTuple); /* keep the system catalog indices current */ CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); CatalogIndexInsert(idescs, Num_pg_class_indices, relation, newTuple); CatalogCloseIndices(Num_pg_class_indices, idescs); pfree(newTuple); heap_endscan(scan); heap_close(relation); return(1); }