📄 funcapi.c
字号:
/*------------------------------------------------------------------------- * * funcapi.c * Utility and convenience functions for fmgr functions that return * sets and/or composite types. * * Copyright (c) 2002-2005, PostgreSQL Global Development Group * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.26.2.1 2006/01/17 17:33:21 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "funcapi.h"#include "catalog/namespace.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "utils/array.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/syscache.h"#include "utils/typcache.h"static void shutdown_MultiFuncCall(Datum arg);static TypeFuncClass internal_get_result_type(Oid funcid, Node *call_expr, ReturnSetInfo *rsinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc);static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, Node *call_expr);static TypeFuncClass get_type_func_class(Oid typid);/* * init_MultiFuncCall * Create an empty FuncCallContext data structure * and do some other basic Multi-function call setup * and error checking */FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS){ FuncCallContext *retval; /* * Bail if we're called in the wrong context */ if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (fcinfo->flinfo->fn_extra == NULL) { /* * First call */ ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; /* * Allocate suitably long-lived space and zero it */ retval = (FuncCallContext *) MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt, sizeof(FuncCallContext)); /* * initialize the elements */ retval->call_cntr = 0; retval->max_calls = 0; retval->slot = NULL; retval->user_fctx = NULL; retval->attinmeta = NULL; retval->tuple_desc = NULL; retval->multi_call_memory_ctx = fcinfo->flinfo->fn_mcxt; /* * save the pointer for cross-call use */ fcinfo->flinfo->fn_extra = retval; /* * Ensure we will get shut down cleanly if the exprcontext is not run * to completion. */ RegisterExprContextCallback(rsi->econtext, shutdown_MultiFuncCall, PointerGetDatum(fcinfo->flinfo)); } else { /* second and subsequent calls */ elog(ERROR, "init_MultiFuncCall may not be called more than once"); /* never reached, but keep compiler happy */ retval = NULL; } return retval;}/* * per_MultiFuncCall * * Do Multi-function per-call setup */FuncCallContext *per_MultiFuncCall(PG_FUNCTION_ARGS){ FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra; /* * Clear the TupleTableSlot, if present. This is for safety's sake: the * Slot will be in a long-lived context (it better be, if the * FuncCallContext is pointing to it), but in most usage patterns the * tuples stored in it will be in the function's per-tuple context. So at * the beginning of each call, the Slot will hold a dangling pointer to an * already-recycled tuple. We clear it out here. * * Note: use of retval->slot is obsolete as of 8.0, and we expect that it * will always be NULL. This is just here for backwards compatibility in * case someone creates a slot anyway. */ if (retval->slot != NULL) ExecClearTuple(retval->slot); return retval;}/* * end_MultiFuncCall * Clean up after init_MultiFuncCall */voidend_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx){ ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; /* Deregister the shutdown callback */ UnregisterExprContextCallback(rsi->econtext, shutdown_MultiFuncCall, PointerGetDatum(fcinfo->flinfo)); /* But use it to do the real work */ shutdown_MultiFuncCall(PointerGetDatum(fcinfo->flinfo));}/* * shutdown_MultiFuncCall * Shutdown function to clean up after init_MultiFuncCall */static voidshutdown_MultiFuncCall(Datum arg){ FmgrInfo *flinfo = (FmgrInfo *) DatumGetPointer(arg); FuncCallContext *funcctx = (FuncCallContext *) flinfo->fn_extra; /* unbind from flinfo */ flinfo->fn_extra = NULL; /* * Caller is responsible to free up memory for individual struct elements * other than att_in_funcinfo and elements. */ if (funcctx->attinmeta != NULL) pfree(funcctx->attinmeta); pfree(funcctx);}/* * get_call_result_type * Given a function's call info record, determine the kind of datatype * it is supposed to return. If resultTypeId isn't NULL, *resultTypeId * receives the actual datatype OID (this is mainly useful for scalar * result types). If resultTupleDesc isn't NULL, *resultTupleDesc * receives a pointer to a TupleDesc when the result is of a composite * type, or NULL when it's a scalar result. NB: the tupledesc should * be copied if it is to be accessed over a long period. * * One hard case that this handles is resolution of actual rowtypes for * functions returning RECORD (from either the function's OUT parameter * list, or a ReturnSetInfo context node). TYPEFUNC_RECORD is returned * only when we couldn't resolve the actual rowtype for lack of information. * * The other hard case that this handles is resolution of polymorphism. * We will never return ANYELEMENT or ANYARRAY, either as a scalar result * type or as a component of a rowtype. * * This function is relatively expensive --- in a function returning set, * try to call it only the first time through. */TypeFuncClassget_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc){ return internal_get_result_type(fcinfo->flinfo->fn_oid, fcinfo->flinfo->fn_expr, (ReturnSetInfo *) fcinfo->resultinfo, resultTypeId, resultTupleDesc);}/* * get_expr_result_type * As above, but work from a calling expression node tree */TypeFuncClassget_expr_result_type(Node *expr, Oid *resultTypeId, TupleDesc *resultTupleDesc){ TypeFuncClass result; if (expr && IsA(expr, FuncExpr)) result = internal_get_result_type(((FuncExpr *) expr)->funcid, expr, NULL, resultTypeId, resultTupleDesc); else if (expr && IsA(expr, OpExpr)) result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno), expr, NULL, resultTypeId, resultTupleDesc); else { /* handle as a generic expression; no chance to resolve RECORD */ Oid typid = exprType(expr); if (resultTypeId) *resultTypeId = typid; if (resultTupleDesc) *resultTupleDesc = NULL; result = get_type_func_class(typid); if (result == TYPEFUNC_COMPOSITE && resultTupleDesc) *resultTupleDesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typid, -1)); } return result;}/* * get_func_result_type * As above, but work from a function's OID only * * This will not be able to resolve pure-RECORD results nor polymorphism. */TypeFuncClassget_func_result_type(Oid functionId, Oid *resultTypeId, TupleDesc *resultTupleDesc){ return internal_get_result_type(functionId, NULL, NULL, resultTypeId, resultTupleDesc);}/* * internal_get_result_type -- workhorse code implementing all the above * * funcid must always be supplied. call_expr and rsinfo can be NULL if not * available. We will return TYPEFUNC_RECORD, and store NULL into * *resultTupleDesc, if we cannot deduce the complete result rowtype from * the available information. */static TypeFuncClassinternal_get_result_type(Oid funcid, Node *call_expr, ReturnSetInfo *rsinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc){ TypeFuncClass result; HeapTuple tp; Form_pg_proc procform; Oid rettype; TupleDesc tupdesc; /* First fetch the function's pg_proc row to inspect its rettype */ tp = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for function %u", funcid); procform = (Form_pg_proc) GETSTRUCT(tp); rettype = procform->prorettype; /* Check for OUT parameters defining a RECORD result */ tupdesc = build_function_result_tupdesc_t(tp); if (tupdesc) { /* * It has OUT parameters, so it's basically like a regular composite * type, except we have to be able to resolve any polymorphic OUT * parameters. */ if (resultTypeId) *resultTypeId = rettype; if (resolve_polymorphic_tupdesc(tupdesc, &procform->proargtypes, call_expr)) { if (tupdesc->tdtypeid == RECORDOID && tupdesc->tdtypmod < 0) assign_record_type_typmod(tupdesc); if (resultTupleDesc) *resultTupleDesc = tupdesc; result = TYPEFUNC_COMPOSITE; } else { if (resultTupleDesc) *resultTupleDesc = NULL; result = TYPEFUNC_RECORD; } ReleaseSysCache(tp); return result; } /* * If scalar polymorphic result, try to resolve it. */ if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID) { Oid newrettype = exprType(call_expr); if (newrettype == InvalidOid) /* this probably should not happen */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not determine actual result type for function \"%s\" declared to return type %s", NameStr(procform->proname), format_type_be(rettype)))); rettype = newrettype; } if (resultTypeId) *resultTypeId = rettype; if (resultTupleDesc) *resultTupleDesc = NULL; /* default result */ /* Classify the result type */ result = get_type_func_class(rettype); switch (result) { case TYPEFUNC_COMPOSITE: if (resultTupleDesc) *resultTupleDesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(rettype, -1)); /* Named composite types can't have any polymorphic columns */ break; case TYPEFUNC_SCALAR: break; case TYPEFUNC_RECORD: /* We must get the tupledesc from call context */ if (rsinfo && IsA(rsinfo, ReturnSetInfo) && rsinfo->expectedDesc != NULL) { result = TYPEFUNC_COMPOSITE; if (resultTupleDesc) *resultTupleDesc = rsinfo->expectedDesc; /* Assume no polymorphic columns here, either */ } break; default: break; } ReleaseSysCache(tp); return result;}/* * Given the result tuple descriptor for a function with OUT parameters, * replace any polymorphic columns (ANYELEMENT/ANYARRAY) with correct data * types deduced from the input arguments. Returns TRUE if able to deduce * all types, FALSE if not. */static boolresolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, Node *call_expr){ int natts = tupdesc->natts; int nargs = declared_args->dim1; bool have_anyelement_result = false; bool have_anyarray_result = false; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; int i; /* See if there are any polymorphic outputs; quick out if not */ for (i = 0; i < natts; i++) { switch (tupdesc->attrs[i]->atttypid) { case ANYELEMENTOID: have_anyelement_result = true; break; case ANYARRAYOID: have_anyarray_result = true; break; default: break; } } if (!have_anyelement_result && !have_anyarray_result) return true; /* * Otherwise, extract actual datatype(s) from input arguments. (We assume * the parser already validated consistency of the arguments.) */ if (!call_expr) return false; /* no hope */ for (i = 0; i < nargs; i++) { switch (declared_args->values[i]) { case ANYELEMENTOID: if (!OidIsValid(anyelement_type)) anyelement_type = get_call_expr_argtype(call_expr, i); break; case ANYARRAYOID: if (!OidIsValid(anyarray_type)) anyarray_type = get_call_expr_argtype(call_expr, i); break; default: break; } } /* If nothing found, parser messed up */ if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type)) return false; /* If needed, deduce one polymorphic type from the other */ if (have_anyelement_result && !OidIsValid(anyelement_type)) anyelement_type = resolve_generic_type(ANYELEMENTOID, anyarray_type, ANYARRAYOID); if (have_anyarray_result && !OidIsValid(anyarray_type)) anyarray_type = resolve_generic_type(ANYARRAYOID, anyelement_type, ANYELEMENTOID); /* And finally replace the tuple column types as needed */ for (i = 0; i < natts; i++) { switch (tupdesc->attrs[i]->atttypid) { case ANYELEMENTOID: TupleDescInitEntry(tupdesc, i + 1, NameStr(tupdesc->attrs[i]->attname), anyelement_type, -1, 0); break; case ANYARRAYOID: TupleDescInitEntry(tupdesc, i + 1, NameStr(tupdesc->attrs[i]->attname), anyarray_type, -1, 0); break; default: break; } } return true;}/* * Given the declared argument types and modes for a function, * replace any polymorphic types (ANYELEMENT/ANYARRAY) with correct data * types deduced from the input arguments. Returns TRUE if able to deduce * all types, FALSE if not. This is the same logic as * resolve_polymorphic_tupdesc, but with a different argument representation. * * argmodes may be NULL, in which case all arguments are assumed to be IN mode. */boolresolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, Node *call_expr){ bool have_anyelement_result = false; bool have_anyarray_result = false; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; int inargno;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -