PostgreSQL Source Code git master
functions.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/functions.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_func.h"
#include "rewrite/rewriteHandler.h"
#include "storage/proc.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/funccache.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
Include dependency graph for functions.c:

Go to the source code of this file.

Data Structures

struct  DR_sqlfunction
 
struct  execution_state
 
struct  SQLFunctionHashEntry
 
struct  SQLFunctionCache
 

Typedefs

typedef struct execution_state execution_state
 
typedef struct SQLFunctionHashEntry SQLFunctionHashEntry
 
typedef struct SQLFunctionCache SQLFunctionCache
 
typedef SQLFunctionCacheSQLFunctionCachePtr
 

Enumerations

enum  ExecStatus { F_EXEC_START , F_EXEC_RUN , F_EXEC_DONE }
 

Functions

static Nodesql_fn_param_ref (ParseState *pstate, ParamRef *pref)
 
static Nodesql_fn_post_column_ref (ParseState *pstate, ColumnRef *cref, Node *var)
 
static Nodesql_fn_make_param (SQLFunctionParseInfoPtr pinfo, int paramno, int location)
 
static Nodesql_fn_resolve_param_name (SQLFunctionParseInfoPtr pinfo, const char *paramname, int location)
 
static SQLFunctionCacheinit_sql_fcache (FunctionCallInfo fcinfo, bool lazyEvalOK)
 
static bool init_execution_state (SQLFunctionCachePtr fcache)
 
static void prepare_next_query (SQLFunctionHashEntry *func)
 
static void sql_compile_callback (FunctionCallInfo fcinfo, HeapTuple procedureTuple, const CachedFunctionHashKey *hashkey, CachedFunction *cfunc, bool forValidator)
 
static void sql_delete_callback (CachedFunction *cfunc)
 
static void sql_postrewrite_callback (List *querytree_list, void *arg)
 
static void postquel_start (execution_state *es, SQLFunctionCachePtr fcache)
 
static bool postquel_getnext (execution_state *es, SQLFunctionCachePtr fcache)
 
static void postquel_end (execution_state *es, SQLFunctionCachePtr fcache)
 
static void postquel_sub_params (SQLFunctionCachePtr fcache, FunctionCallInfo fcinfo)
 
static Datum postquel_get_single_result (TupleTableSlot *slot, FunctionCallInfo fcinfo, SQLFunctionCachePtr fcache)
 
static void sql_compile_error_callback (void *arg)
 
static void sql_exec_error_callback (void *arg)
 
static void ShutdownSQLFunction (Datum arg)
 
static void RemoveSQLFunctionCache (void *arg)
 
static void check_sql_fn_statement (List *queryTreeList)
 
static bool check_sql_stmt_retval (List *queryTreeList, Oid rettype, TupleDesc rettupdesc, char prokind, bool insertDroppedCols)
 
static bool coerce_fn_result_column (TargetEntry *src_tle, Oid res_type, int32 res_typmod, bool tlist_is_modifiable, List **upper_tlist, bool *upper_tlist_nontrivial)
 
static Listget_sql_fn_result_tlist (List *queryTreeList)
 
static void sqlfunction_startup (DestReceiver *self, int operation, TupleDesc typeinfo)
 
static bool sqlfunction_receive (TupleTableSlot *slot, DestReceiver *self)
 
static void sqlfunction_shutdown (DestReceiver *self)
 
static void sqlfunction_destroy (DestReceiver *self)
 
SQLFunctionParseInfoPtr prepare_sql_fn_parse_info (HeapTuple procedureTuple, Node *call_expr, Oid inputCollation)
 
void sql_fn_parser_setup (struct ParseState *pstate, SQLFunctionParseInfoPtr pinfo)
 
Datum fmgr_sql (PG_FUNCTION_ARGS)
 
void check_sql_fn_statements (List *queryTreeLists)
 
bool check_sql_fn_retval (List *queryTreeLists, Oid rettype, TupleDesc rettupdesc, char prokind, bool insertDroppedCols)
 
DestReceiverCreateSQLFunctionDestReceiver (void)
 

Typedef Documentation

◆ execution_state

◆ SQLFunctionCache

◆ SQLFunctionCachePtr

Definition at line 190 of file functions.c.

◆ SQLFunctionHashEntry

Enumeration Type Documentation

◆ ExecStatus

enum ExecStatus
Enumerator
F_EXEC_START 
F_EXEC_RUN 
F_EXEC_DONE 

Definition at line 62 of file functions.c.

63{
ExecStatus
Definition: functions.c:63
@ F_EXEC_START
Definition: functions.c:64
@ F_EXEC_DONE
Definition: functions.c:64
@ F_EXEC_RUN
Definition: functions.c:64

Function Documentation

◆ check_sql_fn_retval()

bool check_sql_fn_retval ( List queryTreeLists,
Oid  rettype,
TupleDesc  rettupdesc,
char  prokind,
bool  insertDroppedCols 
)

Definition at line 2088 of file functions.c.

2092{
2093 List *queryTreeList;
2094
2095 /*
2096 * We consider only the last sublist of Query nodes, so that only the last
2097 * original statement is a candidate to produce the result. This is a
2098 * change from pre-v18 versions, which would back up to the last statement
2099 * that includes a canSetTag query, thus ignoring any ending statement(s)
2100 * that rewrite to DO INSTEAD NOTHING. That behavior was undocumented and
2101 * there seems no good reason for it, except that it was an artifact of
2102 * the original coding.
2103 *
2104 * If the function body is completely empty, handle that the same as if
2105 * the last query had rewritten to nothing.
2106 */
2107 if (queryTreeLists != NIL)
2108 queryTreeList = llast_node(List, queryTreeLists);
2109 else
2110 queryTreeList = NIL;
2111
2112 return check_sql_stmt_retval(queryTreeList,
2113 rettype, rettupdesc,
2114 prokind, insertDroppedCols);
2115}
static bool check_sql_stmt_retval(List *queryTreeList, Oid rettype, TupleDesc rettupdesc, char prokind, bool insertDroppedCols)
Definition: functions.c:2122
#define NIL
Definition: pg_list.h:68
#define llast_node(type, l)
Definition: pg_list.h:202
Definition: pg_list.h:54

References check_sql_stmt_retval(), llast_node, and NIL.

Referenced by fmgr_sql_validator(), inline_function(), and inline_set_returning_function().

◆ check_sql_fn_statement()

static void check_sql_fn_statement ( List queryTreeList)
static

Definition at line 2024 of file functions.c.

2025{
2026 ListCell *lc;
2027
2028 foreach(lc, queryTreeList)
2029 {
2030 Query *query = lfirst_node(Query, lc);
2031
2032 /*
2033 * Disallow calling procedures with output arguments. The current
2034 * implementation would just throw the output values away, unless the
2035 * statement is the last one. Per SQL standard, we should assign the
2036 * output values by name. By disallowing this here, we preserve an
2037 * opportunity for future improvement.
2038 */
2039 if (query->commandType == CMD_UTILITY &&
2040 IsA(query->utilityStmt, CallStmt))
2041 {
2042 CallStmt *stmt = (CallStmt *) query->utilityStmt;
2043
2044 if (stmt->outargs != NIL)
2045 ereport(ERROR,
2046 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2047 errmsg("calling procedures with output arguments is not supported in SQL functions")));
2048 }
2049 }
2050}
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define stmt
Definition: indent_codes.h:59
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
@ CMD_UTILITY
Definition: nodes.h:276
#define lfirst_node(type, lc)
Definition: pg_list.h:176
CmdType commandType
Definition: parsenodes.h:121
Node * utilityStmt
Definition: parsenodes.h:136

References CMD_UTILITY, Query::commandType, ereport, errcode(), errmsg(), ERROR, if(), IsA, lfirst_node, NIL, stmt, and Query::utilityStmt.

Referenced by check_sql_fn_statements(), prepare_next_query(), and sql_postrewrite_callback().

◆ check_sql_fn_statements()

void check_sql_fn_statements ( List queryTreeLists)

Definition at line 2007 of file functions.c.

2008{
2009 ListCell *lc;
2010
2011 /* We are given a list of sublists of Queries */
2012 foreach(lc, queryTreeLists)
2013 {
2014 List *sublist = lfirst_node(List, lc);
2015
2016 check_sql_fn_statement(sublist);
2017 }
2018}
static void check_sql_fn_statement(List *queryTreeList)
Definition: functions.c:2024

References check_sql_fn_statement(), and lfirst_node.

Referenced by fmgr_sql_validator().

◆ check_sql_stmt_retval()

static bool check_sql_stmt_retval ( List queryTreeList,
Oid  rettype,
TupleDesc  rettupdesc,
char  prokind,
bool  insertDroppedCols 
)
static

Definition at line 2122 of file functions.c.

