funcapi.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,163 行 · 第 1/3 页
C
1,163 行
/*------------------------------------------------------------------------- * * funcapi.c * Utility and convenience functions for fmgr functions that return * sets and/or composite types. * * Copyright (c) 2002-2008, PostgreSQL Global Development Group * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.37.2.1 2008/02/29 02:49:43 neilc Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/namespace.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "funcapi.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/memutils.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; MemoryContext multi_call_ctx; /* * Create a suitably long-lived context to hold cross-call data */ multi_call_ctx = AllocSetContextCreate(fcinfo->flinfo->fn_mcxt, "SRF multi-call context", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE); /* * Allocate suitably long-lived space and zero it */ retval = (FuncCallContext *) MemoryContextAllocZero(multi_call_ctx, sizeof(FuncCallContext)); /* * initialize the elements */ retval->call_cntr = 0; retval->max_calls = 0; retval->slot = NULL; retval->user_fctx = NULL; retval->attinmeta = NULL; retval->tuple_desc = NULL; retval->multi_call_memory_ctx = multi_call_ctx; /* * save the pointer for cross-call use */ fcinfo->flinfo->fn_extra = retval; /* * Ensure we will get shut down cleanly if the exprcontext is not run * to completion. */ RegisterExprContextCallback(rsi->econtext, shutdown_MultiFuncCall, PointerGetDatum(fcinfo->flinfo)); } else { /* second and subsequent calls */ elog(ERROR, "init_MultiFuncCall cannot be called more than once"); /* never reached, but keep compiler happy */ retval = NULL; } return retval;}/* * 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; /* * Delete context that holds all multi-call data, including the * FuncCallContext itself */ MemoryContextSwitchTo(flinfo->fn_mcxt); MemoryContextDelete(funcctx->multi_call_memory_ctx);}/* * 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. * * 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 polymorphic pseudotypes (ANYELEMENT etc), 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 = lookup_rowtype_tupdesc_copy(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 (IsPolymorphicType(rettype)) { Oid newrettype = exprType(call_expr); if (newrettype == InvalidOid) /* this probably should not happen */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not determine actual result type for function \"%s\" declared to return type %s", NameStr(procform->proname), format_type_be(rettype)))); rettype = newrettype; } if (resultTypeId) *resultTypeId = rettype; if (resultTupleDesc) *resultTupleDesc = NULL; /* default result */ /* Classify the result type */ result = get_type_func_class(rettype); switch (result) { case TYPEFUNC_COMPOSITE: if (resultTupleDesc) *resultTupleDesc = lookup_rowtype_tupdesc_copy(rettype, -1); /* Named composite types can't have any polymorphic columns */ break; case TYPEFUNC_SCALAR: break; case TYPEFUNC_RECORD: /* We must get the tupledesc from call context */ if (rsinfo && IsA(rsinfo, ReturnSetInfo) && rsinfo->expectedDesc != NULL) { result = TYPEFUNC_COMPOSITE; if (resultTupleDesc) *resultTupleDesc = rsinfo->expectedDesc; /* Assume no polymorphic columns here, either */ }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?