/* * pglogIterateForeignScan * Read next record from the data file and store it into the * ScanTupleSlot as a virtual tuple */ static TupleTableSlot * pglogIterateForeignScan(ForeignScanState *node) { PgLogExecutionState *festate = (PgLogExecutionState *) node->fdw_state; TupleTableSlot *slot = node->ss.ss_ScanTupleSlot; bool found; ErrorContextCallback errcallback; /* Set up callback to identify error line number. */ errcallback.callback = CopyFromErrorCallback; errcallback.arg = (void *) festate->cstate; errcallback.previous = error_context_stack; error_context_stack = &errcallback; /* * The protocol for loading a virtual tuple into a slot is first * ExecClearTuple, then fill the values/isnull arrays, then * ExecStoreVirtualTuple. If we don't find another row in the file, we * just skip the last step, leaving the slot empty as required. * * We can pass ExprContext = NULL because we read all columns from the * file, so no need to evaluate default expressions. * * We can also pass tupleOid = NULL because we don't allow oids for * foreign tables. */ ExecClearTuple(slot); found = GetNextRow(node->ss.ss_currentRelation, festate, slot); if (found) ExecStoreVirtualTuple(slot); else if (! isLastLogFile(festate)) { /* We could have reached the end of a log file * We might have to start reading from the next */ elog(DEBUG1,"Reached end of file %s",festate->filenames[festate->i]); festate->i++; BeginNextCopy(node->ss.ss_currentRelation, festate); found = GetNextRow(node->ss.ss_currentRelation, festate, slot); if (found) ExecStoreVirtualTuple(slot); } /* Remove error callback. */ error_context_stack = errcallback.previous; return slot; }
/* * StoreIndexTuple * Fill the slot with data from the index tuple. * * At some point this might be generally-useful functionality, but * right now we don't need it elsewhere. */ static void StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup, TupleDesc itupdesc) { int nindexatts = itupdesc->natts; Datum *values = slot->tts_values; bool *isnull = slot->tts_isnull; int i; /* * Note: we must use the tupdesc supplied by the AM in index_getattr, not * the slot's tupdesc, in case the latter has different datatypes (this * happens for btree name_ops in particular). They'd better have the same * number of columns though, as well as being datatype-compatible which is * something we can't so easily check. */ Assert(slot->tts_tupleDescriptor->natts == nindexatts); ExecClearTuple(slot); for (i = 0; i < nindexatts; i++) values[i] = index_getattr(itup, i + 1, itupdesc, &isnull[i]); ExecStoreVirtualTuple(slot); }
/* -------------------------------- * ExecStoreAllNullTuple * Set up the slot to contain a null in every column. * * At first glance this might sound just like ExecClearTuple, but it's * entirely different: the slot ends up full, not empty. * -------------------------------- */ TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot) { /* * sanity checks */ Assert(slot != NULL); Assert(slot->tts_tupleDescriptor != NULL); /* Clear any old contents */ ExecClearTuple(slot); /* * Fill all the columns of the virtual tuple with nulls */ MemSet(slot->tts_values, 0, slot->tts_tupleDescriptor->natts * sizeof(Datum)); memset(slot->tts_isnull, true, slot->tts_tupleDescriptor->natts * sizeof(bool)); return ExecStoreVirtualTuple(slot); }
/* ---------------------------------------------------------------- * ValuesNext * * This is a workhorse for ExecValuesScan * ---------------------------------------------------------------- */ static TupleTableSlot * ValuesNext(ValuesScanState *node) { TupleTableSlot *slot; EState *estate; ExprContext *econtext; ScanDirection direction; List *exprlist; /* * get information from the estate and scan state */ estate = node->ss.ps.state; direction = estate->es_direction; slot = node->ss.ss_ScanTupleSlot; econtext = node->rowcontext; /* * Get the next tuple. Return NULL if no more tuples. */ if (ScanDirectionIsForward(direction)) { if (node->curr_idx < node->array_len) node->curr_idx++; if (node->curr_idx < node->array_len) exprlist = node->exprlists[node->curr_idx]; else exprlist = NIL; } else { if (node->curr_idx >= 0) node->curr_idx--; if (node->curr_idx >= 0) exprlist = node->exprlists[node->curr_idx]; else exprlist = NIL; } /* * Always clear the result slot; this is appropriate if we are at the end * of the data, and if we're not, we still need it as the first step of * the store-virtual-tuple protocol. It seems wise to clear the slot * before we reset the context it might have pointers into. */ ExecClearTuple(slot); if (exprlist) { MemoryContext oldContext; List *exprstatelist; Datum *values; bool *isnull; ListCell *lc; int resind; /* * Get rid of any prior cycle's leftovers. We use ReScanExprContext * not just ResetExprContext because we want any registered shutdown * callbacks to be called. */ ReScanExprContext(econtext); /* * Build the expression eval state in the econtext's per-tuple memory. * This is a tad unusual, but we want to delete the eval state again * when we move to the next row, to avoid growth of memory * requirements over a long values list. */ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * Pass NULL, not my plan node, because we don't want anything in this * transient state linking into permanent state. The only possibility * is a SubPlan, and there shouldn't be any (any subselects in the * VALUES list should be InitPlans). */ exprstatelist = (List *) ExecInitExpr((Expr *) exprlist, NULL); /* parser should have checked all sublists are the same length */ Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts); /* * Compute the expressions and build a virtual result tuple. We * already did ExecClearTuple(slot). */ values = slot->tts_values; isnull = slot->tts_isnull; resind = 0; foreach(lc, exprstatelist) { ExprState *estate = (ExprState *) lfirst(lc); values[resind] = ExecEvalExpr(estate, econtext, &isnull[resind], NULL); resind++; } MemoryContextSwitchTo(oldContext); /* * And return the virtual tuple. */ ExecStoreVirtualTuple(slot); }
/* * Modify slot with user data provided as C strings. * This is somewhat similar to heap_modify_tuple but also calls the type * input function on the user data as the input is the text representation * of the types. */ static void slot_modify_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel, char **values, bool *replaces) { int natts = slot->tts_tupleDescriptor->natts; int i; SlotErrCallbackArg errarg; ErrorContextCallback errcallback; slot_getallattrs(slot); ExecClearTuple(slot); /* Push callback + info on the error context stack */ errarg.rel = rel; errarg.local_attnum = -1; errarg.remote_attnum = -1; errcallback.callback = slot_store_error_callback; errcallback.arg = (void *) &errarg; errcallback.previous = error_context_stack; error_context_stack = &errcallback; /* Call the "in" function for each replaced attribute */ for (i = 0; i < natts; i++) { Form_pg_attribute att = TupleDescAttr(slot->tts_tupleDescriptor, i); int remoteattnum = rel->attrmap[i]; if (remoteattnum < 0) continue; if (!replaces[remoteattnum]) continue; if (values[remoteattnum] != NULL) { Oid typinput; Oid typioparam; errarg.local_attnum = i; errarg.remote_attnum = remoteattnum; getTypeInputInfo(att->atttypid, &typinput, &typioparam); slot->tts_values[i] = OidInputFunctionCall(typinput, values[remoteattnum], typioparam, att->atttypmod); slot->tts_isnull[i] = false; errarg.local_attnum = -1; errarg.remote_attnum = -1; } else { slot->tts_values[i] = (Datum) 0; slot->tts_isnull[i] = true; } } /* Pop the error context stack */ error_context_stack = errcallback.previous; ExecStoreVirtualTuple(slot); }
/* * Store data in C string form into slot. * This is similar to BuildTupleFromCStrings but TupleTableSlot fits our * use better. */ static void slot_store_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel, char **values) { int natts = slot->tts_tupleDescriptor->natts; int i; SlotErrCallbackArg errarg; ErrorContextCallback errcallback; ExecClearTuple(slot); /* Push callback + info on the error context stack */ errarg.rel = rel; errarg.local_attnum = -1; errarg.remote_attnum = -1; errcallback.callback = slot_store_error_callback; errcallback.arg = (void *) &errarg; errcallback.previous = error_context_stack; error_context_stack = &errcallback; /* Call the "in" function for each non-dropped attribute */ for (i = 0; i < natts; i++) { Form_pg_attribute att = TupleDescAttr(slot->tts_tupleDescriptor, i); int remoteattnum = rel->attrmap[i]; if (!att->attisdropped && remoteattnum >= 0 && values[remoteattnum] != NULL) { Oid typinput; Oid typioparam; errarg.local_attnum = i; errarg.remote_attnum = remoteattnum; getTypeInputInfo(att->atttypid, &typinput, &typioparam); slot->tts_values[i] = OidInputFunctionCall(typinput, values[remoteattnum], typioparam, att->atttypmod); slot->tts_isnull[i] = false; errarg.local_attnum = -1; errarg.remote_attnum = -1; } else { /* * We assign NULL to dropped attributes, NULL values, and missing * values (missing values should be later filled using * slot_fill_defaults). */ slot->tts_values[i] = (Datum) 0; slot->tts_isnull[i] = true; } } /* Pop the error context stack */ error_context_stack = errcallback.previous; ExecStoreVirtualTuple(slot); }
/* * Use the supplied ResultRelInfo to create an appropriately restructured * version of the tuple in the supplied slot, if necessary. * * slot -- slot containing the input tuple * resultRelInfo -- info pertaining to the target part of an insert * * If no restructuring is required, the result is the argument slot, else * it is the slot from the argument result info updated to hold the * restructured tuple. */ TupleTableSlot * reconstructMatchingTupleSlot(TupleTableSlot *slot, ResultRelInfo *resultRelInfo) { int natts; Datum *values; bool *isnull; AttrMap *map; TupleTableSlot *partslot; Datum *partvalues; bool *partisnull; map = resultRelInfo->ri_partInsertMap; TupleDesc inputTupDesc = slot->tts_tupleDescriptor; TupleDesc resultTupDesc = resultRelInfo->ri_RelationDesc->rd_att; bool tupleDescMatch = (resultRelInfo->tupdesc_match == 1); if (resultRelInfo->tupdesc_match == 0) { tupleDescMatch = equalTupleDescs(inputTupDesc, resultTupDesc, false); if (tupleDescMatch) { resultRelInfo->tupdesc_match = 1; } else { resultRelInfo->tupdesc_match = -1; } } /* No map and matching tuple descriptor means no restructuring needed. */ if (map == NULL && tupleDescMatch) return slot; /* Put the given tuple into attribute arrays. */ natts = slot->tts_tupleDescriptor->natts; slot_getallattrs(slot); values = slot_get_values(slot); isnull = slot_get_isnull(slot); /* * Get the target slot ready. If this is a child partition table, * set target slot to ri_partSlot. Otherwise, use ri_resultSlot. */ if (map != NULL) { Assert(resultRelInfo->ri_partSlot != NULL); partslot = resultRelInfo->ri_partSlot; } else { if (resultRelInfo->ri_resultSlot == NULL) { resultRelInfo->ri_resultSlot = MakeSingleTupleTableSlot(resultTupDesc); } partslot = resultRelInfo->ri_resultSlot; } partslot = ExecStoreAllNullTuple(partslot); partvalues = slot_get_values(partslot); partisnull = slot_get_isnull(partslot); /* Restructure the input tuple. Non-zero map entries are attribute * numbers in the target tuple, however, not every attribute * number of the input tuple need be present. In particular, * attribute numbers corresponding to dropped attributes will be * missing. */ reconstructTupleValues(map, values, isnull, natts, partvalues, partisnull, partslot->tts_tupleDescriptor->natts); partslot = ExecStoreVirtualTuple(partslot); return partslot; }
/* ---------------------------------------------------------------- * ValuesNext * * This is a workhorse for ExecValuesScan * ---------------------------------------------------------------- */ static TupleTableSlot * ValuesNext(ValuesScanState *node) { TupleTableSlot *slot; EState *estate; ExprContext *econtext; ScanDirection direction; List *exprlist; /* * get information from the estate and scan state */ estate = node->ss.ps.state; direction = estate->es_direction; slot = node->ss.ss_ScanTupleSlot; econtext = node->rowcontext; /* * Get the next tuple. Return NULL if no more tuples. */ if (ScanDirectionIsForward(direction)) { if (node->curr_idx < node->array_len) node->curr_idx++; if (node->curr_idx < node->array_len) exprlist = node->exprlists[node->curr_idx]; else exprlist = NIL; } else { if (node->curr_idx >= 0) node->curr_idx--; if (node->curr_idx >= 0) exprlist = node->exprlists[node->curr_idx]; else exprlist = NIL; } /* * Always clear the result slot; this is appropriate if we are at the end * of the data, and if we're not, we still need it as the first step of * the store-virtual-tuple protocol. It seems wise to clear the slot * before we reset the context it might have pointers into. */ ExecClearTuple(slot); if (exprlist) { MemoryContext oldContext; List *oldsubplans; List *exprstatelist; Datum *values; bool *isnull; ListCell *lc; int resind; int saved_jit_flags; /* * Get rid of any prior cycle's leftovers. We use ReScanExprContext * not just ResetExprContext because we want any registered shutdown * callbacks to be called. */ ReScanExprContext(econtext); /* * Build the expression eval state in the econtext's per-tuple memory. * This is a tad unusual, but we want to delete the eval state again * when we move to the next row, to avoid growth of memory * requirements over a long values list. */ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * The expressions might contain SubPlans (this is currently only * possible if there's a sub-select containing a LATERAL reference, * otherwise sub-selects in a VALUES list should be InitPlans). Those * subplans will want to hook themselves into our subPlan list, which * would result in a corrupted list after we delete the eval state. We * can work around this by saving and restoring the subPlan list. * (There's no need for the functionality that would be enabled by * having the list entries, since the SubPlans aren't going to be * re-executed anyway.) */ oldsubplans = node->ss.ps.subPlan; node->ss.ps.subPlan = NIL; /* * As the expressions are only ever used once, disable JIT for them. * This is worthwhile because it's common to insert significant * amounts of data via VALUES(). */ saved_jit_flags = econtext->ecxt_estate->es_jit_flags; econtext->ecxt_estate->es_jit_flags = PGJIT_NONE; exprstatelist = ExecInitExprList(exprlist, &node->ss.ps); econtext->ecxt_estate->es_jit_flags = saved_jit_flags; node->ss.ps.subPlan = oldsubplans; /* parser should have checked all sublists are the same length */ Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts); /* * Compute the expressions and build a virtual result tuple. We * already did ExecClearTuple(slot). */ values = slot->tts_values; isnull = slot->tts_isnull; resind = 0; foreach(lc, exprstatelist) { ExprState *estate = (ExprState *) lfirst(lc); Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor, resind); values[resind] = ExecEvalExpr(estate, econtext, &isnull[resind]); /* * We must force any R/W expanded datums to read-only state, in * case they are multiply referenced in the plan node's output * expressions, or in case we skip the output projection and the * output column is multiply referenced in higher plan nodes. */ values[resind] = MakeExpandedObjectReadOnly(values[resind], isnull[resind], attr->attlen); resind++; } MemoryContextSwitchTo(oldContext); /* * And return the virtual tuple. */ ExecStoreVirtualTuple(slot); }