2125{
2126 bool is_tuple_result = false;
2127 Query *parse;
2128 ListCell *parse_cell;
2129 List *tlist;
2130 int tlistlen;
2131 bool tlist_is_modifiable;
2132 char fn_typtype;
2133 List *upper_tlist = NIL;
2134 bool upper_tlist_nontrivial = false;
2135 ListCell *lc;
2136
2137 /*
2138 * If it's declared to return VOID, we don't care what's in the function.
2139 * (This takes care of procedures with no output parameters, as well.)
2140 */
2141 if (rettype == VOIDOID)
2142 return false;
2143
2144 /*
2145 * Find the last canSetTag query in the list of Query nodes. This isn't
2146 * necessarily the last parsetree, because rule rewriting can insert
2147 * queries after what the user wrote.
2148 */
2149 parse = NULL;
2150 parse_cell = NULL;
2151 foreach(lc, queryTreeList)
2152 {
2153 Query *q = lfirst_node(Query, lc);
2154
2155 if (q->canSetTag)
2156 {
2157 parse = q;
2158 parse_cell = lc;
2159 }
2160 }
2161
2162 /*
2163 * If it's a plain SELECT, it returns whatever the targetlist says.
2164 * Otherwise, if it's INSERT/UPDATE/DELETE/MERGE with RETURNING, it
2165 * returns that. Otherwise, the function return type must be VOID.
2166 *
2167 * Note: eventually replace this test with QueryReturnsTuples? We'd need
2168 * a more general method of determining the output type, though. Also, it
2169 * seems too dangerous to consider FETCH or EXECUTE as returning a
2170 * determinable rowtype, since they depend on relatively short-lived
2171 * entities.
2172 */
2173 if (parse &&
2174 parse->commandType == CMD_SELECT)
2175 {
2176 tlist = parse->targetList;
2177 /* tlist is modifiable unless it's a dummy in a setop query */
2178 tlist_is_modifiable = (parse->setOperations == NULL);
2179 }
2180 else if (parse &&
2181 (parse->commandType == CMD_INSERT ||
2182 parse->commandType == CMD_UPDATE ||
2183 parse->commandType == CMD_DELETE ||
2184 parse->commandType == CMD_MERGE) &&
2185 parse->returningList)
2186 {
2187 tlist = parse->returningList;
2188 /* returningList can always be modified */
2189 tlist_is_modifiable = true;
2190 }
2191 else
2192 {
2193 /* Last statement is a utility command, or it rewrote to nothing */
2194 ereport(ERROR,
2195 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2196 errmsg("return type mismatch in function declared to return %s",
2197 format_type_be(rettype)),
2198 errdetail("Function's final statement must be SELECT or INSERT/UPDATE/DELETE/MERGE RETURNING.")));
2199 return false; /* keep compiler quiet */
2200 }
2201
2202 /*
2203 * OK, check that the targetlist returns something matching the declared
2204 * type, and modify it if necessary. If possible, we insert any coercion
2205 * steps right into the final statement's targetlist. However, that might
2206 * risk changes in the statement's semantics --- we can't safely change
2207 * the output type of a grouping column, for instance. In such cases we
2208 * handle coercions by inserting an extra level of Query that effectively
2209 * just does a projection.
2210 */
2211
2212 /*
2213 * Count the non-junk entries in the result targetlist.
2214 */
2215 tlistlen = ExecCleanTargetListLength(tlist);
2216
2217 fn_typtype = get_typtype(rettype);
2218
2219 if (fn_typtype == TYPTYPE_BASE ||
2220 fn_typtype == TYPTYPE_DOMAIN ||
2221 fn_typtype == TYPTYPE_ENUM ||
2222 fn_typtype == TYPTYPE_RANGE ||
2223 fn_typtype == TYPTYPE_MULTIRANGE)
2224 {
2225 /*
2226 * For scalar-type returns, the target list must have exactly one
2227 * non-junk entry, and its type must be coercible to rettype.
2228 */
2229 TargetEntry *tle;
2230
2231 if (tlistlen != 1)
2232 ereport(ERROR,
2233 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2234 errmsg("return type mismatch in function declared to return %s",
2235 format_type_be(rettype)),
2236 errdetail("Final statement must return exactly one column.")));
2237
2238 /* We assume here that non-junk TLEs must come first in tlists */
2239 tle = (TargetEntry *) linitial(tlist);
2240 Assert(!tle->resjunk);
2241
2242 if (!coerce_fn_result_column(tle, rettype, -1,
2243 tlist_is_modifiable,
2244 &upper_tlist,
2245 &upper_tlist_nontrivial))
2246 ereport(ERROR,
2247 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2248 errmsg("return type mismatch in function declared to return %s",
2249 format_type_be(rettype)),
2250 errdetail("Actual return type is %s.",
2251 format_type_be(exprType((Node *) tle->expr)))));
2252 }
2253 else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID)
2254 {
2255 /*
2256 * Returns a rowtype.
2257 *
2258 * Note that we will not consider a domain over composite to be a
2259 * "rowtype" return type; it goes through the scalar case above. This
2260 * is because we only provide column-by-column implicit casting, and
2261 * will not cast the complete record result. So the only way to
2262 * produce a domain-over-composite result is to compute it as an
2263 * explicit single-column result. The single-composite-column code
2264 * path just below could handle such cases, but it won't be reached.
2265 */
2266 int tupnatts; /* physical number of columns in tuple */
2267 int tuplogcols; /* # of nondeleted columns in tuple */
2268 int colindex; /* physical column index */
2269
2270 /*
2271 * If the target list has one non-junk entry, and that expression has
2272 * or can be coerced to the declared return type, take it as the
2273 * result. This allows, for example, 'SELECT func2()', where func2
2274 * has the same composite return type as the function that's calling
2275 * it. This provision creates some ambiguity --- maybe the expression
2276 * was meant to be the lone field of the composite result --- but it
2277 * works well enough as long as we don't get too enthusiastic about
2278 * inventing coercions from scalar to composite types.
2279 *
2280 * XXX Note that if rettype is RECORD and the expression is of a named
2281 * composite type, or vice versa, this coercion will succeed, whether
2282 * or not the record type really matches. For the moment we rely on
2283 * runtime type checking to catch any discrepancy, but it'd be nice to
2284 * do better at parse time.
2285 *
2286 * We must *not* do this for a procedure, however. Procedures with
2287 * output parameter(s) have rettype RECORD, and the CALL code expects
2288 * to get results corresponding to the list of output parameters, even
2289 * when there's just one parameter that's composite.
2290 */
2291 if (tlistlen == 1 && prokind != PROKIND_PROCEDURE)
2292 {
2293 TargetEntry *tle = (TargetEntry *) linitial(tlist);
2294
2295 Assert(!tle->resjunk);
2296 if (coerce_fn_result_column(tle, rettype, -1,
2297 tlist_is_modifiable,
2298 &upper_tlist,
2299 &upper_tlist_nontrivial))
2300 {
2301 /* Note that we're NOT setting is_tuple_result */
2302 goto tlist_coercion_finished;
2303 }
2304 }
2305
2306 /*
2307 * If the caller didn't provide an expected tupdesc, we can't do any
2308 * further checking. Assume we're returning the whole tuple.
2309 */
2310 if (rettupdesc == NULL)
2311 return true;
2312
2313 /*
2314 * Verify that the targetlist matches the return tuple type. We scan
2315 * the non-resjunk columns, and coerce them if necessary to match the
2316 * datatypes of the non-deleted attributes. For deleted attributes,
2317 * insert NULL result columns if the caller asked for that.
2318 */
2319 tupnatts = rettupdesc->natts;
2320 tuplogcols = 0; /* we'll count nondeleted cols as we go */
2321 colindex = 0;
2322
2323 foreach(lc, tlist)
2324 {
2325 TargetEntry *tle = (TargetEntry *) lfirst(lc);
2326 Form_pg_attribute attr;
2327
2328 /* resjunk columns can simply be ignored */
2329 if (tle->resjunk)
2330 continue;
2331
2332 do
2333 {
2334 colindex++;
2335 if (colindex > tupnatts)
2336 ereport(ERROR,
2337 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2338 errmsg("return type mismatch in function declared to return %s",
2339 format_type_be(rettype)),
2340 errdetail("Final statement returns too many columns.")));
2341 attr = TupleDescAttr(rettupdesc, colindex - 1);
2342 if (attr->attisdropped && insertDroppedCols)
2343 {
2344 Expr *null_expr;
2345
2346 /* The type of the null we insert isn't important */
2347 null_expr = (Expr *) makeConst(INT4OID,
2348 -1,
2349 InvalidOid,
2350 sizeof(int32),
2351 (Datum) 0,
2352 true, /* isnull */
2353 true /* byval */ );
2354 upper_tlist = lappend(upper_tlist,
2355 makeTargetEntry(null_expr,
2356 list_length(upper_tlist) + 1,
2357 NULL,
2358 false));
2359 upper_tlist_nontrivial = true;
2360 }
2361 } while (attr->attisdropped);
2362 tuplogcols++;
2363
2364 if (!coerce_fn_result_column(tle,
2365 attr->atttypid, attr->atttypmod,
2366 tlist_is_modifiable,
2367 &upper_tlist,
2368 &upper_tlist_nontrivial))
2369 ereport(ERROR,
2370 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2371 errmsg("return type mismatch in function declared to return %s",
2372 format_type_be(rettype)),
2373 errdetail("Final statement returns %s instead of %s at column %d.",
2374 format_type_be(exprType((Node *) tle->expr)),
2375 format_type_be(attr->atttypid),
2376 tuplogcols)));
2377 }
2378
2379 /* remaining columns in rettupdesc had better all be dropped */
2380 for (colindex++; colindex <= tupnatts; colindex++)
2381 {
2382 if (!TupleDescCompactAttr(rettupdesc, colindex - 1)->attisdropped)
2383 ereport(ERROR,
2384 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2385 errmsg("return type mismatch in function declared to return %s",
2386 format_type_be(rettype)),
2387 errdetail("Final statement returns too few columns.")));
2388 if (insertDroppedCols)
2389 {
2390 Expr *null_expr;
2391
2392 /* The type of the null we insert isn't important */
2393 null_expr = (Expr *) makeConst(INT4OID,
2394 -1,
2395 InvalidOid,
2396 sizeof(int32),
2397 (Datum) 0,
2398 true, /* isnull */
2399 true /* byval */ );
2400 upper_tlist = lappend(upper_tlist,
2401 makeTargetEntry(null_expr,
2402 list_length(upper_tlist) + 1,
2403 NULL,
2404 false));
2405 upper_tlist_nontrivial = true;
2406 }
2407 }
2408
2409 /* Report that we are returning entire tuple result */
2410 is_tuple_result = true;
2411 }
2412 else
2413 ereport(ERROR,
2414 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2415 errmsg("return type %s is not supported for SQL functions",
2416 format_type_be(rettype))));
2417
2418tlist_coercion_finished:
2419
2420 /*
2421 * If necessary, modify the final Query by injecting an extra Query level
2422 * that just performs a projection. (It'd be dubious to do this to a
2423 * non-SELECT query, but we never have to; RETURNING lists can always be
2424 * modified in-place.)
2425 */
2426 if (upper_tlist_nontrivial)
2427 {
2428 Query *newquery;
2429 List *colnames;
2430 RangeTblEntry *rte;
2431 RangeTblRef *rtr;
2432
2433 Assert(parse->commandType == CMD_SELECT);
2434
2435 /* Most of the upper Query struct can be left as zeroes/nulls */
2436 newquery = makeNode(Query);
2437 newquery->commandType = CMD_SELECT;
2438 newquery->querySource = parse->querySource;
2439 newquery->canSetTag = true;
2440 newquery->targetList = upper_tlist;
2441
2442 /* We need a moderately realistic colnames list for the subquery RTE */
2443 colnames = NIL;
2444 foreach(lc, parse->targetList)
2445 {
2446 TargetEntry *tle = (TargetEntry *) lfirst(lc);
2447
2448 if (tle->resjunk)
2449 continue;
2450 colnames = lappend(colnames,
2451 makeString(tle->resname ? tle->resname : ""));
2452 }
2453
2454 /* Build a suitable RTE for the subquery */
2455 rte = makeNode(RangeTblEntry);
2456 rte->rtekind = RTE_SUBQUERY;
2457 rte->subquery = parse;
2458 rte->eref = rte->alias = makeAlias("*SELECT*", colnames);
2459 rte->lateral = false;
2460 rte->inh = false;
2461 rte->inFromCl = true;
2462 newquery->rtable = list_make1(rte);
2463
2464 rtr = makeNode(RangeTblRef);
2465 rtr->rtindex = 1;
2466 newquery->jointree = makeFromExpr(list_make1(rtr), NULL);
2467
2468 /*
2469 * Make sure the new query is marked as having row security if the
2470 * original one does.
2471 */
2472 newquery->hasRowSecurity = parse->hasRowSecurity;
2473
2474 /* Replace original query in the correct element of the query list */
2475 lfirst(parse_cell) = newquery;
2476 }
2477
2478 return is_tuple_result;
2479}
int32_t int32
Definition: c.h:498
int errdetail(const char *fmt,...)
Definition: elog.c:1204
int ExecCleanTargetListLength(List *targetlist)
Definition: execUtils.c:1186
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
static bool coerce_fn_result_column(TargetEntry *src_tle, Oid res_type, int32 res_typmod, bool tlist_is_modifiable, List **upper_tlist, bool *upper_tlist_nontrivial)
Definition: functions.c:2492
Assert(PointerIsAligned(start, uint64))
List * lappend(List *list, void *datum)
Definition: list.c:339
char get_typtype(Oid typid)
Definition: lsyscache.c:2769
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:438
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:336
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:289
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:350
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
@ CMD_MERGE
Definition: nodes.h:275
@ CMD_INSERT
Definition: nodes.h:273
@ CMD_DELETE
Definition: nodes.h:274
@ CMD_UPDATE
Definition: nodes.h:272
@ CMD_SELECT
Definition: nodes.h:271
#define makeNode(_type_)
Definition: nodes.h:161
@ RTE_SUBQUERY
Definition: parsenodes.h:1027
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define list_make1(x1)
Definition: pg_list.h:212
#define linitial(l)
Definition: pg_list.h:178
uintptr_t Datum
Definition: postgres.h:69
#define InvalidOid
Definition: postgres_ext.h:35
static struct subre * parse(struct vars *v, int stopper, int type, struct state *init, struct state *final)
Definition: regcomp.c:717
Definition: nodes.h:135
FromExpr * jointree
Definition: parsenodes.h:177
List * rtable
Definition: parsenodes.h:170
List * targetList
Definition: parsenodes.h:193
Query * subquery
Definition: parsenodes.h:1118
RTEKind rtekind
Definition: parsenodes.h:1061
Expr * expr
Definition: primnodes.h:2219
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:175
String * makeString(char *str)
Definition: value.c:63

References Assert(), CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_SELECT, CMD_UPDATE, coerce_fn_result_column(), Query::commandType, ereport, errcode(), errdetail(), errmsg(), ERROR, ExecCleanTargetListLength(), TargetEntry::expr, exprType(), format_type_be(), get_typtype(), RangeTblEntry::inh, InvalidOid, Query::jointree, lappend(), lfirst, lfirst_node, linitial, list_length(), list_make1, makeAlias(), makeConst(), makeFromExpr(), makeNode, makeString(), makeTargetEntry(), TupleDescData::natts, NIL, parse(), Query::rtable, RTE_SUBQUERY, RangeTblEntry::rtekind, RangeTblRef::rtindex, RangeTblEntry::subquery, Query::targetList, TupleDescAttr(), and TupleDescCompactAttr().

Referenced by check_sql_fn_retval(), prepare_next_query(), and sql_postrewrite_callback().

◆ coerce_fn_result_column()

static bool coerce_fn_result_column ( TargetEntry src_tle,
Oid  res_type,
int32  res_typmod,
bool  tlist_is_modifiable,
List **  upper_tlist,
bool *  upper_tlist_nontrivial 
)
static

Definition at line 2492 of file functions.c.

2498{
2499 TargetEntry *new_tle;
2500 Expr *new_tle_expr;
2501 Node *cast_result;
2502
2503 /*
2504 * If the TLE has a sortgroupref marking, don't change it, as it probably
2505 * is referenced by ORDER BY, DISTINCT, etc, and changing its type would
2506 * break query semantics. Otherwise, it's safe to modify in-place unless
2507 * the query as a whole has issues with that.
2508 */
2509 if (tlist_is_modifiable && src_tle->ressortgroupref == 0)
2510 {
2511 /* OK to modify src_tle in place, if necessary */
2512 cast_result = coerce_to_target_type(NULL,
2513 (Node *) src_tle->expr,
2514 exprType((Node *) src_tle->expr),
2515 res_type, res_typmod,
2518 -1);
2519 if (cast_result == NULL)
2520 return false;
2521 assign_expr_collations(NULL, cast_result);
2522 src_tle->expr = (Expr *) cast_result;
2523 /* Make a Var referencing the possibly-modified TLE */
2524 new_tle_expr = (Expr *) makeVarFromTargetEntry(1, src_tle);
2525 }
2526 else
2527 {
2528 /* Any casting must happen in the upper tlist */
2529 Var *var = makeVarFromTargetEntry(1, src_tle);
2530
2531 cast_result = coerce_to_target_type(NULL,
2532 (Node *) var,
2533 var->vartype,
2534 res_type, res_typmod,
2537 -1);
2538 if (cast_result == NULL)
2539 return false;
2540 assign_expr_collations(NULL, cast_result);
2541 /* Did the coercion actually do anything? */
2542 if (cast_result != (Node *) var)
2543 *upper_tlist_nontrivial = true;
2544 new_tle_expr = (Expr *) cast_result;
2545 }
2546 new_tle = makeTargetEntry(new_tle_expr,
2547 list_length(*upper_tlist) + 1,
2548 src_tle->resname, false);
2549 *upper_tlist = lappend(*upper_tlist, new_tle);
2550 return true;
2551}
Var * makeVarFromTargetEntry(int varno, TargetEntry *tle)
Definition: makefuncs.c:107
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:78
void assign_expr_collations(ParseState *pstate, Node *expr)
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:753
@ COERCION_ASSIGNMENT
Definition: primnodes.h:732
Index ressortgroupref
Definition: primnodes.h:2225
Definition: primnodes.h:262

References assign_expr_collations(), COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, TargetEntry::expr, exprType(), lappend(), list_length(), makeTargetEntry(), makeVarFromTargetEntry(), and TargetEntry::ressortgroupref.

Referenced by check_sql_stmt_retval().

◆ CreateSQLFunctionDestReceiver()

DestReceiver * CreateSQLFunctionDestReceiver ( void  )

Definition at line 2589 of file functions.c.

2590{
2592
2597 self->pub.mydest = DestSQLFunction;
2598
2599 /* private fields will be set by postquel_start */
2600
2601 return (DestReceiver *) self;
2602}
@ DestSQLFunction
Definition: dest.h:96
static void sqlfunction_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
Definition: functions.c:2608
static bool sqlfunction_receive(TupleTableSlot *slot, DestReceiver *self)
Definition: functions.c:2617
static void sqlfunction_destroy(DestReceiver *self)
Definition: functions.c:2664
static void sqlfunction_shutdown(DestReceiver *self)
Definition: functions.c:2655
void * palloc0(Size size)
Definition: mcxt.c:1975
DestReceiver pub
Definition: functions.c:46
void(* rStartup)(DestReceiver *self, int operation, TupleDesc typeinfo)
Definition: dest.h:121
void(* rShutdown)(DestReceiver *self)
Definition: dest.h:124
bool(* receiveSlot)(TupleTableSlot *slot, DestReceiver *self)
Definition: dest.h:118
void(* rDestroy)(DestReceiver *self)
Definition: dest.h:126
CommandDest mydest
Definition: dest.h:128

References DestSQLFunction, _DestReceiver::mydest, palloc0(), DR_sqlfunction::pub, _DestReceiver::rDestroy, _DestReceiver::receiveSlot, _DestReceiver::rShutdown, _DestReceiver::rStartup, sqlfunction_destroy(), sqlfunction_receive(), sqlfunction_shutdown(), and sqlfunction_startup().

Referenced by CreateDestReceiver().

◆ fmgr_sql()

