Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * nodeTableFuncscan.c
4 : : * Support routines for scanning RangeTableFunc (XMLTABLE like functions).
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/executor/nodeTableFuncscan.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : /*
16 : : * INTERFACE ROUTINES
17 : : * ExecTableFuncScan scans a function.
18 : : * ExecFunctionNext retrieve next tuple in sequential order.
19 : : * ExecInitTableFuncScan creates and initializes a TableFuncscan node.
20 : : * ExecEndTableFuncScan releases any storage allocated.
21 : : * ExecReScanTableFuncScan rescans the function
22 : : */
23 : : #include "postgres.h"
24 : :
25 : : #include "executor/executor.h"
26 : : #include "executor/nodeTableFuncscan.h"
27 : : #include "executor/tablefunc.h"
28 : : #include "miscadmin.h"
29 : : #include "nodes/execnodes.h"
30 : : #include "utils/builtins.h"
31 : : #include "utils/jsonpath.h"
32 : : #include "utils/lsyscache.h"
33 : : #include "utils/memutils.h"
34 : : #include "utils/tuplestore.h"
35 : : #include "utils/xml.h"
36 : :
37 : : static TupleTableSlot *TableFuncNext(TableFuncScanState *node);
38 : : static bool TableFuncRecheck(TableFuncScanState *node, TupleTableSlot *slot);
39 : :
40 : : static void tfuncFetchRows(TableFuncScanState *tstate, ExprContext *econtext);
41 : : static void tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc);
42 : : static void tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext);
43 : :
44 : : /* ----------------------------------------------------------------
45 : : * Scan Support
46 : : * ----------------------------------------------------------------
47 : : */
48 : : /* ----------------------------------------------------------------
49 : : * TableFuncNext
50 : : *
51 : : * This is a workhorse for ExecTableFuncScan
52 : : * ----------------------------------------------------------------
53 : : */
54 : : static TupleTableSlot *
3505 alvherre@alvh.no-ip. 55 :CBC 17139 : TableFuncNext(TableFuncScanState *node)
56 : : {
57 : : TupleTableSlot *scanslot;
58 : :
59 : 17139 : scanslot = node->ss.ss_ScanTupleSlot;
60 : :
61 : : /*
62 : : * If first time through (or first since a parameter change), read all
63 : : * tuples from function and put them in a tuplestore. Subsequent calls
64 : : * just fetch tuples from tuplestore.
65 : : */
7 drowley@postgresql.o 66 [ + + ]:GNC 17139 : if (!node->tupstore_filled)
67 : : {
3505 alvherre@alvh.no-ip. 68 :CBC 590 : tfuncFetchRows(node, node->ss.ps.ps_ExprContext);
7 drowley@postgresql.o 69 :GNC 514 : node->tupstore_filled = true;
70 : : }
71 : :
72 : : /*
73 : : * Get the next tuple from tuplestore.
74 : : */
3505 alvherre@alvh.no-ip. 75 :CBC 17063 : (void) tuplestore_gettupleslot(node->tupstore,
76 : : true,
77 : : false,
78 : : scanslot);
79 : 17063 : return scanslot;
80 : : }
81 : :
82 : : /*
83 : : * TableFuncRecheck -- access method routine to recheck a tuple in EvalPlanQual
84 : : */
85 : : static bool
3505 alvherre@alvh.no-ip. 86 :UBC 0 : TableFuncRecheck(TableFuncScanState *node, TupleTableSlot *slot)
87 : : {
88 : : /* nothing to check */
89 : 0 : return true;
90 : : }
91 : :
92 : : /* ----------------------------------------------------------------
93 : : * ExecTableFuncScan(node)
94 : : *
95 : : * Scans the function sequentially and returns the next qualifying
96 : : * tuple.
97 : : * We call the ExecScan() routine and pass it the appropriate
98 : : * access method functions.
99 : : * ----------------------------------------------------------------
100 : : */
101 : : static TupleTableSlot *
3374 andres@anarazel.de 102 :CBC 17103 : ExecTableFuncScan(PlanState *pstate)
103 : : {
104 : 17103 : TableFuncScanState *node = castNode(TableFuncScanState, pstate);
105 : :
3505 alvherre@alvh.no-ip. 106 : 17103 : return ExecScan(&node->ss,
107 : : (ExecScanAccessMtd) TableFuncNext,
108 : : (ExecScanRecheckMtd) TableFuncRecheck);
109 : : }
110 : :
111 : : /* ----------------------------------------------------------------
112 : : * ExecInitTableFuncScan
113 : : * ----------------------------------------------------------------
114 : : */
115 : : TableFuncScanState *
116 : 482 : ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
117 : : {
118 : : TableFuncScanState *scanstate;
119 : 482 : TableFunc *tf = node->tablefunc;
120 : : TupleDesc tupdesc;
121 : : int i;
122 : :
123 : : /* check for unsupported flags */
124 [ - + ]: 482 : Assert(!(eflags & EXEC_FLAG_MARK));
125 : :
126 : : /*
127 : : * TableFuncscan should not have any children.
128 : : */
129 [ - + ]: 482 : Assert(outerPlan(node) == NULL);
130 [ - + ]: 482 : Assert(innerPlan(node) == NULL);
131 : :
132 : : /*
133 : : * create new ScanState for node
134 : : */
135 : 482 : scanstate = makeNode(TableFuncScanState);
136 : 482 : scanstate->ss.ps.plan = (Plan *) node;
137 : 482 : scanstate->ss.ps.state = estate;
3374 andres@anarazel.de 138 : 482 : scanstate->ss.ps.ExecProcNode = ExecTableFuncScan;
139 : :
140 : : /*
141 : : * Miscellaneous initialization
142 : : *
143 : : * create expression context for node
144 : : */
3505 alvherre@alvh.no-ip. 145 : 482 : ExecAssignExprContext(estate, &scanstate->ss.ps);
146 : :
147 : : /*
148 : : * initialize source tuple type
149 : : */
150 : 482 : tupdesc = BuildDescFromLists(tf->colnames,
151 : 482 : tf->coltypes,
152 : 482 : tf->coltypmods,
153 : 482 : tf->colcollations);
154 : : /* and the corresponding scan slot */
2888 andres@anarazel.de 155 : 482 : ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc,
156 : : &TTSOpsMinimalTuple, 0);
157 : :
158 : : /*
159 : : * Initialize result type and projection.
160 : : */
2894 161 : 482 : ExecInitResultTypeTL(&scanstate->ss.ps);
3505 alvherre@alvh.no-ip. 162 : 482 : ExecAssignScanProjectionInfo(&scanstate->ss);
163 : :
164 : : /*
165 : : * initialize child expressions
166 : : */
3160 andres@anarazel.de 167 : 482 : scanstate->ss.ps.qual =
168 : 482 : ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
169 : :
170 : : /* Only XMLTABLE and JSON_TABLE are supported currently */
921 amitlan@postgresql.o 171 : 482 : scanstate->routine =
172 [ + + ]: 482 : tf->functype == TFT_XMLTABLE ? &XmlTableRoutine : &JsonbTableRoutine;
173 : :
2982 rhodiumtoad@postgres 174 : 482 : scanstate->perTableCxt =
3505 alvherre@alvh.no-ip. 175 : 482 : AllocSetContextCreate(CurrentMemoryContext,
176 : : "TableFunc per value context",
177 : : ALLOCSET_DEFAULT_SIZES);
178 : 482 : scanstate->opaque = NULL; /* initialized at runtime */
7 drowley@postgresql.o 179 :GNC 482 : scanstate->tupstore_filled = false;
180 : :
3505 alvherre@alvh.no-ip. 181 :CBC 482 : scanstate->ns_names = tf->ns_names;
182 : :
3499 andres@anarazel.de 183 : 482 : scanstate->ns_uris =
184 : 482 : ExecInitExprList(tf->ns_uris, (PlanState *) scanstate);
3505 alvherre@alvh.no-ip. 185 : 482 : scanstate->docexpr =
186 : 482 : ExecInitExpr((Expr *) tf->docexpr, (PlanState *) scanstate);
187 : 482 : scanstate->rowexpr =
188 : 482 : ExecInitExpr((Expr *) tf->rowexpr, (PlanState *) scanstate);
3499 andres@anarazel.de 189 : 482 : scanstate->colexprs =
190 : 482 : ExecInitExprList(tf->colexprs, (PlanState *) scanstate);
191 : 482 : scanstate->coldefexprs =
192 : 482 : ExecInitExprList(tf->coldefexprs, (PlanState *) scanstate);
921 amitlan@postgresql.o 193 : 482 : scanstate->colvalexprs =
194 : 482 : ExecInitExprList(tf->colvalexprs, (PlanState *) scanstate);
195 : 482 : scanstate->passingvalexprs =
196 : 482 : ExecInitExprList(tf->passingvalexprs, (PlanState *) scanstate);
197 : :
3505 alvherre@alvh.no-ip. 198 : 482 : scanstate->notnulls = tf->notnulls;
199 : :
200 : : /* these are allocated now and initialized later */
306 michael@paquier.xyz 201 : 482 : scanstate->in_functions = palloc_array(FmgrInfo, tupdesc->natts);
202 : 482 : scanstate->typioparams = palloc_array(Oid, tupdesc->natts);
203 : :
204 : : /*
205 : : * Fill in the necessary fmgr infos.
206 : : */
3505 alvherre@alvh.no-ip. 207 [ + + ]: 1908 : for (i = 0; i < tupdesc->natts; i++)
208 : : {
209 : : Oid in_funcid;
210 : :
3340 andres@anarazel.de 211 : 1426 : getTypeInputInfo(TupleDescAttr(tupdesc, i)->atttypid,
3505 alvherre@alvh.no-ip. 212 : 1426 : &in_funcid, &scanstate->typioparams[i]);
213 : 1426 : fmgr_info(in_funcid, &scanstate->in_functions[i]);
214 : : }
215 : :
216 : 482 : return scanstate;
217 : : }
218 : :
219 : : /* ----------------------------------------------------------------
220 : : * ExecEndTableFuncScan
221 : : *
222 : : * frees any storage allocated through C routines.
223 : : * ----------------------------------------------------------------
224 : : */
225 : : void
226 : 406 : ExecEndTableFuncScan(TableFuncScanState *node)
227 : : {
228 : : /*
229 : : * Release tuplestore resources
230 : : */
231 [ + + ]: 406 : if (node->tupstore != NULL)
232 : 350 : tuplestore_end(node->tupstore);
233 : 406 : node->tupstore = NULL;
234 : 406 : }
235 : :
236 : : /* ----------------------------------------------------------------
237 : : * ExecReScanTableFuncScan
238 : : *
239 : : * Rescans the relation.
240 : : * ----------------------------------------------------------------
241 : : */
242 : : void
243 : 332 : ExecReScanTableFuncScan(TableFuncScanState *node)
244 : : {
245 : 332 : Bitmapset *chgparam = node->ss.ps.chgParam;
246 : :
2894 andres@anarazel.de 247 [ - + ]: 332 : if (node->ss.ps.ps_ResultTupleSlot)
2894 andres@anarazel.de 248 :UBC 0 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
3505 alvherre@alvh.no-ip. 249 :CBC 332 : ExecScanReScan(&node->ss);
250 : :
251 : : /*
252 : : * Recompute when parameters are changed. It's important to use
253 : : * tuplestore_clear() rather than tuplestore_end() here so that we keep
254 : : * track of the maximum storage used by all rescans.
255 : : */
256 [ + - ]: 332 : if (chgparam)
257 : : {
258 [ + + ]: 332 : if (node->tupstore != NULL)
7 drowley@postgresql.o 259 :GNC 164 : tuplestore_clear(node->tupstore);
260 : 332 : node->tupstore_filled = false;
261 : : }
7 drowley@postgresql.o 262 [ # # ]:UNC 0 : else if (node->tupstore != NULL)
3505 alvherre@alvh.no-ip. 263 :UBC 0 : tuplestore_rescan(node->tupstore);
3505 alvherre@alvh.no-ip. 264 :CBC 332 : }
265 : :
266 : : /* ----------------------------------------------------------------
267 : : * tfuncFetchRows
268 : : *
269 : : * Read rows from a TableFunc producer
270 : : * ----------------------------------------------------------------
271 : : */
272 : : static void
273 : 590 : tfuncFetchRows(TableFuncScanState *tstate, ExprContext *econtext)
274 : : {
275 : 590 : const TableFuncRoutine *routine = tstate->routine;
276 : : MemoryContext oldcxt;
277 : : Datum value;
278 : : bool isnull;
279 : :
280 [ - + ]: 590 : Assert(tstate->opaque == NULL);
281 : :
282 : : /* build tuplestore for the result, unless we have one from a prior scan */
283 : 590 : oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
7 drowley@postgresql.o 284 [ + + ]:GNC 590 : if (tstate->tupstore == NULL)
285 : 426 : tstate->tupstore = tuplestore_begin_heap(false, false, work_mem);
286 : :
287 : : /*
288 : : * Each call to fetch a new set of rows - of which there may be very many
289 : : * if XMLTABLE or JSON_TABLE is being used in a lateral join - will
290 : : * allocate a possibly substantial amount of memory, so we cannot use the
291 : : * per-query context here. perTableCxt now serves the same function as
292 : : * "argcontext" does in FunctionScan - a place to store per-one-call (i.e.
293 : : * one result table) lifetime data (as opposed to per-query or
294 : : * per-result-tuple).
295 : : */
2982 rhodiumtoad@postgres 296 :CBC 590 : MemoryContextSwitchTo(tstate->perTableCxt);
297 : :
3505 alvherre@alvh.no-ip. 298 [ + + ]: 590 : PG_TRY();
299 : : {
300 : 590 : routine->InitOpaque(tstate,
3400 tgl@sss.pgh.pa.us 301 : 590 : tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor->natts);
302 : :
303 : : /*
304 : : * If evaluating the document expression returns NULL, the table
305 : : * expression is empty and we return immediately.
306 : : */
3505 alvherre@alvh.no-ip. 307 : 590 : value = ExecEvalExpr(tstate->docexpr, econtext, &isnull);
308 : :
309 [ + + ]: 590 : if (!isnull)
310 : : {
311 : : /* otherwise, pass the document value to the table builder */
312 : 586 : tfuncInitialize(tstate, econtext, value);
313 : :
314 : : /* initialize ordinality counter */
315 : 578 : tstate->ordinal = 1;
316 : :
317 : : /* Load all rows into the tuplestore, and we're done */
318 : 578 : tfuncLoadRows(tstate, econtext);
319 : : }
320 : : }
321 : 76 : PG_CATCH();
322 : : {
323 [ + - ]: 76 : if (tstate->opaque != NULL)
324 : 76 : routine->DestroyOpaque(tstate);
325 : 76 : PG_RE_THROW();
326 : : }
327 [ - + ]: 514 : PG_END_TRY();
328 : :
329 : : /* clean up and return to original memory context */
330 : :
331 [ + - ]: 514 : if (tstate->opaque != NULL)
332 : : {
333 : 514 : routine->DestroyOpaque(tstate);
334 : 514 : tstate->opaque = NULL;
335 : : }
336 : :
2982 rhodiumtoad@postgres 337 : 514 : MemoryContextSwitchTo(oldcxt);
338 : 514 : MemoryContextReset(tstate->perTableCxt);
3505 alvherre@alvh.no-ip. 339 : 514 : }
340 : :
341 : : /*
342 : : * Fill in namespace declarations, the row filter, and column filters in a
343 : : * table expression builder context.
344 : : */
345 : : static void
346 : 586 : tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc)
347 : : {
348 : 586 : const TableFuncRoutine *routine = tstate->routine;
349 : : TupleDesc tupdesc;
350 : : ListCell *lc1,
351 : : *lc2;
352 : : bool isnull;
353 : : int colno;
354 : : Datum value;
355 : 586 : int ordinalitycol =
1242 tgl@sss.pgh.pa.us 356 : 586 : ((TableFuncScan *) (tstate->ss.ps.plan))->tablefunc->ordinalitycol;
357 : :
358 : : /*
359 : : * Install the document as a possibly-toasted Datum into the tablefunc
360 : : * context.
361 : : */
3505 alvherre@alvh.no-ip. 362 : 586 : routine->SetDocument(tstate, doc);
363 : :
364 : : /* Evaluate namespace specifications */
365 [ + + + + : 590 : forboth(lc1, tstate->ns_uris, lc2, tstate->ns_names)
+ + + + +
+ + - +
+ ]
366 : : {
367 : 12 : ExprState *expr = (ExprState *) lfirst(lc1);
1859 peter@eisentraut.org 368 : 12 : String *ns_node = lfirst_node(String, lc2);
369 : : char *ns_uri;
370 : : char *ns_name;
371 : :
314 372 : 12 : value = ExecEvalExpr(expr, econtext, &isnull);
3505 alvherre@alvh.no-ip. 373 [ - + ]: 12 : if (isnull)
3505 alvherre@alvh.no-ip. 374 [ # # ]:UBC 0 : ereport(ERROR,
375 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
376 : : errmsg("namespace URI must not be null")));
3505 alvherre@alvh.no-ip. 377 :CBC 12 : ns_uri = TextDatumGetCString(value);
378 : :
379 : : /* DEFAULT is passed down to SetNamespace as NULL */
2947 tgl@sss.pgh.pa.us 380 [ + + ]: 12 : ns_name = ns_node ? strVal(ns_node) : NULL;
381 : :
3505 alvherre@alvh.no-ip. 382 : 12 : routine->SetNamespace(tstate, ns_name, ns_uri);
383 : : }
384 : :
385 : : /*
386 : : * Install the row filter expression, if any, into the table builder
387 : : * context.
388 : : */
921 amitlan@postgresql.o 389 [ + + ]: 578 : if (routine->SetRowFilter)
390 : : {
391 : 172 : value = ExecEvalExpr(tstate->rowexpr, econtext, &isnull);
392 [ - + ]: 172 : if (isnull)
921 amitlan@postgresql.o 393 [ # # ]:UBC 0 : ereport(ERROR,
394 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
395 : : errmsg("row filter expression must not be null")));
396 : :
921 amitlan@postgresql.o 397 :CBC 172 : routine->SetRowFilter(tstate, TextDatumGetCString(value));
398 : : }
399 : :
400 : : /*
401 : : * Install the column filter expressions into the table builder context.
402 : : * If an expression is given, use that; otherwise the column name itself
403 : : * is the column filter.
404 : : */
3505 alvherre@alvh.no-ip. 405 : 578 : colno = 0;
406 : 578 : tupdesc = tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
407 [ + + + + : 1142 : foreach(lc1, tstate->colexprs)
+ + ]
408 : : {
409 : : char *colfilter;
3340 andres@anarazel.de 410 : 564 : Form_pg_attribute att = TupleDescAttr(tupdesc, colno);
411 : :
3505 alvherre@alvh.no-ip. 412 [ + + ]: 564 : if (colno != ordinalitycol)
413 : : {
414 : 516 : ExprState *colexpr = lfirst(lc1);
415 : :
416 [ + + ]: 516 : if (colexpr != NULL)
417 : : {
418 : 404 : value = ExecEvalExpr(colexpr, econtext, &isnull);
419 [ - + ]: 404 : if (isnull)
3505 alvherre@alvh.no-ip. 420 [ # # ]:UBC 0 : ereport(ERROR,
421 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
422 : : errmsg("column filter expression must not be null"),
423 : : errdetail("Filter for column \"%s\" is null.",
424 : : NameStr(att->attname))));
3505 alvherre@alvh.no-ip. 425 :CBC 404 : colfilter = TextDatumGetCString(value);
426 : : }
427 : : else
3340 andres@anarazel.de 428 : 112 : colfilter = NameStr(att->attname);
429 : :
3505 alvherre@alvh.no-ip. 430 : 516 : routine->SetColumnFilter(tstate, colfilter, colno);
431 : : }
432 : :
433 : 564 : colno++;
434 : : }
435 : 578 : }
436 : :
437 : : /*
438 : : * Load all the rows from the TableFunc table builder into a tuplestore.
439 : : */
440 : : static void
441 : 578 : tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext)
442 : : {
443 : 578 : const TableFuncRoutine *routine = tstate->routine;
444 : 578 : TupleTableSlot *slot = tstate->ss.ss_ScanTupleSlot;
445 : 578 : TupleDesc tupdesc = slot->tts_tupleDescriptor;
446 : 578 : Datum *values = slot->tts_values;
447 : 578 : bool *nulls = slot->tts_isnull;
448 : 578 : int natts = tupdesc->natts;
449 : : MemoryContext oldcxt;
450 : : int ordinalitycol;
451 : :
452 : 578 : ordinalitycol =
453 : 578 : ((TableFuncScan *) (tstate->ss.ps.plan))->tablefunc->ordinalitycol;
454 : :
455 : : /*
456 : : * We need a short-lived memory context that we can clean up each time
457 : : * around the loop, to avoid wasting space. Our default per-tuple context
458 : : * is fine for the job, since we won't have used it for anything yet in
459 : : * this tuple cycle.
460 : : */
2982 rhodiumtoad@postgres 461 : 578 : oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
462 : :
463 : : /*
464 : : * Keep requesting rows from the table builder until there aren't any.
465 : : */
3505 alvherre@alvh.no-ip. 466 [ + + ]: 17127 : while (routine->FetchRow(tstate))
467 : : {
468 : 16617 : ListCell *cell = list_head(tstate->coldefexprs);
469 : : int colno;
470 : :
3366 andres@anarazel.de 471 [ - + ]: 16617 : CHECK_FOR_INTERRUPTS();
472 : :
3505 alvherre@alvh.no-ip. 473 : 16617 : ExecClearTuple(tstate->ss.ss_ScanTupleSlot);
474 : :
475 : : /*
476 : : * Obtain the value of each column for this row, installing them into
477 : : * the slot; then add the tuple to the tuplestore.
478 : : */
479 [ + + ]: 113493 : for (colno = 0; colno < natts; colno++)
480 : : {
3340 andres@anarazel.de 481 : 96944 : Form_pg_attribute att = TupleDescAttr(tupdesc, colno);
482 : :
3505 alvherre@alvh.no-ip. 483 [ + + ]: 96944 : if (colno == ordinalitycol)
484 : : {
485 : : /* Fast path for ordinality column */
486 : 180 : values[colno] = Int32GetDatum(tstate->ordinal++);
487 : 180 : nulls[colno] = false;
488 : : }
489 : : else
490 : : {
491 : : bool isnull;
492 : :
493 : 96764 : values[colno] = routine->GetValue(tstate,
494 : : colno,
495 : : att->atttypid,
496 : : att->atttypmod,
497 : : &isnull);
498 : :
499 : : /* No value? Evaluate and apply the default, if any */
500 [ + + + + ]: 96700 : if (isnull && cell != NULL)
501 : : {
502 : 15311 : ExprState *coldefexpr = (ExprState *) lfirst(cell);
503 : :
504 [ + + ]: 15311 : if (coldefexpr != NULL)
505 : 148 : values[colno] = ExecEvalExpr(coldefexpr, econtext,
506 : : &isnull);
507 : : }
508 : :
509 : : /* Verify a possible NOT NULL constraint */
510 [ + + + + ]: 96700 : if (isnull && bms_is_member(colno, tstate->notnulls))
511 [ + - ]: 4 : ereport(ERROR,
512 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
513 : : errmsg("null is not allowed in column \"%s\"",
514 : : NameStr(att->attname))));
515 : :
516 : 96696 : nulls[colno] = isnull;
517 : : }
518 : :
519 : : /* advance list of default expressions */
520 [ + + ]: 96876 : if (cell != NULL)
2646 tgl@sss.pgh.pa.us 521 : 91794 : cell = lnext(tstate->coldefexprs, cell);
522 : : }
523 : :
3505 alvherre@alvh.no-ip. 524 : 16549 : tuplestore_putvalues(tstate->tupstore, tupdesc, values, nulls);
525 : :
2982 rhodiumtoad@postgres 526 : 16549 : MemoryContextReset(econtext->ecxt_per_tuple_memory);
527 : : }
528 : :
3505 alvherre@alvh.no-ip. 529 : 510 : MemoryContextSwitchTo(oldcxt);
530 : 510 : }
|