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