Datum fmgr_sql ( PG_FUNCTION_ARGS  )

Definition at line 1554 of file functions.c.

1555{
1556 SQLFunctionCachePtr fcache;
1557 ErrorContextCallback sqlerrcontext;
1558 MemoryContext tscontext;
1559 bool randomAccess;
1560 bool lazyEvalOK;
1561 bool pushed_snapshot;
1562 execution_state *es;
1563 TupleTableSlot *slot;
1564 Datum result;
1565
1566 /* Check call context */
1567 if (fcinfo->flinfo->fn_retset)
1568 {
1569 ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
1570
1571 /*
1572 * For simplicity, we require callers to support both set eval modes.
1573 * There are cases where we must use one or must use the other, and
1574 * it's not really worthwhile to postpone the check till we know. But
1575 * note we do not require caller to provide an expectedDesc.
1576 */
1577 if (!rsi || !IsA(rsi, ReturnSetInfo) ||
1578 (rsi->allowedModes & SFRM_ValuePerCall) == 0 ||
1579 (rsi->allowedModes & SFRM_Materialize) == 0)
1580 ereport(ERROR,
1581 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1582 errmsg("set-valued function called in context that cannot accept a set")));
1583 randomAccess = rsi->allowedModes & SFRM_Materialize_Random;
1584 lazyEvalOK = !(rsi->allowedModes & SFRM_Materialize_Preferred);
1585 /* tuplestore, if used, must have query lifespan */
1586 tscontext = rsi->econtext->ecxt_per_query_memory;
1587 }
1588 else
1589 {
1590 randomAccess = false;
1591 lazyEvalOK = true;
1592 /* we won't need a tuplestore */
1593 tscontext = NULL;
1594 }
1595
1596 /*
1597 * Initialize fcache if starting a fresh execution.
1598 */
1599 fcache = init_sql_fcache(fcinfo, lazyEvalOK);
1600
1601 /* Remember info that we might need later to construct tuplestore */
1602 fcache->tscontext = tscontext;
1603 fcache->randomAccess = randomAccess;
1604
1605 /*
1606 * Now we can set up error traceback support for ereport()
1607 */
1608 sqlerrcontext.callback = sql_exec_error_callback;
1609 sqlerrcontext.arg = fcache;
1610 sqlerrcontext.previous = error_context_stack;
1611 error_context_stack = &sqlerrcontext;
1612
1613 /*
1614 * Find first unfinished execution_state. If none, advance to the next
1615 * query in function.
1616 */
1617 do
1618 {
1619 es = fcache->eslist;
1620 while (es && es->status == F_EXEC_DONE)
1621 es = es->next;
1622 if (es)
1623 break;
1624 } while (init_execution_state(fcache));
1625
1626 /*
1627 * Execute each command in the function one after another until we either
1628 * run out of commands or get a result row from a lazily-evaluated SELECT.
1629 *
1630 * Notes about snapshot management:
1631 *
1632 * In a read-only function, we just use the surrounding query's snapshot.
1633 *
1634 * In a non-read-only function, we rely on the fact that we'll never
1635 * suspend execution between queries of the function: the only reason to
1636 * suspend execution before completion is if we are returning a row from a
1637 * lazily-evaluated SELECT. So, when first entering this loop, we'll
1638 * either start a new query (and push a fresh snapshot) or re-establish
1639 * the active snapshot from the existing query descriptor. If we need to
1640 * start a new query in a subsequent execution of the loop, either we need
1641 * a fresh snapshot (and pushed_snapshot is false) or the existing
1642 * snapshot is on the active stack and we can just bump its command ID.
1643 */
1644 pushed_snapshot = false;
1645 while (es)
1646 {
1647 bool completed;
1648
1649 if (es->status == F_EXEC_START)
1650 {
1651 /*
1652 * If not read-only, be sure to advance the command counter for
1653 * each command, so that all work to date in this transaction is
1654 * visible. Take a new snapshot if we don't have one yet,
1655 * otherwise just bump the command ID in the existing snapshot.
1656 */
1657 if (!fcache->func->readonly_func)
1658 {
1660 if (!pushed_snapshot)
1661 {
1663 pushed_snapshot = true;
1664 }
1665 else
1667 }
1668
1669 postquel_start(es, fcache);
1670 }
1671 else if (!fcache->func->readonly_func && !pushed_snapshot)
1672 {
1673 /* Re-establish active snapshot when re-entering function */
1675 pushed_snapshot = true;
1676 }
1677
1678 completed = postquel_getnext(es, fcache);
1679
1680 /*
1681 * If we ran the command to completion, we can shut it down now. Any
1682 * row(s) we need to return are safely stashed in the result slot or
1683 * tuplestore, and we want to be sure that, for example, AFTER
1684 * triggers get fired before we return anything. Also, if the
1685 * function doesn't return set, we can shut it down anyway because it
1686 * must be a SELECT and we don't care about fetching any more result
1687 * rows.
1688 */
1689 if (completed || !fcache->func->returnsSet)
1690 postquel_end(es, fcache);
1691
1692 /*
1693 * Break from loop if we didn't shut down (implying we got a
1694 * lazily-evaluated row). Otherwise we'll press on till the whole
1695 * function is done, relying on the tuplestore to keep hold of the
1696 * data to eventually be returned. This is necessary since an
1697 * INSERT/UPDATE/DELETE RETURNING that sets the result might be
1698 * followed by additional rule-inserted commands, and we want to
1699 * finish doing all those commands before we return anything.
1700 */
1701 if (es->status != F_EXEC_DONE)
1702 break;
1703
1704 /*
1705 * Advance to next execution_state, and perhaps next query.
1706 */
1707 es = es->next;
1708 while (!es)
1709 {
1710 /*
1711 * Flush the current snapshot so that we will take a new one for
1712 * the new query list. This ensures that new snaps are taken at
1713 * original-query boundaries, matching the behavior of interactive
1714 * execution.
1715 */
1716 if (pushed_snapshot)
1717 {
1719 pushed_snapshot = false;
1720 }
1721
1722 if (!init_execution_state(fcache))
1723 break; /* end of function */
1724
1725 es = fcache->eslist;
1726 }
1727 }
1728
1729 /*
1730 * The result slot or tuplestore now contains whatever row(s) we are
1731 * supposed to return.
1732 */
1733 if (fcache->func->returnsSet)
1734 {
1735 ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
1736
1737 if (es)
1738 {
1739 /*
1740 * If we stopped short of being done, we must have a lazy-eval
1741 * row.
1742 */
1743 Assert(es->lazyEval);
1744 /* The junkfilter's result slot contains the query result tuple */
1745 Assert(fcache->junkFilter);
1746 slot = fcache->junkFilter->jf_resultSlot;
1747 Assert(!TTS_EMPTY(slot));
1748 /* Extract the result as a datum, and copy out from the slot */
1749 result = postquel_get_single_result(slot, fcinfo, fcache);
1750
1751 /*
1752 * Let caller know we're not finished.
1753 */
1755
1756 /*
1757 * Ensure we will get shut down cleanly if the exprcontext is not
1758 * run to completion.
1759 */
1760 if (!fcache->shutdown_reg)
1761 {
1764 PointerGetDatum(fcache));
1765 fcache->shutdown_reg = true;
1766 }
1767 }
1768 else if (fcache->lazyEval)
1769 {
1770 /*
1771 * We are done with a lazy evaluation. Let caller know we're
1772 * finished.
1773 */
1774 rsi->isDone = ExprEndResult;
1775
1776 fcinfo->isnull = true;
1777 result = (Datum) 0;
1778
1779 /* Deregister shutdown callback, if we made one */
1780 if (fcache->shutdown_reg)
1781 {
1784 PointerGetDatum(fcache));
1785 fcache->shutdown_reg = false;
1786 }
1787 }
1788 else
1789 {
1790 /*
1791 * We are done with a non-lazy evaluation. Return whatever is in
1792 * the tuplestore. (It is now caller's responsibility to free the
1793 * tuplestore when done.)
1794 *
1795 * Note an edge case: we could get here without having made a
1796 * tuplestore if the function is declared to return SETOF VOID.
1797 * ExecMakeTableFunctionResult will cope with null setResult.
1798 */
1799 Assert(fcache->tstore || fcache->func->rettype == VOIDOID);
1801 rsi->setResult = fcache->tstore;
1802 fcache->tstore = NULL;
1803 /* must copy desc because execSRF.c will free it */
1804 if (fcache->junkFilter)
1806
1807 fcinfo->isnull = true;
1808 result = (Datum) 0;
1809
1810 /* Deregister shutdown callback, if we made one */
1811 if (fcache->shutdown_reg)
1812 {
1815 PointerGetDatum(fcache));
1816 fcache->shutdown_reg = false;
1817 }
1818 }
1819 }
1820 else
1821 {
1822 /*
1823 * Non-set function. If we got a row, return it; else return NULL.
1824 */
1825 if (fcache->junkFilter)
1826 {
1827 /* The junkfilter's result slot contains the query result tuple */
1828 slot = fcache->junkFilter->jf_resultSlot;
1829 if (!TTS_EMPTY(slot))
1830 result = postquel_get_single_result(slot, fcinfo, fcache);
1831 else
1832 {
1833 fcinfo->isnull = true;
1834 result = (Datum) 0;
1835 }
1836 }
1837 else
1838 {
1839 /* Should only get here for VOID functions and procedures */
1840 Assert(fcache->func->rettype == VOIDOID);
1841 fcinfo->isnull = true;
1842 result = (Datum) 0;
1843 }
1844 }
1845
1846 /* Pop snapshot if we have pushed one */
1847 if (pushed_snapshot)
1849
1850 /*
1851 * If we've gone through every command in the function, we are done. Reset
1852 * state to start over again on next call.
1853 */
1854 if (es == NULL)
1855 fcache->eslist = NULL;
1856
1857 error_context_stack = sqlerrcontext.previous;
1858
1859 return result;
1860}
ErrorContextCallback * error_context_stack
Definition: elog.c:95
void UnregisterExprContextCallback(ExprContext *econtext, ExprContextCallbackFunction function, Datum arg)
Definition: execUtils.c:990
void RegisterExprContextCallback(ExprContext *econtext, ExprContextCallbackFunction function, Datum arg)
Definition: execUtils.c:964
@ ExprMultipleResult
Definition: execnodes.h:323
@ ExprEndResult
Definition: execnodes.h:324
@ SFRM_Materialize_Preferred
Definition: execnodes.h:338
@ SFRM_ValuePerCall
Definition: execnodes.h:335
@ SFRM_Materialize_Random
Definition: execnodes.h:337
@ SFRM_Materialize
Definition: execnodes.h:336
static Datum postquel_get_single_result(TupleTableSlot *slot, FunctionCallInfo fcinfo, SQLFunctionCachePtr fcache)
Definition: functions.c:1514
static bool postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
Definition: functions.c:1378
static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
Definition: functions.c:1252
static bool init_execution_state(SQLFunctionCachePtr fcache)
Definition: functions.c:629
static void postquel_end(execution_state *es, SQLFunctionCachePtr fcache)
Definition: functions.c:1419
static void sql_exec_error_callback(void *arg)
Definition: functions.c:1901
static void ShutdownSQLFunction(Datum arg)
Definition: functions.c:1939
static SQLFunctionCache * init_sql_fcache(FunctionCallInfo fcinfo, bool lazyEvalOK)
Definition: functions.c:534
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:271
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:669
void UpdateActiveSnapshotCommandId(void)
Definition: snapmgr.c:731
void PopActiveSnapshot(void)
Definition: snapmgr.c:762
struct ErrorContextCallback * previous
Definition: elog.h:296
void(* callback)(void *arg)
Definition: elog.h:297
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:275
TupleDesc jf_cleanTupType
Definition: execnodes.h:414
TupleTableSlot * jf_resultSlot
Definition: execnodes.h:416
Snapshot snapshot
Definition: execdesc.h:40
SetFunctionReturnMode returnMode
Definition: execnodes.h:355
ExprContext * econtext
Definition: execnodes.h:351
TupleDesc setDesc
Definition: execnodes.h:359
Tuplestorestate * setResult
Definition: execnodes.h:358
int allowedModes
Definition: execnodes.h:353
ExprDoneCond isDone
Definition: execnodes.h:356
execution_state * eslist
Definition: functions.c:173
SQLFunctionHashEntry * func
Definition: functions.c:143
JunkFilter * junkFilter
Definition: functions.c:156
Tuplestorestate * tstore
Definition: functions.c:153
MemoryContext tscontext
Definition: functions.c:154
ExecStatus status
Definition: functions.c:70
struct execution_state * next
Definition: functions.c:69
QueryDesc * qd
Definition: functions.c:74
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:245
#define TTS_EMPTY(slot)
Definition: tuptable.h:96
void CommandCounterIncrement(void)
Definition: xact.c:1100

References ReturnSetInfo::allowedModes, ErrorContextCallback::arg, Assert(), ErrorContextCallback::callback, CommandCounterIncrement(), CreateTupleDescCopy(), ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ereport, errcode(), errmsg(), ERROR, error_context_stack, SQLFunctionCache::eslist, ExprEndResult, ExprMultipleResult, F_EXEC_DONE, F_EXEC_START, SQLFunctionCache::func, GetTransactionSnapshot(), if(), init_execution_state(), init_sql_fcache(), IsA, ReturnSetInfo::isDone, JunkFilter::jf_cleanTupType, JunkFilter::jf_resultSlot, SQLFunctionCache::junkFilter, execution_state::lazyEval, SQLFunctionCache::lazyEval, execution_state::next, PointerGetDatum(), PopActiveSnapshot(), postquel_end(), postquel_get_single_result(), postquel_getnext(), postquel_start(), ErrorContextCallback::previous, PushActiveSnapshot(), execution_state::qd, SQLFunctionCache::randomAccess, SQLFunctionHashEntry::readonly_func, RegisterExprContextCallback(), SQLFunctionHashEntry::rettype, ReturnSetInfo::returnMode, SQLFunctionHashEntry::returnsSet, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Preferred, SFRM_Materialize_Random, SFRM_ValuePerCall, SQLFunctionCache::shutdown_reg, ShutdownSQLFunction(), QueryDesc::snapshot, sql_exec_error_callback(), execution_state::status, SQLFunctionCache::tscontext, SQLFunctionCache::tstore, TTS_EMPTY, UnregisterExprContextCallback(), and UpdateActiveSnapshotCommandId().

Referenced by fmgr_info_cxt_security().

◆ get_sql_fn_result_tlist()

static List * get_sql_fn_result_tlist ( List queryTreeList)
static

Definition at line 2558 of file functions.c.

2559{
2560 Query *parse = NULL;
2561 ListCell *lc;
2562
2563 foreach(lc, queryTreeList)
2564 {
2565 Query *q = lfirst_node(Query, lc);
2566
2567 if (q->canSetTag)
2568 parse = q;
2569 }
2570 if (parse &&
2571 parse->commandType == CMD_SELECT)
2572 return parse->targetList;
2573 else if (parse &&
2574 (parse->commandType == CMD_INSERT ||
2575 parse->commandType == CMD_UPDATE ||
2576 parse->commandType == CMD_DELETE ||
2577 parse->commandType == CMD_MERGE) &&
2578 parse->returningList)
2579 return parse->returningList;
2580 else
2581 return NIL;
2582}

References CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_SELECT, CMD_UPDATE, lfirst_node, NIL, and parse().

Referenced by init_execution_state().

◆ init_execution_state()

static bool init_execution_state ( SQLFunctionCachePtr  fcache)
static

Definition at line 629 of file functions.c.

630{
631 CachedPlanSource *plansource;
632 execution_state *preves = NULL;
633 execution_state *lasttages = NULL;
634 int nstmts;
635 ListCell *lc;
636
637 /*
638 * Clean up after previous query, if there was one.
639 */
640 if (fcache->cplan)
641 {
642 ReleaseCachedPlan(fcache->cplan, fcache->cowner);
643 fcache->cplan = NULL;
644 }
645 fcache->eslist = NULL;
646
647 /*
648 * Get the next CachedPlanSource, or stop if there are no more. We might
649 * need to create the next CachedPlanSource; if so, advance
650 * error_query_index first, so that errors detected in prepare_next_query
651 * are blamed on the right statement.
652 */
653 if (fcache->next_query_index >= list_length(fcache->func->plansource_list))
654 {
655 if (fcache->next_query_index >= fcache->func->num_queries)
656 return false;
657 fcache->error_query_index++;
658 prepare_next_query(fcache->func);
659 }
660 else
661 fcache->error_query_index++;
662
663 plansource = (CachedPlanSource *) list_nth(fcache->func->plansource_list,
664 fcache->next_query_index);
665 fcache->next_query_index++;
666
667 /*
668 * Generate plans for the query or queries within this CachedPlanSource.
669 * Register the CachedPlan with the current resource owner. (Saving
670 * cowner here is mostly paranoia, but this way we needn't assume that
671 * CurrentResourceOwner will be the same when ShutdownSQLFunction runs.)
672 */
674 fcache->cplan = GetCachedPlan(plansource,
675 fcache->paramLI,
676 fcache->cowner,
677 NULL);
678
679 /*
680 * If necessary, make esarray[] bigger to hold the needed state.
681 */
682 nstmts = list_length(fcache->cplan->stmt_list);
683 if (nstmts > fcache->esarray_len)
684 {
685 if (fcache->esarray == NULL)
686 fcache->esarray = (execution_state *)
688 sizeof(execution_state) * nstmts);
689 else
690 fcache->esarray = repalloc_array(fcache->esarray,
691 execution_state, nstmts);
692 fcache->esarray_len = nstmts;
693 }
694
695 /*
696 * Build execution_state list to match the number of contained plans.
697 */
698 foreach(lc, fcache->cplan->stmt_list)
699 {
701 execution_state *newes;
702
703 /*
704 * Precheck all commands for validity in a function. This should
705 * generally match the restrictions spi.c applies.
706 */
707 if (stmt->commandType == CMD_UTILITY)
708 {
709 if (IsA(stmt->utilityStmt, CopyStmt) &&
710 ((CopyStmt *) stmt->utilityStmt)->filename == NULL)
712 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
713 errmsg("cannot COPY to/from client in an SQL function")));
714
715 if (IsA(stmt->utilityStmt, TransactionStmt))
717 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
718 /* translator: %s is a SQL statement name */
719 errmsg("%s is not allowed in an SQL function",
720 CreateCommandName(stmt->utilityStmt))));
721 }
722
723 if (fcache->func->readonly_func && !CommandIsReadOnly(stmt))
725 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
726 /* translator: %s is a SQL statement name */
727 errmsg("%s is not allowed in a non-volatile function",
729
730 /* OK, build the execution_state for this query */
731 newes = &fcache->esarray[foreach_current_index(lc)];
732 if (preves)
733 preves->next = newes;
734 else
735 fcache->eslist = newes;
736
737 newes->next = NULL;
738 newes->status = F_EXEC_START;
739 newes->setsResult = false; /* might change below */
740 newes->lazyEval = false; /* might change below */
741 newes->stmt = stmt;
742 newes->qd = NULL;
743
744 if (stmt->canSetTag)
745 lasttages = newes;
746
747 preves = newes;
748 }
749
750 /*
751 * If this isn't the last CachedPlanSource, we're done here. Otherwise,
752 * we need to prepare information about how to return the results.
753 */
754 if (fcache->next_query_index < fcache->func->num_queries)
755 return true;
756
757 /*
758 * Construct a JunkFilter we can use to coerce the returned rowtype to the
759 * desired form, unless the result type is VOID, in which case there's
760 * nothing to coerce to. (XXX Frequently, the JunkFilter isn't doing
761 * anything very interesting, but much of this module expects it to be
762 * there anyway.)
763 *
764 * Normally we can re-use the JunkFilter across executions, but if the
765 * plan for the last CachedPlanSource changed, we'd better rebuild it.
766 *
767 * The JunkFilter, its result slot, and its tupledesc are kept in a
768 * subsidiary memory context so that we can free them easily when needed.
769 */
770 if (fcache->func->rettype != VOIDOID &&
771 (fcache->junkFilter == NULL ||
772 fcache->jf_generation != fcache->cplan->generation))
773 {
774 TupleTableSlot *slot;
775 List *resulttlist;
776 MemoryContext oldcontext;
777
778 /* Create or reset the jfcontext */
779 if (fcache->jfcontext == NULL)
780 fcache->jfcontext = AllocSetContextCreate(fcache->fcontext,
781 "SQL function junkfilter",
783 else
785 oldcontext = MemoryContextSwitchTo(fcache->jfcontext);
786
788
789 /*
790 * Re-fetch the (possibly modified) output tlist of the final
791 * statement. By this point, we should have thrown an error if there
792 * is not one.
793 */
794 resulttlist = get_sql_fn_result_tlist(plansource->query_list);
795
796 /*
797 * If the result is composite, *and* we are returning the whole tuple
798 * result, we need to insert nulls for any dropped columns. In the
799 * single-column-result case, there might be dropped columns within
800 * the composite column value, but it's not our problem here. There
801 * should be no resjunk entries in resulttlist, so in the second case
802 * the JunkFilter is certainly a no-op.
803 */
804 if (fcache->func->rettupdesc && fcache->func->returnsTuple)
805 fcache->junkFilter = ExecInitJunkFilterConversion(resulttlist,
806 fcache->func->rettupdesc,
807 slot);
808 else
809 fcache->junkFilter = ExecInitJunkFilter(resulttlist, slot);
810
811 /*
812 * The resulttlist tree belongs to the plancache and might disappear
813 * underneath us due to plancache invalidation. While we could
814 * forestall that by copying it, that'd just be a waste of cycles,
815 * because the junkfilter doesn't need it anymore. (It'd only be used
816 * by ExecFindJunkAttribute(), which we don't use here.) To ensure
817 * there's not a dangling pointer laying about, clear the junkFilter's
818 * pointer.
819 */
820 fcache->junkFilter->jf_targetList = NIL;
821
822 /* Make sure output rowtype is properly blessed */
823 if (fcache->func->returnsTuple)
825
826 /* Mark the JunkFilter as up-to-date */
827 fcache->jf_generation = fcache->cplan->generation;
828
829 MemoryContextSwitchTo(oldcontext);
830 }
831
832 if (fcache->func->returnsSet &&
833 !fcache->func->returnsTuple &&
834 type_is_rowtype(fcache->func->rettype))
835 {
836 /*
837 * Returning rowtype as if it were scalar --- materialize won't work.
838 * Right now it's sufficient to override any caller preference for
839 * materialize mode, but this might need more work in future.
840 */
841 fcache->lazyEvalOK = true;
842 }
843
844 /*
845 * Mark the last canSetTag query as delivering the function result; then,
846 * if it is a plain SELECT, mark it for lazy evaluation. If it's not a
847 * SELECT we must always run it to completion.
848 *
849 * Note: at some point we might add additional criteria for whether to use
850 * lazy eval. However, we should prefer to use it whenever the function
851 * doesn't return set, since fetching more than one row is useless in that
852 * case.
853 *
854 * Note: don't set setsResult if the function returns VOID, as evidenced
855 * by not having made a junkfilter. This ensures we'll throw away any
856 * output from the last statement in such a function.
857 */
858 if (lasttages && fcache->junkFilter)
859 {
860 lasttages->setsResult = true;
861 if (fcache->lazyEvalOK &&
862 lasttages->stmt->commandType == CMD_SELECT &&
863 !lasttages->stmt->hasModifyingCTE)
864 fcache->lazyEval = lasttages->lazyEval = true;
865 }
866
867 return true;
868}
JunkFilter * ExecInitJunkFilterConversion(List *targetList, TupleDesc cleanTupType, TupleTableSlot *slot)
Definition: execJunk.c:137
JunkFilter * ExecInitJunkFilter(List *targetList, TupleTableSlot *slot)
Definition: execJunk.c:60
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2260
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1427
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:86
#define repalloc_array(pointer, type, count)
Definition: fe_memutils.h:78
static void prepare_next_query(SQLFunctionHashEntry *func)
Definition: functions.c:876
static List * get_sql_fn_result_tlist(List *queryTreeList)
Definition: functions.c:2558
bool type_is_rowtype(Oid typid)
Definition: lsyscache.c:2795
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1260
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:414
#define AllocSetContextCreate
Definition: memutils.h:149
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:190
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
CachedPlan * GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams, ResourceOwner owner, QueryEnvironment *queryEnv)
Definition: plancache.c:1422
void ReleaseCachedPlan(CachedPlan *plan, ResourceOwner owner)
Definition: plancache.c:1547
ResourceOwner CurrentResourceOwner
Definition: resowner.c:173
List * query_list
Definition: plancache.h:125
int generation
Definition: plancache.h:174
List * stmt_list
Definition: plancache.h:165
List * jf_targetList
Definition: execnodes.h:413
bool hasModifyingCTE
Definition: plannodes.h:65
CmdType commandType
Definition: plannodes.h:53
ParamListInfo paramLI
Definition: functions.c:151
MemoryContext fcontext
Definition: functions.c:180
execution_state * esarray
Definition: functions.c:174
ResourceOwner cowner
Definition: functions.c:170
CachedPlan * cplan
Definition: functions.c:169
MemoryContext jfcontext
Definition: functions.c:182
TupleDesc rettupdesc
Definition: functions.c:129
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:123
PlannedStmt * stmt
Definition: functions.c:73
bool CommandIsReadOnly(PlannedStmt *pstmt)
Definition: utility.c:94
static const char * CreateCommandName(Node *parsetree)
Definition: utility.h:103

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, BlessTupleDesc(), CMD_SELECT, CMD_UTILITY, CommandIsReadOnly(), PlannedStmt::commandType, SQLFunctionCache::cowner, SQLFunctionCache::cplan, CreateCommandName(), CurrentResourceOwner, ereport, errcode(), errmsg(), ERROR, SQLFunctionCache::error_query_index, SQLFunctionCache::esarray, SQLFunctionCache::esarray_len, SQLFunctionCache::eslist, ExecInitJunkFilter(), ExecInitJunkFilterConversion(), F_EXEC_START, SQLFunctionCache::fcontext, foreach_current_index, SQLFunctionCache::func, CachedPlan::generation, get_sql_fn_result_tlist(), GetCachedPlan(), PlannedStmt::hasModifyingCTE, IsA, SQLFunctionCache::jf_generation, JunkFilter::jf_resultSlot, JunkFilter::jf_targetList, SQLFunctionCache::jfcontext, SQLFunctionCache::junkFilter, execution_state::lazyEval, SQLFunctionCache::lazyEval, SQLFunctionCache::lazyEvalOK, lfirst_node, list_length(), list_nth(), MakeSingleTupleTableSlot(), MemoryContextAlloc(), MemoryContextReset(), MemoryContextSwitchTo(), execution_state::next, SQLFunctionCache::next_query_index, NIL, SQLFunctionHashEntry::num_queries, SQLFunctionCache::paramLI, SQLFunctionHashEntry::plansource_list, prepare_next_query(), execution_state::qd, CachedPlanSource::query_list, SQLFunctionHashEntry::readonly_func, ReleaseCachedPlan(), repalloc_array, SQLFunctionHashEntry::rettupdesc, SQLFunctionHashEntry::rettype, SQLFunctionHashEntry::returnsSet, SQLFunctionHashEntry::returnsTuple, execution_state::setsResult, execution_state::status, execution_state::stmt, stmt, CachedPlan::stmt_list, TupleTableSlot::tts_tupleDescriptor, TTSOpsMinimalTuple, and type_is_rowtype().

Referenced by fmgr_sql().

◆ init_sql_fcache()

static SQLFunctionCache * init_sql_fcache ( FunctionCallInfo  fcinfo,
bool  lazyEvalOK 
)
static

Definition at line 534 of file functions.c.

535{
536 FmgrInfo *finfo = fcinfo->flinfo;
538 SQLFunctionCache *fcache;
539
540 /*
541 * If this is the first execution for this FmgrInfo, set up a cache struct
542 * (initially containing null pointers). The cache must live as long as
543 * the FmgrInfo, so it goes in fn_mcxt. Also set up a memory context
544 * callback that will be invoked when fn_mcxt is deleted.
545 */
546 fcache = finfo->fn_extra;
547 if (fcache == NULL)
548 {
549 fcache = (SQLFunctionCache *)
551 fcache->fcontext = finfo->fn_mcxt;
553 fcache->mcb.arg = fcache;
555 finfo->fn_extra = fcache;
556 }
557
558 /*
559 * If we are resuming execution of a set-returning function, just keep
560 * using the same cache. We do not ask funccache.c to re-validate the
561 * SQLFunctionHashEntry: we want to run to completion using the function's
562 * initial definition.
563 */
564 if (fcache->eslist != NULL)
565 {
566 Assert(fcache->func != NULL);
567 return fcache;
568 }
569
570 /*
571 * Look up, or re-validate, the long-lived hash entry. Make the hash key
572 * depend on the result of get_call_result_type() when that's composite,
573 * so that we can safely assume that we'll build a new hash entry if the
574 * composite rowtype changes.
575 */
576 func = (SQLFunctionHashEntry *)
578 (CachedFunction *) fcache->func,
581 sizeof(SQLFunctionHashEntry),
582 true,
583 false);
584
585 /*
586 * Install the hash pointer in the SQLFunctionCache, and increment its use
587 * count to reflect that. If cached_function_compile gave us back a
588 * different hash entry than we were using before, we must decrement that
589 * one's use count.
590 */
591 if (func != fcache->func)
592 {
593 if (fcache->func != NULL)
594 {
595 Assert(fcache->func->cfunc.use_count > 0);
596 fcache->func->cfunc.use_count--;
597 }
598 fcache->func = func;
599 func->cfunc.use_count++;
600 /* Assume we need to rebuild the junkFilter */
601 fcache->junkFilter = NULL;
602 }
603
604 /*
605 * We're beginning a new execution of the function, so convert params to
606 * appropriate format.
607 */
608 postquel_sub_params(fcache, fcinfo);
609
610 /* Also reset lazyEval state for the new execution. */
611 fcache->lazyEvalOK = lazyEvalOK;
612 fcache->lazyEval = false;
613
614 /* Also reset data about where we are in the function. */
615 fcache->eslist = NULL;
616 fcache->next_query_index = 0;
617 fcache->error_query_index = 0;
618
619 return fcache;
620}
CachedFunction * cached_function_compile(FunctionCallInfo fcinfo, CachedFunction *function, CachedFunctionCompileCallback ccallback, CachedFunctionDeleteCallback dcallback, Size cacheEntrySize, bool includeResultType, bool forValidator)
Definition: funccache.c:480
static void sql_delete_callback(CachedFunction *cfunc)
Definition: functions.c:1189
static void sql_compile_callback(FunctionCallInfo fcinfo, HeapTuple procedureTuple, const CachedFunctionHashKey *hashkey, CachedFunction *cfunc, bool forValidator)
Definition: functions.c:1016
static void postquel_sub_params(SQLFunctionCachePtr fcache, FunctionCallInfo fcinfo)
Definition: functions.c:1451
static void RemoveSQLFunctionCache(void *arg)
Definition: functions.c:1986
struct SQLFunctionHashEntry SQLFunctionHashEntry
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1294
void MemoryContextRegisterResetCallback(MemoryContext context, MemoryContextCallback *cb)
Definition: mcxt.c:599
uint64 use_count
Definition: funccache.h:117
Definition: fmgr.h:57
void * fn_extra
Definition: fmgr.h:64
MemoryContext fn_mcxt
Definition: fmgr.h:65
FmgrInfo * flinfo
Definition: fmgr.h:87
MemoryContextCallbackFunction func
Definition: palloc.h:49
MemoryContextCallback mcb
Definition: functions.c:187
CachedFunction cfunc
Definition: functions.c:113

References MemoryContextCallback::arg, Assert(), cached_function_compile(), SQLFunctionHashEntry::cfunc, SQLFunctionCache::error_query_index, SQLFunctionCache::eslist, SQLFunctionCache::fcontext, FunctionCallInfoBaseData::flinfo, FmgrInfo::fn_extra, FmgrInfo::fn_mcxt, SQLFunctionCache::func, MemoryContextCallback::func, SQLFunctionCache::junkFilter, SQLFunctionCache::lazyEval, SQLFunctionCache::lazyEvalOK, SQLFunctionCache::mcb, MemoryContextAllocZero(), MemoryContextRegisterResetCallback(), SQLFunctionCache::next_query_index, postquel_sub_params(), RemoveSQLFunctionCache(), sql_compile_callback(), sql_delete_callback(), and CachedFunction::use_count.

Referenced by fmgr_sql().

◆ postquel_end()

static void postquel_end ( execution_state es,
SQLFunctionCachePtr  fcache 
)
static

Definition at line 1419 of file functions.c.

1420{
1421 MemoryContext oldcontext;
1422
1423 /* Run the sub-executor in subcontext */
1424 oldcontext = MemoryContextSwitchTo(fcache->subcontext);
1425
1426 /* mark status done to ensure we don't do ExecutorEnd twice */
1427 es->status = F_EXEC_DONE;
1428
1429 /* Utility commands don't need Executor. */
1430 if (es->qd->operation != CMD_UTILITY)
1431 {
1432 ExecutorFinish(es->qd);
1433 ExecutorEnd(es->qd);
1434 }
1435
1436 es->qd->dest->rDestroy(es->qd->dest);
1437
1438 FreeQueryDesc(es->qd);
1439 es->qd = NULL;
1440
1441 MemoryContextSwitchTo(oldcontext);
1442
1443 /* Delete the subcontext, if it's actually a separate context */
1444 if (fcache->ownSubcontext)
1446 fcache->subcontext = NULL;
1447}
void ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:538
void ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:475
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:485
void FreeQueryDesc(QueryDesc *qdesc)
Definition: pquery.c:112
DestReceiver * dest
Definition: execdesc.h:42
CmdType operation
Definition: execdesc.h:36
MemoryContext subcontext
Definition: functions.c:184

References CMD_UTILITY, QueryDesc::dest, ExecutorEnd(), ExecutorFinish(), F_EXEC_DONE, FreeQueryDesc(), MemoryContextDelete(), MemoryContextSwitchTo(), QueryDesc::operation, SQLFunctionCache::ownSubcontext, execution_state::qd, _DestReceiver::rDestroy, execution_state::status, and SQLFunctionCache::subcontext.

Referenced by fmgr_sql(), and ShutdownSQLFunction().

◆ postquel_get_single_result()

static Datum postquel_get_single_result ( TupleTableSlot slot,
FunctionCallInfo  fcinfo,
SQLFunctionCachePtr  fcache 
)
static

Definition at line 1514 of file functions.c.

1517{
1518 Datum value;
1519
1520 /*
1521 * Set up to return the function value. For pass-by-reference datatypes,
1522 * be sure to copy the result into the current context. We can't leave
1523 * the data in the TupleTableSlot because we must clear the slot before
1524 * returning.
1525 */
1526 if (fcache->func->returnsTuple)
1527 {
1528 /* We must return the whole tuple as a Datum. */
1529 fcinfo->isnull = false;
1531 }
1532 else
1533 {
1534 /*
1535 * Returning a scalar, which we have to extract from the first column
1536 * of the SELECT result, and then copy into current context if needed.
1537 */
1538 value = slot_getattr(slot, 1, &(fcinfo->isnull));
1539
1540 if (!fcinfo->isnull)
1541 value = datumCopy(value, fcache->func->typbyval, fcache->func->typlen);
1542 }
1543
1544 /* Clear the slot for next time */
1545 ExecClearTuple(slot);
1546
1547 return value;
1548}
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
Datum ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot)
Definition: execTuples.c:1912
static struct @165 value
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:399
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:458

References datumCopy(), ExecClearTuple(), ExecFetchSlotHeapTupleDatum(), SQLFunctionCache::func, FunctionCallInfoBaseData::isnull, SQLFunctionHashEntry::returnsTuple, slot_getattr(), SQLFunctionHashEntry::typbyval, SQLFunctionHashEntry::typlen, and value.

Referenced by fmgr_sql().

◆ postquel_getnext()

static bool postquel_getnext ( execution_state es,
SQLFunctionCachePtr  fcache 
)
static

Definition at line 1378 of file functions.c.

1379{
1380 bool result;
1381 MemoryContext oldcontext;
1382
1383 /* Run the sub-executor in subcontext */
1384 oldcontext = MemoryContextSwitchTo(fcache->subcontext);
1385
1386 if (es->qd->operation == CMD_UTILITY)
1387 {
1389 fcache->func->src,
1390 true, /* protect function cache's parsetree */
1392 es->qd->params,
1393 es->qd->queryEnv,
1394 es->qd->dest,
1395 NULL);
1396 result = true; /* never stops early */
1397 }
1398 else
1399 {
1400 /* Run regular commands to completion unless lazyEval */
1401 uint64 count = (es->lazyEval) ? 1 : 0;
1402
1403 ExecutorRun(es->qd, ForwardScanDirection, count);
1404
1405 /*
1406 * If we requested run to completion OR there was no tuple returned,
1407 * command must be complete.
1408 */
1409 result = (count == 0 || es->qd->estate->es_processed == 0);
1410 }
1411
1412 MemoryContextSwitchTo(oldcontext);
1413
1414 return result;
1415}
uint64_t uint64
Definition: c.h:503
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
Definition: execMain.c:365
@ ForwardScanDirection
Definition: sdir.h:28
uint64 es_processed
Definition: execnodes.h:712
ParamListInfo params
Definition: execdesc.h:43
EState * estate
Definition: execdesc.h:49
PlannedStmt * plannedstmt
Definition: execdesc.h:37
QueryEnvironment * queryEnv
Definition: execdesc.h:44
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:499
@ PROCESS_UTILITY_QUERY
Definition: utility.h:23

References CMD_UTILITY, QueryDesc::dest, EState::es_processed, QueryDesc::estate, ExecutorRun(), ForwardScanDirection, SQLFunctionCache::func, execution_state::lazyEval, MemoryContextSwitchTo(), QueryDesc::operation, QueryDesc::params, QueryDesc::plannedstmt, PROCESS_UTILITY_QUERY, ProcessUtility(), execution_state::qd, QueryDesc::queryEnv, SQLFunctionHashEntry::src, and SQLFunctionCache::subcontext.

Referenced by fmgr_sql().

◆ postquel_start()

static void postquel_start ( execution_state es,
SQLFunctionCachePtr  fcache 
)
static

Definition at line 1252 of file functions.c.

1253{
1256
1257 Assert(es->qd == NULL);
1258
1259 /* Caller should have ensured a suitable snapshot is active */
1261
1262 /*
1263 * In lazyEval mode for a SRF, we must run the sub-executor in a child of
1264 * fcontext, so that it can survive across multiple calls to fmgr_sql.
1265 * (XXX in the case of a long-lived FmgrInfo, this policy potentially
1266 * causes memory leakage, but it's not very clear where we could keep
1267 * stuff instead. Fortunately, there are few if any cases where
1268 * set-returning functions are invoked via FmgrInfos that would outlive
1269 * the calling query.) Otherwise, we're going to run it to completion
1270 * before exiting fmgr_sql, so it can perfectly well run in the caller's
1271 * context.
1272 */
1273 if (es->lazyEval && fcache->func->returnsSet)
1274 {
1275 fcache->subcontext = AllocSetContextCreate(fcache->fcontext,
1276 "SQL function execution",
1278 fcache->ownSubcontext = true;
1279 }
1280 else if (es->stmt->commandType == CMD_UTILITY)
1281 {
1282 /*
1283 * The code path using a sub-executor is pretty good about cleaning up
1284 * cruft, since the executor will make its own sub-context. We don't
1285 * really need an additional layer of sub-context in that case.
1286 * However, if this is a utility statement, it won't make its own
1287 * sub-context, so it seems advisable to make one that we can free on
1288 * completion.
1289 */
1291 "SQL function execution",
1293 fcache->ownSubcontext = true;
1294 }
1295 else
1296 {
1298 fcache->ownSubcontext = false;
1299 }
1300
1301 /*
1302 * Build a tuplestore if needed, that is if it's a set-returning function
1303 * and we're producing the function result without using lazyEval mode.
1304 */
1305 if (es->setsResult)
1306 {
1307 Assert(fcache->tstore == NULL);
1308 if (fcache->func->returnsSet && !es->lazyEval)
1309 {
1311 fcache->tstore = tuplestore_begin_heap(fcache->randomAccess,
1312 false, work_mem);
1313 }
1314 }
1315
1316 /* Switch into the selected subcontext (might be a no-op) */
1318
1319 /*
1320 * If this query produces the function result, collect its output using
1321 * our custom DestReceiver; else discard any output.
1322 */
1323 if (es->setsResult)
1324 {
1325 DR_sqlfunction *myState;
1326
1328 /* pass down the needed info to the dest receiver routines */
1329 myState = (DR_sqlfunction *) dest;
1330 Assert(myState->pub.mydest == DestSQLFunction);
1331 myState->tstore = fcache->tstore; /* might be NULL */
1332 myState->filter = fcache->junkFilter;
1333
1334 /* Make very sure the junkfilter's result slot is empty */
1336 }
1337 else
1339
1340 es->qd = CreateQueryDesc(es->stmt,
1341 NULL,
1342 fcache->func->src,
1345 dest,
1346 fcache->paramLI,
1347 es->qd ? es->qd->queryEnv : NULL,
1348 0);
1349
1350 /* Utility commands don't need Executor. */
1351 if (es->qd->operation != CMD_UTILITY)
1352 {
1353 /*
1354 * In lazyEval mode, do not let the executor set up an AfterTrigger
1355 * context. This is necessary not just an optimization, because we
1356 * mustn't exit from the function execution with a stacked
1357 * AfterTrigger level still active. We are careful not to select
1358 * lazyEval mode for any statement that could possibly queue triggers.
1359 */
1360 int eflags;
1361
1362 if (es->lazyEval)
1363 eflags = EXEC_FLAG_SKIP_TRIGGERS;
1364 else
1365 eflags = 0; /* default run-to-completion flags */
1366 if (!ExecutorStart(es->qd, eflags))
1367 elog(ERROR, "ExecutorStart() failed unexpectedly");
1368 }
1369
1370 es->status = F_EXEC_RUN;
1371
1372 MemoryContextSwitchTo(oldcontext);
1373}
DestReceiver * CreateDestReceiver(CommandDest dest)
Definition: dest.c:113
DestReceiver * None_Receiver
Definition: dest.c:96
#define elog(elevel,...)
Definition: elog.h:225
bool ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:128
#define EXEC_FLAG_SKIP_TRIGGERS
Definition: executor.h:71
int work_mem
Definition: globals.c:132
MemoryContext CurrentMemoryContext
Definition: mcxt.c:159
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:180
QueryDesc * CreateQueryDesc(PlannedStmt *plannedstmt, CachedPlan *cplan, const char *sourceText, Snapshot snapshot, Snapshot crosscheck_snapshot, DestReceiver *dest, ParamListInfo params, QueryEnvironment *queryEnv, int instrument_options)
Definition: pquery.c:72
bool ActiveSnapshotSet(void)
Definition: snapmgr.c:799
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:787
#define InvalidSnapshot
Definition: snapshot.h:119
JunkFilter * filter
Definition: functions.c:48
Tuplestorestate * tstore
Definition: functions.c:47
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:330

References ActiveSnapshotSet(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), CMD_UTILITY, PlannedStmt::commandType, CreateDestReceiver(), CreateQueryDesc(), CurrentMemoryContext, generate_unaccent_rules::dest, DestSQLFunction, elog, ERROR, EXEC_FLAG_SKIP_TRIGGERS, ExecClearTuple(), ExecutorStart(), F_EXEC_RUN, SQLFunctionCache::fcontext, DR_sqlfunction::filter, SQLFunctionCache::func, GetActiveSnapshot(), InvalidSnapshot, JunkFilter::jf_resultSlot, SQLFunctionCache::junkFilter, execution_state::lazyEval, MemoryContextSwitchTo(), _DestReceiver::mydest, None_Receiver, QueryDesc::operation, SQLFunctionCache::ownSubcontext, SQLFunctionCache::paramLI, DR_sqlfunction::pub, execution_state::qd, QueryDesc::queryEnv, SQLFunctionCache::randomAccess, SQLFunctionHashEntry::returnsSet, execution_state::setsResult, SQLFunctionHashEntry::src, execution_state::status, execution_state::stmt, SQLFunctionCache::subcontext, SQLFunctionCache::tscontext, DR_sqlfunction::tstore, SQLFunctionCache::tstore, tuplestore_begin_heap(), and work_mem.

Referenced by fmgr_sql().

◆ postquel_sub_params()

static void postquel_sub_params ( SQLFunctionCachePtr  fcache,
FunctionCallInfo  fcinfo 
)
static

Definition at line 1451 of file functions.c.

1453{
1454 int nargs = fcinfo->nargs;
1455
1456 if (nargs > 0)
1457 {
1458 ParamListInfo paramLI;
1459 Oid *argtypes = fcache->func->pinfo->argtypes;
1460 int16 *argtyplen = fcache->func->argtyplen;
1461
1462 if (fcache->paramLI == NULL)
1463 {
1464 /* First time through: build a persistent ParamListInfo struct */
1465 MemoryContext oldcontext;
1466
1467 oldcontext = MemoryContextSwitchTo(fcache->fcontext);
1468 paramLI = makeParamList(nargs);
1469 fcache->paramLI = paramLI;
1470 MemoryContextSwitchTo(oldcontext);
1471 }
1472 else
1473 {
1474 paramLI = fcache->paramLI;
1475 Assert(paramLI->numParams == nargs);
1476 }
1477
1478 for (int i = 0; i < nargs; i++)
1479 {
1480 ParamExternData *prm = &paramLI->params[i];
1481
1482 /*
1483 * If an incoming parameter value is a R/W expanded datum, we
1484 * force it to R/O. We'd be perfectly entitled to scribble on it,
1485 * but the problem is that if the parameter is referenced more
1486 * than once in the function, earlier references might mutate the
1487 * value seen by later references, which won't do at all. We
1488 * could do better if we could be sure of the number of Param
1489 * nodes in the function's plans; but we might not have planned
1490 * all the statements yet, nor do we have plan tree walker
1491 * infrastructure. (Examining the parse trees is not good enough,
1492 * because of possible function inlining during planning.)
1493 */
1494 prm->isnull = fcinfo->args[i].isnull;
1496 prm->isnull,
1497 argtyplen[i]);
1498 /* Allow the value to be substituted into custom plans */
1499 prm->pflags = PARAM_FLAG_CONST;
1500 prm->ptype = argtypes[i];
1501 }
1502 }
1503 else
1504 fcache->paramLI = NULL;
1505}
int16_t int16
Definition: c.h:497
#define MakeExpandedObjectReadOnly(d, isnull, typlen)
int i
Definition: isn.c:77
ParamListInfo makeParamList(int numParams)
Definition: params.c:44
#define PARAM_FLAG_CONST
Definition: params.h:88
unsigned int Oid
Definition: postgres_ext.h:30
NullableDatum args[FLEXIBLE_ARRAY_MEMBER]
Definition: fmgr.h:95
Datum value
Definition: postgres.h:80
bool isnull
Definition: postgres.h:82
bool isnull
Definition: params.h:93
uint16 pflags
Definition: params.h:94
Datum value
Definition: params.h:92
ParamExternData params[FLEXIBLE_ARRAY_MEMBER]
Definition: params.h:125
SQLFunctionParseInfoPtr pinfo
Definition: functions.c:118

References FunctionCallInfoBaseData::args, SQLFunctionParseInfo::argtypes, SQLFunctionHashEntry::argtyplen, Assert(), SQLFunctionCache::fcontext, SQLFunctionCache::func, i, ParamExternData::isnull, NullableDatum::isnull, MakeExpandedObjectReadOnly, makeParamList(), MemoryContextSwitchTo(), FunctionCallInfoBaseData::nargs, ParamListInfoData::numParams, PARAM_FLAG_CONST, SQLFunctionCache::paramLI, ParamListInfoData::params, ParamExternData::pflags, SQLFunctionHashEntry::pinfo, ParamExternData::ptype, ParamExternData::value, and NullableDatum::value.

Referenced by init_sql_fcache().

◆ prepare_next_query()

static void prepare_next_query ( SQLFunctionHashEntry func)
static

Definition at line 876 of file functions.c.

877{
878 int qindex;
879 bool islast;
880 CachedPlanSource *plansource;
881 List *queryTree_list;
882 MemoryContext oldcontext;
883
884 /* Which query should we process? */
885 qindex = list_length(func->plansource_list);
886 Assert(qindex < func->num_queries); /* else caller error */
887 islast = (qindex + 1 >= func->num_queries);
888
889 /*
890 * Parse and/or rewrite the query, creating a CachedPlanSource that holds
891 * a copy of the original parsetree. Note fine point: we make a copy of
892 * each original parsetree to ensure that the source_list in pcontext
893 * remains unmodified during parse analysis and rewrite. This is normally
894 * unnecessary, but we have to do it in case an error is raised during
895 * parse analysis. Otherwise, a fresh attempt to execute the function
896 * will arrive back here and try to work from a corrupted source_list.
897 */
898 if (!func->raw_source)
899 {
900 /* Source queries are already parse-analyzed */
901 Query *parsetree = list_nth_node(Query, func->source_list, qindex);
902
903 parsetree = copyObject(parsetree);
904 plansource = CreateCachedPlanForQuery(parsetree,
905 func->src,
906 CreateCommandTag((Node *) parsetree));
907 AcquireRewriteLocks(parsetree, true, false);
908 queryTree_list = pg_rewrite_query(parsetree);
909 }
910 else
911 {
912 /* Source queries are raw parsetrees */
913 RawStmt *parsetree = list_nth_node(RawStmt, func->source_list, qindex);
914
915 parsetree = copyObject(parsetree);
916 plansource = CreateCachedPlan(parsetree,
917 func->src,
918 CreateCommandTag(parsetree->stmt));
919 queryTree_list = pg_analyze_and_rewrite_withcb(parsetree,
920 func->src,
922 func->pinfo,
923 NULL);
924 }
925
926 /*
927 * Check that there are no statements we don't want to allow.
928 */
929 check_sql_fn_statement(queryTree_list);
930
931 /*
932 * If this is the last query, check that the function returns the type it
933 * claims to. Although in simple cases this was already done when the
934 * function was defined, we have to recheck because database objects used
935 * in the function's queries might have changed type. We'd have to
936 * recheck anyway if the function had any polymorphic arguments. Moreover,
937 * check_sql_stmt_retval takes care of injecting any required column type
938 * coercions. (But we don't ask it to insert nulls for dropped columns;
939 * the junkfilter handles that.)
940 *
941 * Note: we set func->returnsTuple according to whether we are returning
942 * the whole tuple result or just a single column. In the latter case we
943 * clear returnsTuple because we need not act different from the scalar
944 * result case, even if it's a rowtype column. (However, we have to force
945 * lazy eval mode in that case; otherwise we'd need extra code to expand
946 * the rowtype column into multiple columns, since we have no way to
947 * notify the caller that it should do that.)
948 */
949 if (islast)
950 func->returnsTuple = check_sql_stmt_retval(queryTree_list,
951 func->rettype,
952 func->rettupdesc,
953 func->prokind,
954 false);
955
956 /*
957 * Now that check_sql_stmt_retval has done its thing, we can complete plan
958 * cache entry creation.
959 */
960 CompleteCachedPlan(plansource,
961 queryTree_list,
962 NULL,
963 NULL,
964 0,
966 func->pinfo,
968 false);
969
970 /*
971 * Install post-rewrite hook. Its arg is the hash entry if this is the
972 * last statement, else NULL.
973 */
974 SetPostRewriteHook(plansource,
976 islast ? func : NULL);
977
978 /*
979 * While the CachedPlanSources can take care of themselves, our List
980 * pointing to them had better be in the hcontext.
981 */
982 oldcontext = MemoryContextSwitchTo(func->hcontext);
983 func->plansource_list = lappend(func->plansource_list, plansource);
984 MemoryContextSwitchTo(oldcontext);
985
986 /*
987 * As soon as we've linked the CachedPlanSource into the list, mark it as
988 * "saved".
989 */
990 SaveCachedPlan(plansource);
991
992 /*
993 * Finally, if this was the last statement, we can flush the pcontext with
994 * the original query trees; they're all safely copied into
995 * CachedPlanSources now.
996 */
997 if (islast)
998 {
999 func->source_list = NIL; /* avoid dangling pointer */
1001 func->pcontext = NULL;
1002 }
1003}
static void sql_postrewrite_callback(List *querytree_list, void *arg)
Definition: functions.c:1219
void sql_fn_parser_setup(struct ParseState *pstate, SQLFunctionParseInfoPtr pinfo)
Definition: functions.c:338
#define copyObject(obj)
Definition: nodes.h:230
void(* ParserSetupHook)(struct ParseState *pstate, void *arg)
Definition: params.h:108
#define CURSOR_OPT_PARALLEL_OK
Definition: parsenodes.h:3385
#define CURSOR_OPT_NO_SCROLL
Definition: parsenodes.h:3377
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
void SaveCachedPlan(CachedPlanSource *plansource)
Definition: plancache.c:531
void CompleteCachedPlan(CachedPlanSource *plansource, List *querytree_list, MemoryContext querytree_context, Oid *param_types, int num_params, ParserSetupHook parserSetup, void *parserSetupArg, int cursor_options, bool fixed_result)
Definition: plancache.c:392
CachedPlanSource * CreateCachedPlanForQuery(Query *analyzed_parse_tree, const char *query_string, CommandTag commandTag)
Definition: plancache.c:264
void SetPostRewriteHook(CachedPlanSource *plansource, PostRewriteHook postRewrite, void *postRewriteArg)
Definition: plancache.c:506
CachedPlanSource * CreateCachedPlan(RawStmt *raw_parse_tree, const char *query_string, CommandTag commandTag)
Definition: plancache.c:184
List * pg_analyze_and_rewrite_withcb(RawStmt *parsetree, const char *query_string, ParserSetupHook parserSetup, void *parserSetupArg, QueryEnvironment *queryEnv)
Definition: postgres.c:758
List * pg_rewrite_query(Query *query)
Definition: postgres.c:798
void AcquireRewriteLocks(Query *parsetree, bool forExecute, bool forUpdatePushedDown)
Node * stmt
Definition: parsenodes.h:2071
MemoryContext pcontext
Definition: functions.c:137
MemoryContext hcontext
Definition: functions.c:138
CommandTag CreateCommandTag(Node *parsetree)
Definition: utility.c:2362

References AcquireRewriteLocks(), Assert(), check_sql_fn_statement(), check_sql_stmt_retval(), CompleteCachedPlan(), copyObject, CreateCachedPlan(), CreateCachedPlanForQuery(), CreateCommandTag(), CURSOR_OPT_NO_SCROLL, CURSOR_OPT_PARALLEL_OK, SQLFunctionHashEntry::hcontext, lappend(), list_length(), list_nth_node, MemoryContextDelete(), MemoryContextSwitchTo(), NIL, SQLFunctionHashEntry::num_queries, SQLFunctionHashEntry::pcontext, pg_analyze_and_rewrite_withcb(), pg_rewrite_query(), SQLFunctionHashEntry::pinfo, SQLFunctionHashEntry::plansource_list, SQLFunctionHashEntry::prokind, SQLFunctionHashEntry::raw_source, SQLFunctionHashEntry::rettupdesc, SQLFunctionHashEntry::rettype, SQLFunctionHashEntry::returnsTuple, SaveCachedPlan(), SetPostRewriteHook(), SQLFunctionHashEntry::source_list, sql_fn_parser_setup(), sql_postrewrite_callback(), SQLFunctionHashEntry::src, and RawStmt::stmt.

Referenced by init_execution_state().

◆ prepare_sql_fn_parse_info()

SQLFunctionParseInfoPtr prepare_sql_fn_parse_info ( HeapTuple  procedureTuple,
Node call_expr,
Oid  inputCollation 
)

Definition at line 249 of file functions.c.

252{
254 Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
255 int nargs;
256
258
259 /* Function's name (only) can be used to qualify argument names */
260 pinfo->fname = pstrdup(NameStr(procedureStruct->proname));
261
262 /* Save the function's input collation */
263 pinfo->collation = inputCollation;
264
265 /*
266 * Copy input argument types from the pg_proc entry, then resolve any
267 * polymorphic types.
268 */
269 pinfo->nargs = nargs = procedureStruct->pronargs;
270 if (nargs > 0)
271 {
272 Oid *argOidVect;
273 int argnum;
274
275 argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
276 memcpy(argOidVect,
277 procedureStruct->proargtypes.values,
278 nargs * sizeof(Oid));
279
280 for (argnum = 0; argnum < nargs; argnum++)
281 {
282 Oid argtype = argOidVect[argnum];
283
284 if (IsPolymorphicType(argtype))
285 {
286 argtype = get_call_expr_argtype(call_expr, argnum);
287 if (argtype == InvalidOid)
289 (errcode(ERRCODE_DATATYPE_MISMATCH),
290 errmsg("could not determine actual type of argument declared %s",
291 format_type_be(argOidVect[argnum]))));
292 argOidVect[argnum] = argtype;
293 }
294 }
295
296 pinfo->argtypes = argOidVect;
297 }
298
299 /*
300 * Collect names of arguments, too, if any
301 */
302 if (nargs > 0)
303 {
304 Datum proargnames;
305 Datum proargmodes;
306 int n_arg_names;
307 bool isNull;
308
309 proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple,
310 Anum_pg_proc_proargnames,
311 &isNull);
312 if (isNull)
313 proargnames = PointerGetDatum(NULL); /* just to be sure */
314
315 proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple,
316 Anum_pg_proc_proargmodes,
317 &isNull);
318 if (isNull)
319 proargmodes = PointerGetDatum(NULL); /* just to be sure */
320
321 n_arg_names = get_func_input_arg_names(proargnames, proargmodes,
322 &pinfo->argnames);
323
324 /* Paranoia: ignore the result if too few array entries */
325 if (n_arg_names < nargs)
326 pinfo->argnames = NULL;
327 }
328 else
329 pinfo->argnames = NULL;
330
331 return pinfo;
332}
#define NameStr(name)
Definition: c.h:717
Oid get_call_expr_argtype(Node *expr, int argnum)
Definition: fmgr.c:1929
int get_func_input_arg_names(Datum proargnames, Datum proargmodes, char ***arg_names)
Definition: funcapi.c:1522
SQLFunctionParseInfo * SQLFunctionParseInfoPtr
Definition: functions.h:35
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
char * pstrdup(const char *in)
Definition: mcxt.c:2327
void * palloc(Size size)
Definition: mcxt.c:1945
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:136
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:600

References SQLFunctionParseInfo::argnames, SQLFunctionParseInfo::argtypes, SQLFunctionParseInfo::collation, ereport, errcode(), errmsg(), ERROR, SQLFunctionParseInfo::fname, format_type_be(), get_call_expr_argtype(), get_func_input_arg_names(), GETSTRUCT(), InvalidOid, NameStr, SQLFunctionParseInfo::nargs, palloc(), palloc0(), PointerGetDatum(), pstrdup(), and SysCacheGetAttr().

Referenced by fmgr_sql_validator(), inline_function(), inline_set_returning_function(), and sql_compile_callback().

◆ RemoveSQLFunctionCache()

static void RemoveSQLFunctionCache ( void *  arg)
static

Definition at line 1986 of file functions.c.

1987{
1989
1990 /* Release reference count on SQLFunctionHashEntry */
1991 if (fcache->func != NULL)
1992 {
1993 Assert(fcache->func->cfunc.use_count > 0);
1994 fcache->func->cfunc.use_count--;
1995 /* This should be unnecessary, but let's just be sure: */
1996 fcache->func = NULL;
1997 }
1998}
void * arg

References arg, Assert(), SQLFunctionHashEntry::cfunc, SQLFunctionCache::func, and CachedFunction::use_count.

Referenced by init_sql_fcache().

◆ ShutdownSQLFunction()

static void ShutdownSQLFunction ( Datum  arg)
static

Definition at line 1939 of file functions.c.

1940{
1942 execution_state *es;
1943
1944 es = fcache->eslist;
1945 while (es)
1946 {
1947 /* Shut down anything still running */
1948 if (es->status == F_EXEC_RUN)
1949 {
1950 /* Re-establish active snapshot for any called functions */
1951 if (!fcache->func->readonly_func)
1953
1954 postquel_end(es, fcache);
1955
1956 if (!fcache->func->readonly_func)
1958 }
1959 es = es->next;
1960 }
1961 fcache->eslist = NULL;
1962
1963 /* Release tuplestore if we have one */
1964 if (fcache->tstore)
1965 tuplestore_end(fcache->tstore);
1966 fcache->tstore = NULL;
1967
1968 /* Release CachedPlan if we have one */
1969 if (fcache->cplan)
1970 ReleaseCachedPlan(fcache->cplan, fcache->cowner);
1971 fcache->cplan = NULL;
1972
1973 /* execUtils will deregister the callback... */
1974 fcache->shutdown_reg = false;
1975}
SQLFunctionCache * SQLFunctionCachePtr
Definition: functions.c:190
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:492

References arg, SQLFunctionCache::cowner, SQLFunctionCache::cplan, DatumGetPointer(), SQLFunctionCache::eslist, F_EXEC_RUN, SQLFunctionCache::func, execution_state::next, PopActiveSnapshot(), postquel_end(), PushActiveSnapshot(), execution_state::qd, SQLFunctionHashEntry::readonly_func, ReleaseCachedPlan(), SQLFunctionCache::shutdown_reg, QueryDesc::snapshot, execution_state::status, SQLFunctionCache::tstore, and tuplestore_end().

Referenced by fmgr_sql().

◆ sql_compile_callback()

static void sql_compile_callback ( FunctionCallInfo  fcinfo,
HeapTuple  procedureTuple,
const CachedFunctionHashKey hashkey,
CachedFunction cfunc,
bool  forValidator 
)
static

Definition at line 1016 of file functions.c.

1021{
1023 Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
1024 ErrorContextCallback comperrcontext;
1025 MemoryContext hcontext;
1026 MemoryContext pcontext;
1028 Oid rettype;
1029 TupleDesc rettupdesc;
1030 Datum tmp;
1031 bool isNull;
1032 List *source_list;
1033
1034 /*
1035 * Setup error traceback support for ereport() during compile. (This is
1036 * mainly useful for reporting parse errors from pg_parse_query.)
1037 */
1038 comperrcontext.callback = sql_compile_error_callback;
1039 comperrcontext.arg = func;
1040 comperrcontext.previous = error_context_stack;
1041 error_context_stack = &comperrcontext;
1042
1043 /*
1044 * Create the hash entry's memory context. For now it's a child of the
1045 * caller's context, so that it will go away if we fail partway through.
1046 */
1048 "SQL function",
1050
1051 /*
1052 * Create the not-as-long-lived pcontext. We make this a child of
1053 * hcontext so that it doesn't require separate deletion.
1054 */
1055 pcontext = AllocSetContextCreate(hcontext,
1056 "SQL function parse trees",
1058 func->pcontext = pcontext;
1059
1060 /*
1061 * copy function name immediately for use by error reporting callback, and
1062 * for use as memory context identifier
1063 */
1064 func->fname = MemoryContextStrdup(hcontext,
1065 NameStr(procedureStruct->proname));
1066 MemoryContextSetIdentifier(hcontext, func->fname);
1067
1068 /*
1069 * Resolve any polymorphism, obtaining the actual result type, and the
1070 * corresponding tupdesc if it's a rowtype.
1071 */
1072 (void) get_call_result_type(fcinfo, &rettype, &rettupdesc);
1073
1074 func->rettype = rettype;
1075 if (rettupdesc)
1076 {
1077 MemoryContextSwitchTo(hcontext);
1078 func->rettupdesc = CreateTupleDescCopy(rettupdesc);
1079 MemoryContextSwitchTo(oldcontext);
1080 }
1081
1082 /* Fetch the typlen and byval info for the result type */
1083 get_typlenbyval(rettype, &func->typlen, &func->typbyval);
1084
1085 /* Remember whether we're returning setof something */
1086 func->returnsSet = procedureStruct->proretset;
1087
1088 /* Remember if function is STABLE/IMMUTABLE */
1089 func->readonly_func =
1090 (procedureStruct->provolatile != PROVOLATILE_VOLATILE);
1091
1092 /* Remember routine kind */
1093 func->prokind = procedureStruct->prokind;
1094
1095 /*
1096 * We need the actual argument types to pass to the parser. Also make
1097 * sure that parameter symbols are considered to have the function's
1098 * resolved input collation.
1099 */
1100 MemoryContextSwitchTo(hcontext);
1101 func->pinfo = prepare_sql_fn_parse_info(procedureTuple,
1102 fcinfo->flinfo->fn_expr,
1104 MemoryContextSwitchTo(oldcontext);
1105
1106 /*
1107 * Now that we have the resolved argument types, collect their typlens for
1108 * use in postquel_sub_params.
1109 */
1110 func->argtyplen = (int16 *)
1111 MemoryContextAlloc(hcontext, func->pinfo->nargs * sizeof(int16));
1112 for (int i = 0; i < func->pinfo->nargs; i++)
1113 func->argtyplen[i] = get_typlen(func->pinfo->argtypes[i]);
1114
1115 /*
1116 * And of course we need the function body text.
1117 */
1118 tmp = SysCacheGetAttrNotNull(PROCOID, procedureTuple, Anum_pg_proc_prosrc);
1119 func->src = MemoryContextStrdup(hcontext,
1120 TextDatumGetCString(tmp));
1121
1122 /* If we have prosqlbody, pay attention to that not prosrc. */
1123 tmp = SysCacheGetAttr(PROCOID,
1124 procedureTuple,
1125 Anum_pg_proc_prosqlbody,
1126 &isNull);
1127 if (!isNull)
1128 {
1129 /* Source queries are already parse-analyzed */
1130 Node *n;
1131
1133 if (IsA(n, List))
1134 source_list = linitial_node(List, castNode(List, n));
1135 else
1136 source_list = list_make1(n);
1137 func->raw_source = false;
1138 }
1139 else
1140 {
1141 /* Source queries are raw parsetrees */
1142 source_list = pg_parse_query(func->src);
1143 func->raw_source = true;
1144 }
1145
1146 /*
1147 * Note: we must save the number of queries so that we'll still remember
1148 * how many there are after we discard source_list.
1149 */
1150 func->num_queries = list_length(source_list);
1151
1152 /*
1153 * Edge case: empty function body is OK only if it returns VOID. Normally
1154 * we validate that the last statement returns the right thing in
1155 * check_sql_stmt_retval, but we'll never reach that if there's no last
1156 * statement.
1157 */
1158 if (func->num_queries == 0 && rettype != VOIDOID)
1159 ereport(ERROR,
1160 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
1161 errmsg("return type mismatch in function declared to return %s",
1162 format_type_be(rettype)),
1163 errdetail("Function's final statement must be SELECT or INSERT/UPDATE/DELETE/MERGE RETURNING.")));
1164
1165 /* Save the source trees in pcontext for now. */
1166 MemoryContextSwitchTo(pcontext);
1167 func->source_list = copyObject(source_list);
1168 MemoryContextSwitchTo(oldcontext);
1169
1170 /*
1171 * We now have a fully valid hash entry, so reparent hcontext under
1172 * CacheMemoryContext to make all the subsidiary data long-lived, and only
1173 * then install the hcontext link so that sql_delete_callback will know to
1174 * delete it.
1175 */
1177 func->hcontext = hcontext;
1178
1179 error_context_stack = comperrcontext.previous;
1180}
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define PG_GET_COLLATION()
Definition: fmgr.h:198
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:276
static void sql_compile_error_callback(void *arg)
Definition: functions.c:1867
SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(HeapTuple procedureTuple, Node *call_expr, Oid inputCollation)
Definition: functions.c:249
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2391
int16 get_typlen(Oid typid)
Definition: lsyscache.c:2337
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:2314
void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
Definition: mcxt.c:668
MemoryContext CacheMemoryContext
Definition: mcxt.c:168
void MemoryContextSetIdentifier(MemoryContext context, const char *id)
Definition: mcxt.c:643
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
#define linitial_node(type, l)
Definition: pg_list.h:181
List * pg_parse_query(const char *query_string)
Definition: postgres.c:603
void * stringToNode(const char *str)
Definition: read.c:90
fmNodePtr fn_expr
Definition: fmgr.h:66
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:631

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, ErrorContextCallback::arg, SQLFunctionParseInfo::argtypes, SQLFunctionHashEntry::argtyplen, CacheMemoryContext, ErrorContextCallback::callback, castNode, copyObject, CreateTupleDescCopy(), CurrentMemoryContext, ereport, errcode(), errdetail(), errmsg(), ERROR, error_context_stack, FunctionCallInfoBaseData::flinfo, FmgrInfo::fn_expr, SQLFunctionHashEntry::fname, format_type_be(), get_call_result_type(), get_typlen(), get_typlenbyval(), GETSTRUCT(), SQLFunctionHashEntry::hcontext, i, IsA, linitial_node, list_length(), list_make1, MemoryContextAlloc(), MemoryContextSetIdentifier(), MemoryContextSetParent(), MemoryContextStrdup(), MemoryContextSwitchTo(), NameStr, SQLFunctionParseInfo::nargs, SQLFunctionHashEntry::num_queries, SQLFunctionHashEntry::pcontext, PG_GET_COLLATION, pg_parse_query(), SQLFunctionHashEntry::pinfo, prepare_sql_fn_parse_info(), ErrorContextCallback::previous, SQLFunctionHashEntry::prokind, SQLFunctionHashEntry::raw_source, SQLFunctionHashEntry::readonly_func, SQLFunctionHashEntry::rettupdesc, SQLFunctionHashEntry::rettype, SQLFunctionHashEntry::returnsSet, SQLFunctionHashEntry::source_list, sql_compile_error_callback(), SQLFunctionHashEntry::src, stringToNode(), SysCacheGetAttr(), SysCacheGetAttrNotNull(), TextDatumGetCString, SQLFunctionHashEntry::typbyval, and SQLFunctionHashEntry::typlen.

Referenced by init_sql_fcache().

◆ sql_compile_error_callback()

static void sql_compile_error_callback ( void *  arg)
static

Definition at line 1867 of file functions.c.

1868{
1870 int syntaxerrposition;
1871
1872 /*
1873 * We can do nothing useful if sql_compile_callback() didn't get as far as
1874 * copying the function name
1875 */
1876 if (func->fname == NULL)
1877 return;
1878
1879 /*
1880 * If there is a syntax error position, convert to internal syntax error
1881 */
1882 syntaxerrposition = geterrposition();
1883 if (syntaxerrposition > 0 && func->src != NULL)
1884 {
1885 errposition(0);
1886 internalerrposition(syntaxerrposition);
1887 internalerrquery(func->src);
1888 }
1889
1890 /*
1891 * sql_compile_callback() doesn't do any per-query processing, so just
1892 * report the context as "during startup".
1893 */
1894 errcontext("SQL function \"%s\" during startup", func->fname);
1895}
int internalerrquery(const char *query)
Definition: elog.c:1504
int internalerrposition(int cursorpos)
Definition: elog.c:1484
int geterrposition(void)
Definition: elog.c:1600
int errposition(int cursorpos)
Definition: elog.c:1468
#define errcontext
Definition: elog.h:197

References arg, errcontext, errposition(), SQLFunctionHashEntry::fname, geterrposition(), internalerrposition(), internalerrquery(), and SQLFunctionHashEntry::src.

Referenced by sql_compile_callback().

◆ sql_delete_callback()

static void sql_delete_callback ( CachedFunction cfunc)
static

Definition at line 1189 of file functions.c.

1190{
1192 ListCell *lc;
1193
1194 /* Release the CachedPlanSources */
1195 foreach(lc, func->plansource_list)
1196 {
1197 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
1198
1199 DropCachedPlan(plansource);
1200 }
1201 func->plansource_list = NIL;
1202
1203 /*
1204 * If we have an hcontext, free it, thereby getting rid of all subsidiary
1205 * data. (If we still have a pcontext, this gets rid of that too.)
1206 */
1207 if (func->hcontext)
1209 func->hcontext = NULL;
1210}
void DropCachedPlan(CachedPlanSource *plansource)
Definition: plancache.c:575

References DropCachedPlan(), SQLFunctionHashEntry::hcontext, lfirst, MemoryContextDelete(), NIL, and SQLFunctionHashEntry::plansource_list.

Referenced by init_sql_fcache().

◆ sql_exec_error_callback()

static void sql_exec_error_callback ( void *  arg)
static

Definition at line 1901 of file functions.c.

1902{
1904 int syntaxerrposition;
1905
1906 /*
1907 * If there is a syntax error position, convert to internal syntax error
1908 */
1909 syntaxerrposition = geterrposition();
1910 if (syntaxerrposition > 0 && fcache->func->src != NULL)
1911 {
1912 errposition(0);
1913 internalerrposition(syntaxerrposition);
1914 internalerrquery(fcache->func->src);
1915 }
1916
1917 /*
1918 * If we failed while executing an identifiable query within the function,
1919 * report that. Otherwise say it was "during startup".
1920 */
1921 if (fcache->error_query_index > 0)
1922 errcontext("SQL function \"%s\" statement %d",
1923 fcache->func->fname, fcache->error_query_index);
1924 else
1925 errcontext("SQL function \"%s\" during startup", fcache->func->fname);
1926}

References arg, errcontext, SQLFunctionCache::error_query_index, errposition(), SQLFunctionHashEntry::fname, SQLFunctionCache::func, geterrposition(), internalerrposition(), internalerrquery(), and SQLFunctionHashEntry::src.

Referenced by fmgr_sql().

◆ sql_fn_make_param()

static Node * sql_fn_make_param ( SQLFunctionParseInfoPtr  pinfo,
int  paramno,
int  location 
)
static

Definition at line 483 of file functions.c.

485{
486 Param *param;
487
488 param = makeNode(Param);
489 param->paramkind = PARAM_EXTERN;
490 param->paramid = paramno;
491 param->paramtype = pinfo->argtypes[paramno - 1];
492 param->paramtypmod = -1;
493 param->paramcollid = get_typcollation(param->paramtype);
494 param->location = location;
495
496 /*
497 * If we have a function input collation, allow it to override the
498 * type-derived collation for parameter symbols. (XXX perhaps this should
499 * not happen if the type collation is not default?)
500 */
501 if (OidIsValid(pinfo->collation) && OidIsValid(param->paramcollid))
502 param->paramcollid = pinfo->collation;
503
504 return (Node *) param;
505}
#define OidIsValid(objectId)
Definition: c.h:746
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3196
@ PARAM_EXTERN
Definition: primnodes.h:384
ParseLoc location
Definition: primnodes.h:401
int paramid
Definition: primnodes.h:394
Oid paramtype
Definition: primnodes.h:395
ParamKind paramkind
Definition: primnodes.h:393

References SQLFunctionParseInfo::argtypes, SQLFunctionParseInfo::collation, get_typcollation(), Param::location, makeNode, OidIsValid, PARAM_EXTERN, Param::paramid, Param::paramkind, and Param::paramtype.

Referenced by sql_fn_param_ref(), and sql_fn_resolve_param_name().

◆ sql_fn_param_ref()

static Node * sql_fn_param_ref ( ParseState pstate,
ParamRef pref 
)
static

Definition at line 467 of file functions.c.

468{
470 int paramno = pref->number;
471
472 /* Check parameter number is valid */
473 if (paramno <= 0 || paramno > pinfo->nargs)
474 return NULL; /* unknown parameter number */
475
476 return sql_fn_make_param(pinfo, paramno, pref->location);
477}
static Node * sql_fn_make_param(SQLFunctionParseInfoPtr pinfo, int paramno, int location)
Definition: functions.c:483
ParseLoc location
Definition: parsenodes.h:316
int number
Definition: parsenodes.h:315
void * p_ref_hook_state
Definition: parse_node.h:258

References if(), ParamRef::location, SQLFunctionParseInfo::nargs, ParamRef::number, ParseState::p_ref_hook_state, and sql_fn_make_param().

Referenced by sql_fn_parser_setup().

◆ sql_fn_parser_setup()

void sql_fn_parser_setup ( struct ParseState pstate,
SQLFunctionParseInfoPtr  pinfo 
)

Definition at line 338 of file functions.c.

339{
340 pstate->p_pre_columnref_hook = NULL;
343 /* no need to use p_coerce_param_hook */
344 pstate->p_ref_hook_state = pinfo;
345}
static Node * sql_fn_param_ref(ParseState *pstate, ParamRef *pref)
Definition: functions.c:467
static Node * sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
Definition: functions.c:351
ParseParamRefHook p_paramref_hook
Definition: parse_node.h:256
PreParseColumnRefHook p_pre_columnref_hook
Definition: parse_node.h:254
PostParseColumnRefHook p_post_columnref_hook
Definition: parse_node.h:255

References ParseState::p_paramref_hook, ParseState::p_post_columnref_hook, ParseState::p_pre_columnref_hook, ParseState::p_ref_hook_state, sql_fn_param_ref(), and sql_fn_post_column_ref().

Referenced by fmgr_sql_validator(), inline_function(), inline_set_returning_function(), interpret_AS_clause(), and prepare_next_query().

◆ sql_fn_post_column_ref()

static Node * sql_fn_post_column_ref ( ParseState pstate,
ColumnRef cref,
Node var 
)
static

Definition at line 351 of file functions.c.

352{
354 int nnames;
355 Node *field1;
356 Node *subfield = NULL;
357 const char *name1;
358 const char *name2 = NULL;
359 Node *param;
360
361 /*
362 * Never override a table-column reference. This corresponds to
363 * considering the parameter names to appear in a scope outside the
364 * individual SQL commands, which is what we want.
365 */
366 if (var != NULL)
367 return NULL;
368
369 /*----------
370 * The allowed syntaxes are:
371 *
372 * A A = parameter name
373 * A.B A = function name, B = parameter name
374 * OR: A = record-typed parameter name, B = field name
375 * (the first possibility takes precedence)
376 * A.B.C A = function name, B = record-typed parameter name,
377 * C = field name
378 * A.* Whole-row reference to composite parameter A.
379 * A.B.* Same, with A = function name, B = parameter name
380 *
381 * Here, it's sufficient to ignore the "*" in the last two cases --- the
382 * main parser will take care of expanding the whole-row reference.
383 *----------
384 */
385 nnames = list_length(cref->fields);
386
387 if (nnames > 3)
388 return NULL;
389
390 if (IsA(llast(cref->fields), A_Star))
391 nnames--;
392
393 field1 = (Node *) linitial(cref->fields);
394 name1 = strVal(field1);
395 if (nnames > 1)
396 {
397 subfield = (Node *) lsecond(cref->fields);
398 name2 = strVal(subfield);
399 }
400
401 if (nnames == 3)
402 {
403 /*
404 * Three-part name: if the first part doesn't match the function name,
405 * we can fail immediately. Otherwise, look up the second part, and
406 * take the third part to be a field reference.
407 */
408 if (strcmp(name1, pinfo->fname) != 0)
409 return NULL;
410
411 param = sql_fn_resolve_param_name(pinfo, name2, cref->location);
412
413 subfield = (Node *) lthird(cref->fields);
414 Assert(IsA(subfield, String));
415 }
416 else if (nnames == 2 && strcmp(name1, pinfo->fname) == 0)
417 {
418 /*
419 * Two-part name with first part matching function name: first see if
420 * second part matches any parameter name.
421 */
422 param = sql_fn_resolve_param_name(pinfo, name2, cref->location);
423
424 if (param)
425 {
426 /* Yes, so this is a parameter reference, no subfield */
427 subfield = NULL;
428 }
429 else
430 {
431 /* No, so try to match as parameter name and subfield */
432 param = sql_fn_resolve_param_name(pinfo, name1, cref->location);
433 }
434 }
435 else
436 {
437 /* Single name, or parameter name followed by subfield */
438 param = sql_fn_resolve_param_name(pinfo, name1, cref->location);
439 }
440
441 if (!param)
442 return NULL; /* No match */
443
444 if (subfield)
445 {
446 /*
447 * Must be a reference to a field of a composite parameter; otherwise
448 * ParseFuncOrColumn will return NULL, and we'll fail back at the
449 * caller.
450 */
451 param = ParseFuncOrColumn(pstate,
452 list_make1(subfield),
453 list_make1(param),
454 pstate->p_last_srf,
455 NULL,
456 false,
457 cref->location);
458 }
459
460 return param;
461}
static Node * sql_fn_resolve_param_name(SQLFunctionParseInfoPtr pinfo, const char *paramname, int location)
Definition: functions.c:513
Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, Node *last_srf, FuncCall *fn, bool proc_call, int location)
Definition: parse_func.c:90
#define llast(l)
Definition: pg_list.h:198
#define lthird(l)
Definition: pg_list.h:188
#define lsecond(l)
Definition: pg_list.h:183
ParseLoc location
Definition: parsenodes.h:306
List * fields
Definition: parsenodes.h:305
Node * p_last_srf
Definition: parse_node.h:248
Definition: value.h:64
#define strVal(v)
Definition: value.h:82

References Assert(), ColumnRef::fields, SQLFunctionParseInfo::fname, if(), IsA, linitial, list_length(), list_make1, llast, ColumnRef::location, lsecond, lthird, ParseState::p_last_srf, ParseState::p_ref_hook_state, ParseFuncOrColumn(), sql_fn_resolve_param_name(), and strVal.

Referenced by sql_fn_parser_setup().

◆ sql_fn_resolve_param_name()

static Node * sql_fn_resolve_param_name ( SQLFunctionParseInfoPtr  pinfo,
const char *  paramname,
int  location 
)
static

Definition at line 513 of file functions.c.

515{
516 int i;
517
518 if (pinfo->argnames == NULL)
519 return NULL;
520
521 for (i = 0; i < pinfo->nargs; i++)
522 {
523 if (pinfo->argnames[i] && strcmp(pinfo->argnames[i], paramname) == 0)
524 return sql_fn_make_param(pinfo, i + 1, location);
525 }
526
527 return NULL;
528}

References SQLFunctionParseInfo::argnames, i, SQLFunctionParseInfo::nargs, and sql_fn_make_param().

Referenced by sql_fn_post_column_ref().

◆ sql_postrewrite_callback()

static void sql_postrewrite_callback ( List querytree_list,
void *  arg 
)
static

Definition at line 1219 of file functions.c.

1220{
1221 /*
1222 * Check that there are no statements we don't want to allow. (Presently,
1223 * there's no real point in this because the result can't change from what
1224 * we saw originally. But it's cheap and maybe someday it will matter.)
1225 */
1226 check_sql_fn_statement(querytree_list);
1227
1228 /*
1229 * If this is the last query, we must re-do what check_sql_stmt_retval did
1230 * to its targetlist. Also check that returnsTuple didn't change (it
1231 * probably cannot, but be cautious).
1232 */
1233 if (arg != NULL)
1234 {
1236 bool returnsTuple;
1237
1238 returnsTuple = check_sql_stmt_retval(querytree_list,
1239 func->rettype,
1240 func->rettupdesc,
1241 func->prokind,
1242 false);
1243 if (returnsTuple != func->returnsTuple)
1244 ereport(ERROR,
1245 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1246 errmsg("cached plan must not change result type")));
1247 }
1248}

References arg, check_sql_fn_statement(), check_sql_stmt_retval(), ereport, errcode(), errmsg(), ERROR, SQLFunctionHashEntry::prokind, SQLFunctionHashEntry::rettupdesc, SQLFunctionHashEntry::rettype, and SQLFunctionHashEntry::returnsTuple.

Referenced by prepare_next_query().

◆ sqlfunction_destroy()

static void sqlfunction_destroy ( DestReceiver self)
static

Definition at line 2664 of file functions.c.

2665{
2666 pfree(self);
2667}
void pfree(void *pointer)
Definition: mcxt.c:2152

References pfree().

Referenced by CreateSQLFunctionDestReceiver().

◆ sqlfunction_receive()

static bool sqlfunction_receive ( TupleTableSlot slot,
DestReceiver self 
)
static

Definition at line 2617 of file functions.c.

2618{
2619 DR_sqlfunction *myState = (DR_sqlfunction *) self;
2620
2621 if (myState->tstore)
2622 {
2623 /* We are collecting all of a set result into the tuplestore */
2624
2625 /* Filter tuple as needed */
2626 slot = ExecFilterJunk(myState->filter, slot);
2627
2628 /* Store the filtered tuple into the tuplestore */
2629 tuplestore_puttupleslot(myState->tstore, slot);
2630 }
2631 else
2632 {
2633 /*
2634 * We only want the first tuple, which we'll save in the junkfilter's
2635 * result slot. Ignore any additional tuples passed.
2636 */
2637 if (TTS_EMPTY(myState->filter->jf_resultSlot))
2638 {
2639 /* Filter tuple as needed */
2640 slot = ExecFilterJunk(myState->filter, slot);
2641 Assert(slot == myState->filter->jf_resultSlot);
2642
2643 /* Materialize the slot so it preserves pass-by-ref values */
2644 ExecMaterializeSlot(slot);
2645 }
2646 }
2647
2648 return true;
2649}
TupleTableSlot * ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
Definition: execJunk.c:247
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:742
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:476

References Assert(), ExecFilterJunk(), ExecMaterializeSlot(), DR_sqlfunction::filter, JunkFilter::jf_resultSlot, DR_sqlfunction::tstore, TTS_EMPTY, and tuplestore_puttupleslot().

Referenced by CreateSQLFunctionDestReceiver().

◆ sqlfunction_shutdown()

static void sqlfunction_shutdown ( DestReceiver self)
static

Definition at line 2655 of file functions.c.

2656{
2657 /* no-op */
2658}

Referenced by CreateSQLFunctionDestReceiver().

◆ sqlfunction_startup()

static void sqlfunction_startup ( DestReceiver self,
int  operation,
TupleDesc  typeinfo 
)
static

Definition at line 2608 of file functions.c.

2609{
2610 /* no-op */
2611}

Referenced by CreateSQLFunctionDestReceiver().