static void AOCSMoveTuple(TupleTableSlot *slot, AOCSInsertDesc insertDesc, ResultRelInfo *resultRelInfo, EState *estate) { AOTupleId *oldAoTupleId; AOTupleId newAoTupleId; Assert(resultRelInfo); Assert(slot); Assert(estate); oldAoTupleId = (AOTupleId *) slot_get_ctid(slot); /* Extract all the values of the tuple */ slot_getallattrs(slot); (void) aocs_insert_values(insertDesc, slot_get_values(slot), slot_get_isnull(slot), &newAoTupleId); /* insert index' tuples if needed */ if (resultRelInfo->ri_NumIndices > 0) { ExecInsertIndexTuples(slot, (ItemPointer) &newAoTupleId, estate); ResetPerTupleExprContext(estate); } elogif(Debug_appendonly_print_compaction, DEBUG5, "Compaction: Moved tuple (%d," INT64_FORMAT ") -> (%d," INT64_FORMAT ")", AOTupleIdGet_segmentFileNum(oldAoTupleId), AOTupleIdGet_rowNum(oldAoTupleId), AOTupleIdGet_segmentFileNum(&newAoTupleId), AOTupleIdGet_rowNum(&newAoTupleId)); }
/* -------------------------------- * ExecCopySlotHeadTupleTo * Copy heapTuple to a preallocated buffer. Code adapted from ExecCopySlotTuple * * return the copied heaptule if there is enough space, or, if the memorycontext is * not null, which the function will alloc enough space from the context. One can * test if the tuple is alloced (ret == dest) * * return NULL and set *len to space need if there is not enough space and the mem context is null. * return NULL if heap tuple is not valid, and set *len = 0. See slot->tts_tuple case below. * ------------------------------- */ HeapTuple ExecCopySlotHeapTupleTo(TupleTableSlot *slot, MemoryContext pctxt, char* dest, unsigned int *len) { uint32 dumlen; HeapTuple tup = NULL; Assert(!TupIsNull(slot)); Assert(slot->tts_tupleDescriptor); if(!len) len = &dumlen; if (slot->PRIVATE_tts_heaptuple) { tup = heaptuple_copy_to(slot->PRIVATE_tts_heaptuple, (HeapTuple) dest, len); if(tup || !pctxt) return tup; tup = (HeapTuple) ctxt_alloc(pctxt, *len); tup = heaptuple_copy_to(slot->PRIVATE_tts_heaptuple, tup, len); Assert(tup); return tup; } slot_getallattrs(slot); tup = heaptuple_form_to(slot->tts_tupleDescriptor, slot_get_values(slot), slot_get_isnull(slot), (HeapTuple) dest, len); if(tup || !pctxt) return tup; tup = (HeapTuple) ctxt_alloc(pctxt, *len); tup = heaptuple_form_to(slot->tts_tupleDescriptor, slot_get_values(slot), slot_get_isnull(slot), tup, len); Assert(tup); return tup; }
void AppendOnlyThrowAwayTuple( Relation rel, MemTuple tuple, TupleTableSlot *slot, MemTupleBinding *mt_bind) { AOTupleId *oldAoTupleId; Assert(slot); Assert(mt_bind); oldAoTupleId = (AOTupleId*)slot_get_ctid(slot); /* Extract all the values of the tuple */ slot_getallattrs(slot); if (MemTupleHasExternal(tuple, mt_bind)) { toast_delete(rel, (HeapTuple) tuple, mt_bind); } elogif(Debug_appendonly_print_compaction, DEBUG5, "Compaction: Throw away tuple (%d," INT64_FORMAT ")", AOTupleIdGet_segmentFileNum(oldAoTupleId), AOTupleIdGet_rowNum(oldAoTupleId)); }
MemTuple ExecCopySlotMemTupleTo(TupleTableSlot *slot, MemoryContext pctxt, char *dest, unsigned int *len) { uint32 dumlen; MemTuple mtup = NULL; Assert(!TupIsNull(slot)); Assert(slot->tts_mt_bind); if(!len) len = &dumlen; if (TupHasMemTuple(slot)) { mtup = memtuple_copy_to(slot->PRIVATE_tts_memtuple, slot->tts_mt_bind, (MemTuple) dest, len); if(mtup || !pctxt) return mtup; mtup = (MemTuple) ctxt_alloc(pctxt, *len); mtup = memtuple_copy_to(slot->PRIVATE_tts_memtuple, slot->tts_mt_bind, mtup, len); Assert(mtup); return mtup; } slot_getallattrs(slot); mtup = memtuple_form_to(slot->tts_mt_bind, slot_get_values(slot), slot_get_isnull(slot), (MemTuple) dest, len, false); if(mtup || !pctxt) return mtup; mtup = (MemTuple) ctxt_alloc(pctxt, *len); mtup = memtuple_form_to(slot->tts_mt_bind, slot_get_values(slot), slot_get_isnull(slot), mtup, len, false); Assert(mtup); return mtup; }
/* XXX * This function is not very efficient. We should detech if we can modify * the memtuple inline so no deform/form is needed */ void ExecModifyMemTuple(TupleTableSlot *slot, Datum *values, bool *isnull, bool *doRepl) { int i; MemTuple mtup; uint32 tuplen; Assert(slot->PRIVATE_tts_memtuple); /* First, get all the attrs. Note we set PRIVATE_tts_nvalid to 0 * so we force the attrs are from memtuple */ slot->PRIVATE_tts_nvalid = 0; slot_getallattrs(slot); /* Next, we construct a new memtuple, on the htup buf to avoid palloc */ slot->PRIVATE_tts_heaptuple = NULL; for(i = 0; i<slot->tts_tupleDescriptor->natts; ++i) { if(doRepl[i]) { slot->PRIVATE_tts_values[i] = values[i]; slot->PRIVATE_tts_isnull[i] = isnull[i]; } } tuplen = slot->PRIVATE_tts_htup_buf_len; mtup = memtuple_form_to(slot->tts_mt_bind, slot->PRIVATE_tts_values, slot->PRIVATE_tts_isnull, slot->PRIVATE_tts_htup_buf, &tuplen, false); if(!mtup) { slot->PRIVATE_tts_htup_buf = MemoryContextAlloc(slot->tts_mcxt, tuplen); slot->PRIVATE_tts_htup_buf_len = tuplen; mtup = memtuple_form_to(slot->tts_mt_bind, slot->PRIVATE_tts_values, slot->PRIVATE_tts_isnull, slot->PRIVATE_tts_htup_buf, &tuplen, false); Assert(mtup); } /* Check if we need to free this mem tuple */ if(TupShouldFree(slot) && slot->PRIVATE_tts_memtuple && slot->PRIVATE_tts_memtuple != slot->PRIVATE_tts_mtup_buf ) pfree(slot->PRIVATE_tts_memtuple); slot->PRIVATE_tts_memtuple = mtup; /* swap mtup_buf and htup_buf stuff */ mtup = (MemTuple) slot->PRIVATE_tts_mtup_buf; tuplen = slot->PRIVATE_tts_mtup_buf_len; slot->PRIVATE_tts_mtup_buf = slot->PRIVATE_tts_htup_buf; slot->PRIVATE_tts_mtup_buf_len = slot->PRIVATE_tts_htup_buf_len; slot->PRIVATE_tts_htup_buf = (void *) mtup; slot->PRIVATE_tts_htup_buf_len = tuplen; /* don't forget to reset PRIVATE_tts_nvalid, because we modified the memtuple */ slot->PRIVATE_tts_nvalid = 0; }
/* * Receive a tuple from the executor and store it in the tuplestore. * This is for the case where we have to detoast any toasted values. */ static void tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self) { TStoreState *myState = (TStoreState *) self; TupleDesc typeinfo = slot->tts_tupleDescriptor; Form_pg_attribute *attrs = typeinfo->attrs; int natts = typeinfo->natts; int nfree; int i; HeapTuple tuple; MemoryContext oldcxt; /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); /* * Fetch back any out-of-line datums. We build the new datums array in * myState->outvalues[] (but we can re-use the slot's isnull array). * Also, remember the fetched values to free afterwards. */ nfree = 0; for (i = 0; i < natts; i++) { Datum val = slot->tts_values[i]; if (!attrs[i]->attisdropped && attrs[i]->attlen == -1 && !slot->tts_isnull[i]) { if (VARATT_IS_EXTERNAL(DatumGetPointer(val))) { val = PointerGetDatum(heap_tuple_fetch_attr((varattrib *) DatumGetPointer(val))); myState->tofree[nfree++] = val; } } myState->outvalues[i] = val; } /* * Push the modified tuple into the tuplestore. */ tuple = heap_form_tuple(typeinfo, myState->outvalues, slot->tts_isnull); oldcxt = MemoryContextSwitchTo(myState->cxt); tuplestore_puttuple(myState->tstore, tuple); MemoryContextSwitchTo(oldcxt); heap_freetuple(tuple); /* And release any temporary detoasted values */ for (i = 0; i < nfree; i++) pfree(DatumGetPointer(myState->tofree[i])); }
Oid parquet_insert(ParquetInsertDesc parquetInsertDesc, TupleTableSlot *slot) { Oid oid; AOTupleId aotid; slot_getallattrs(slot); oid = parquet_insert_values(parquetInsertDesc, slot_get_values(slot), slot_get_isnull(slot), &aotid); slot_set_ctid(slot, (ItemPointer)&aotid); return oid; }
/* -------------------------------- * ExecFetchSlotMinimalTuple * Fetch the slot's minimal physical tuple. * * If the slot contains a virtual tuple, we convert it to minimal * physical form. The slot retains ownership of the physical tuple. * Likewise, if it contains a regular tuple we convert to minimal form. * * As above, the result must be treated as read-only. * -------------------------------- */ MemTuple ExecFetchSlotMemTuple(TupleTableSlot *slot, bool inline_toast) { MemTuple newTuple; MemTuple oldTuple = NULL; uint32 tuplen; Assert(!TupIsNull(slot)); Assert(slot->tts_mt_bind); if(slot->PRIVATE_tts_memtuple) { if(!inline_toast || !memtuple_get_hasext(slot->PRIVATE_tts_memtuple, slot->tts_mt_bind)) return slot->PRIVATE_tts_memtuple; oldTuple = slot->PRIVATE_tts_mtup_buf; slot->PRIVATE_tts_mtup_buf = NULL; slot->PRIVATE_tts_mtup_buf_len = 0; } slot_getallattrs(slot); tuplen = slot->PRIVATE_tts_mtup_buf_len; newTuple = memtuple_form_to(slot->tts_mt_bind, slot_get_values(slot), slot_get_isnull(slot), (MemTuple) slot->PRIVATE_tts_mtup_buf, &tuplen, inline_toast); if(!newTuple) { if(slot->PRIVATE_tts_mtup_buf) pfree(slot->PRIVATE_tts_mtup_buf); slot->PRIVATE_tts_mtup_buf = MemoryContextAlloc(slot->tts_mcxt, tuplen); slot->PRIVATE_tts_mtup_buf_len = tuplen; newTuple = memtuple_form_to(slot->tts_mt_bind, slot_get_values(slot), slot_get_isnull(slot), (MemTuple) slot->PRIVATE_tts_mtup_buf, &tuplen, inline_toast); } Assert(newTuple); slot->PRIVATE_tts_memtuple = newTuple; if(oldTuple) pfree(oldTuple); return newTuple; }
/* -------------------------------- * ExecFetchSlotTuple * Fetch the slot's regular physical tuple. * * If the slot contains a virtual tuple, we convert it to physical * form. The slot retains ownership of the physical tuple. * Likewise, if it contains a minimal tuple we convert to regular form. * * The difference between this and ExecMaterializeSlot() is that this * does not guarantee that the contained tuple is local storage. * Hence, the result must be treated as read-only. * -------------------------------- */ HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot) { uint32 tuplen; HeapTuple htup; /* * sanity checks */ Assert(!TupIsNull(slot)); /* * If we have a regular physical tuple then just return it. */ if(slot->PRIVATE_tts_heaptuple) return slot->PRIVATE_tts_heaptuple; slot_getallattrs(slot); Assert(TupHasVirtualTuple(slot)); Assert(slot->PRIVATE_tts_nvalid == slot->tts_tupleDescriptor->natts); tuplen = slot->PRIVATE_tts_htup_buf_len; htup = heaptuple_form_to(slot->tts_tupleDescriptor, slot_get_values(slot), slot_get_isnull(slot), slot->PRIVATE_tts_htup_buf, &tuplen); if(!htup) { if(slot->PRIVATE_tts_htup_buf) pfree(slot->PRIVATE_tts_htup_buf); slot->PRIVATE_tts_htup_buf = (HeapTuple) MemoryContextAlloc(slot->tts_mcxt, tuplen); slot->PRIVATE_tts_htup_buf_len = tuplen; htup = heaptuple_form_to(slot->tts_tupleDescriptor, slot_get_values(slot), slot_get_isnull(slot), slot->PRIVATE_tts_htup_buf, &tuplen); Assert(htup); } slot->PRIVATE_tts_heaptuple = htup; return htup; }
/* -------------------------------- * ExecCopySlotTuple * Obtain a copy of a slot's regular physical tuple. The copy is * palloc'd in the current memory context. * * This works even if the slot contains a virtual or minimal tuple; * however the "system columns" of the result will not be meaningful. * -------------------------------- */ HeapTuple ExecCopySlotHeapTuple(TupleTableSlot *slot) { /* * sanity checks */ Assert(!TupIsNull(slot)); if(slot->PRIVATE_tts_heaptuple) return heap_copytuple(slot->PRIVATE_tts_heaptuple); slot_getallattrs(slot); /* * Otherwise we need to build a tuple from the Datum array. */ return heap_form_tuple(slot->tts_tupleDescriptor, slot_get_values(slot), slot_get_isnull(slot)); }
/* -------------------------------- * ExecCopySlotMinimalTuple * Obtain a copy of a slot's minimal physical tuple. The copy is * palloc'd in the current memory context. * -------------------------------- */ MemTuple ExecCopySlotMemTuple(TupleTableSlot *slot) { /* * sanity checks */ Assert(!TupIsNull(slot)); Assert(slot->tts_mt_bind); /* * If we have a physical tuple then just copy it. */ if (slot->PRIVATE_tts_memtuple) return memtuple_copy_to(slot->PRIVATE_tts_memtuple, slot->tts_mt_bind, NULL, NULL); slot_getallattrs(slot); /* * Otherwise we need to build a tuple from the Datum array. */ return memtuple_form_to(slot->tts_mt_bind, slot_get_values(slot), slot_get_isnull(slot), NULL, 0, false); }
static void AppendOnlyMoveTuple(MemTuple tuple, TupleTableSlot *slot, MemTupleBinding *mt_bind, AppendOnlyInsertDesc insertDesc, ResultRelInfo *resultRelInfo, EState *estate) { AOTupleId *oldAoTupleId; Oid tupleOid; AOTupleId newAoTupleId; Assert(resultRelInfo); Assert(slot); Assert(mt_bind); Assert(estate); oldAoTupleId = (AOTupleId*)slot_get_ctid(slot); /* Extract all the values of the tuple */ slot_getallattrs(slot); tupleOid = MemTupleGetOid(tuple, mt_bind); appendonly_insert(insertDesc, tuple, &tupleOid, &newAoTupleId); /* insert index' tuples if needed */ if (resultRelInfo->ri_NumIndices > 0) { ExecInsertIndexTuples(slot, (ItemPointer)&newAoTupleId, estate, true); ResetPerTupleExprContext(estate); } elogif(Debug_appendonly_print_compaction, DEBUG5, "Compaction: Moved tuple (%d," INT64_FORMAT ") -> (%d," INT64_FORMAT ")", AOTupleIdGet_segmentFileNum(oldAoTupleId), AOTupleIdGet_rowNum(oldAoTupleId), AOTupleIdGet_segmentFileNum(&newAoTupleId), AOTupleIdGet_rowNum(&newAoTupleId)); }
/* * 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); }
/* ---------------------------------------------------------------- * ExecPartitionSelector(node) * * Compute and propagate partition table Oids that will be * used by Dynamic table scan. There are two ways of * executing PartitionSelector. * * 1. Constant partition elimination * Plan structure: * Sequence * |--PartitionSelector * |--DynamicTableScan * In this case, PartitionSelector evaluates constant partition * constraints to compute and propagate partition table Oids. * It only need to be called once. * * 2. Join partition elimination * Plan structure: * ...: * |--DynamicTableScan * |--... * |--PartitionSelector * |--... * In this case, PartitionSelector is in the same slice as * DynamicTableScan, DynamicIndexScan or DynamicBitmapHeapScan. * It is executed for each tuple coming from its child node. * It evaluates partition constraints with the input tuple and * propagate matched partition table Oids. * * * Instead of a Dynamic Table Scan, there can be other nodes that use * a PartSelected qual to filter rows, based on which partitions are * selected. Currently, ORCA uses Dynamic Table Scans, while plans * produced by the non-ORCA planner use gating Result nodes with * PartSelected quals, to exclude unwanted partitions. * * ---------------------------------------------------------------- */ TupleTableSlot * ExecPartitionSelector(PartitionSelectorState *node) { PartitionSelector *ps = (PartitionSelector *) node->ps.plan; EState *estate = node->ps.state; ExprContext *econtext = node->ps.ps_ExprContext; TupleTableSlot *inputSlot = NULL; TupleTableSlot *candidateOutputSlot = NULL; if (ps->staticSelection) { /* propagate the part oids obtained via static partition selection */ partition_propagation(estate, ps->staticPartOids, ps->staticScanIds, ps->selectorId); return NULL; } /* Retrieve PartitionNode and access method from root table. * We cannot do it during node initialization as * DynamicTableScanInfo is not properly initialized yet. */ if (NULL == node->rootPartitionNode) { Assert(NULL != estate->dynamicTableScanInfo); getPartitionNodeAndAccessMethod ( ps->relid, estate->dynamicTableScanInfo->partsMetadata, estate->es_query_cxt, &node->rootPartitionNode, &node->accessMethods ); } if (NULL != outerPlanState(node)) { /* Join partition elimination */ /* get tuple from outer children */ PlanState *outerPlan = outerPlanState(node); Assert(outerPlan); inputSlot = ExecProcNode(outerPlan); if (TupIsNull(inputSlot)) { /* no more tuples from outerPlan */ /* * Make sure we have an entry for this scan id in * dynamicTableScanInfo. Normally, this would've been done the * first time a partition is selected, but we must ensure that * there is an entry even if no partitions were selected. * (The traditional Postgres planner uses this method.) */ if (ps->partTabTargetlist) InsertPidIntoDynamicTableScanInfo(estate, ps->scanId, InvalidOid, ps->selectorId); else LogPartitionSelection(estate, ps->selectorId); return NULL; } } /* partition elimination with the given input tuple */ ResetExprContext(econtext); node->ps.ps_OuterTupleSlot = inputSlot; econtext->ecxt_outertuple = inputSlot; econtext->ecxt_scantuple = inputSlot; if (NULL != inputSlot) { candidateOutputSlot = ExecProject(node->ps.ps_ProjInfo, NULL); } /* * If we have a partitioning projection, project the input tuple * into a tuple that looks like tuples from the partitioned table, and use * selectPartitionMulti() to select the partitions. (The traditional * Postgres planner uses this method.) */ if (ps->partTabTargetlist) { TupleTableSlot *slot; List *oids; ListCell *lc; slot = ExecProject(node->partTabProj, NULL); slot_getallattrs(slot); oids = selectPartitionMulti(node->rootPartitionNode, slot_get_values(slot), slot_get_isnull(slot), slot->tts_tupleDescriptor, node->accessMethods); foreach (lc, oids) { InsertPidIntoDynamicTableScanInfo(estate, ps->scanId, lfirst_oid(lc), ps->selectorId); }
/* ---------------- * printtup_internal_20 --- print a binary tuple in protocol 2.0 * * We use a different message type, i.e. 'B' instead of 'D' to * indicate a tuple in internal (binary) form. * * This is largely same as printtup_20, except we use binary formatting. * ---------------- */ static void printtup_internal_20(TupleTableSlot *slot, DestReceiver *self) { TupleDesc typeinfo = slot->tts_tupleDescriptor; DR_printtup *myState = (DR_printtup *) self; StringInfoData buf; int natts = typeinfo->natts; int i, j, k; /* Set or update my derived attribute info, if needed */ if (myState->attrinfo != typeinfo || myState->nattrs != natts) printtup_prepare_info(myState, typeinfo, natts); /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); /* * tell the frontend to expect new tuple data (in binary style) */ pq_beginmessage(&buf, 'B'); /* * send a bitmap of which attributes are not null */ j = 0; k = 1 << 7; for (i = 0; i < natts; ++i) { if (!slot->tts_isnull[i]) j |= k; /* set bit if not null */ k >>= 1; if (k == 0) /* end of byte? */ { pq_sendint(&buf, j, 1); j = 0; k = 1 << 7; } } if (k != (1 << 7)) /* flush last partial byte */ pq_sendint(&buf, j, 1); /* * send the attributes of this tuple */ for (i = 0; i < natts; ++i) { PrinttupAttrInfo *thisState = myState->myinfo + i; Datum origattr = slot->tts_values[i], attr; bytea *outputbytes; if (slot->tts_isnull[i]) continue; Assert(thisState->format == 1); /* * If we have a toasted datum, forcibly detoast it here to avoid * memory leakage inside the type's output routine. */ if (thisState->typisvarlena) attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); else attr = origattr; outputbytes = SendFunctionCall(&thisState->finfo, attr); /* We assume the result will not have been toasted */ pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4); pq_sendbytes(&buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); pfree(outputbytes); /* Clean up detoasted copy, if any */ if (DatumGetPointer(attr) != DatumGetPointer(origattr)) pfree(DatumGetPointer(attr)); } pq_endmessage(&buf); }
/* ---------------- * printtup --- print a tuple in protocol 3.0 * ---------------- */ static void printtup(TupleTableSlot *slot, DestReceiver *self) { TupleDesc typeinfo = slot->tts_tupleDescriptor; DR_printtup *myState = (DR_printtup *) self; StringInfoData buf; int natts = typeinfo->natts; int i; /* Set or update my derived attribute info, if needed */ if (myState->attrinfo != typeinfo || myState->nattrs != natts) printtup_prepare_info(myState, typeinfo, natts); /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); /* * Prepare a DataRow message */ pq_beginmessage(&buf, 'D'); pq_sendint(&buf, natts, 2); /* * send the attributes of this tuple */ for (i = 0; i < natts; ++i) { PrinttupAttrInfo *thisState = myState->myinfo + i; Datum origattr = slot->tts_values[i], attr; if (slot->tts_isnull[i]) { pq_sendint(&buf, -1, 4); continue; } /* * If we have a toasted datum, forcibly detoast it here to avoid * memory leakage inside the type's output routine. */ if (thisState->typisvarlena) attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); else attr = origattr; if (thisState->format == 0) { /* Text output */ char *outputstr; outputstr = OutputFunctionCall(&thisState->finfo, attr); pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false); pfree(outputstr); } else { /* Binary output */ bytea *outputbytes; outputbytes = SendFunctionCall(&thisState->finfo, attr); pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4); pq_sendbytes(&buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); pfree(outputbytes); } /* Clean up detoasted copy, if any */ if (DatumGetPointer(attr) != DatumGetPointer(origattr)) pfree(DatumGetPointer(attr)); } pq_endmessage(&buf); }
/* ---------------- * printtup --- print a tuple in protocol 3.0 * ---------------- */ static void printtup(TupleTableSlot *slot, DestReceiver *self) { TupleDesc typeinfo = slot->tts_tupleDescriptor; DR_printtup *myState = (DR_printtup *) self; StringInfoData buf; int natts = typeinfo->natts; int i; /* Set or update my derived attribute info, if needed */ if (myState->attrinfo != typeinfo || myState->nattrs != natts) printtup_prepare_info(myState, typeinfo, natts); /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); /* * Prepare a DataRow message */ pq_beginmessage(&buf, 'D'); pq_sendint(&buf, natts, 2); /* * send the attributes of this tuple */ for (i = 0; i < natts; ++i) { PrinttupAttrInfo *thisState = myState->myinfo + i; Datum origattr = slot->tts_values[i], attr; if (slot->tts_isnull[i]) { pq_sendint(&buf, -1, 4); continue; } /* * If we have a toasted datum, forcibly detoast it here to avoid * memory leakage inside the type's output routine. * * Here we catch undefined bytes in tuples that are returned to the * client without hitting disk; see comments at the related check in * PageAddItem(). Whether to test before or after detoast is somewhat * arbitrary, as is whether to test external/compressed data at all. * Undefined bytes in the pre-toast datum will have triggered Valgrind * errors in the compressor or toaster; any error detected here for * such datums would indicate an (unlikely) bug in a type-independent * facility. Therefore, this test is most useful for uncompressed, * non-external datums. * * We don't presently bother checking non-varlena datums for undefined * data. PageAddItem() does check them. */ if (thisState->typisvarlena) { VALGRIND_CHECK_MEM_IS_DEFINED(origattr, VARSIZE_ANY(origattr)); attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); } else attr = origattr; if (thisState->format == 0) { /* Text output */ char *outputstr; outputstr = OutputFunctionCall(&thisState->finfo, attr); pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false); pfree(outputstr); } else { /* Binary output */ bytea *outputbytes; outputbytes = SendFunctionCall(&thisState->finfo, attr); pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4); pq_sendbytes(&buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); pfree(outputbytes); } /* Clean up detoasted copy, if any */ if (DatumGetPointer(attr) != DatumGetPointer(origattr)) pfree(DatumGetPointer(attr)); } pq_endmessage(&buf); }
/* * 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; }
/* * TupleToBytes * * Serialize a tuple to bytes */ StringInfo TupleToBytes(BufferPrinterState *self, TupleTableSlot *slot) { StringInfo buf = makeStringInfo(); TupleDesc typeinfo = slot->tts_tupleDescriptor; int natts = typeinfo->natts; int i; int len = 0; /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); /* * Prepare a DataRow message */ pq_beginmessage(buf, 'D'); len += 1; pq_sendint(buf, natts, 2); len+= 2; /* * send the attributes of this tuple */ for (i = 0; i < natts; ++i) { BufferPrinterAttrInfo *thisState = self->typeinfo + i; Datum origattr = slot->tts_values[i], attr; if (slot->tts_isnull[i]) { pq_sendint(buf, -1, 4); len += 4; continue; } /* * If we have a toasted datum, forcibly detoast it here to avoid * memory leakage inside the type's output routine. */ if (thisState->typisvarlena) attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); else attr = origattr; if (thisState->format == 0) { /* Text output */ char *outputstr; outputstr = OutputFunctionCall(&thisState->finfo, attr); pq_sendcountedtext(buf, outputstr, strlen(outputstr), false); len += strlen(outputstr); pfree(outputstr); } else { /* Binary output */ bytea *outputbytes; int size; outputbytes = SendFunctionCall(&thisState->finfo, attr); pq_sendint(buf, VARSIZE(outputbytes) - VARHDRSZ, 4); len += 4; size = VARSIZE(outputbytes) - VARHDRSZ; pq_sendbytes(buf, VARDATA(outputbytes), size); len += size; pfree(outputbytes); } /* Clean up detoasted copy, if any */ if (DatumGetPointer(attr) != DatumGetPointer(origattr)) pfree(DatumGetPointer(attr)); } return buf; }
/* ---------------- * printtup --- print a tuple in protocol 3.0 * ---------------- */ static bool printtup(TupleTableSlot *slot, DestReceiver *self) { TupleDesc typeinfo = slot->tts_tupleDescriptor; DR_printtup *myState = (DR_printtup *) self; MemoryContext oldcontext; StringInfoData buf; int natts = typeinfo->natts; int i; /* Set or update my derived attribute info, if needed */ if (myState->attrinfo != typeinfo || myState->nattrs != natts) printtup_prepare_info(myState, typeinfo, natts); /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); /* Switch into per-row context so we can recover memory below */ oldcontext = MemoryContextSwitchTo(myState->tmpcontext); /* * Prepare a DataRow message (note buffer is in per-row context) */ pq_beginmessage(&buf, 'D'); pq_sendint(&buf, natts, 2); /* * send the attributes of this tuple */ for (i = 0; i < natts; ++i) { PrinttupAttrInfo *thisState = myState->myinfo + i; Datum attr = slot->tts_values[i]; if (slot->tts_isnull[i]) { pq_sendint(&buf, -1, 4); continue; } /* * Here we catch undefined bytes in datums that are returned to the * client without hitting disk; see comments at the related check in * PageAddItem(). This test is most useful for uncompressed, * non-external datums, but we're quite likely to see such here when * testing new C functions. */ if (thisState->typisvarlena) VALGRIND_CHECK_MEM_IS_DEFINED(DatumGetPointer(attr), VARSIZE_ANY(attr)); if (thisState->format == 0) { /* Text output */ char *outputstr; outputstr = OutputFunctionCall(&thisState->finfo, attr); pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false); } else { /* Binary output */ bytea *outputbytes; outputbytes = SendFunctionCall(&thisState->finfo, attr); pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4); pq_sendbytes(&buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); } } pq_endmessage(&buf); /* Return to caller's context, and flush row's temporary memory */ MemoryContextSwitchTo(oldcontext); MemoryContextReset(myState->tmpcontext); return true; }
/* ---------------- * printtup_internal_20 --- print a binary tuple in protocol 2.0 * * We use a different message type, i.e. 'B' instead of 'D' to * indicate a tuple in internal (binary) form. * * This is largely same as printtup_20, except we use binary formatting. * ---------------- */ static bool printtup_internal_20(TupleTableSlot *slot, DestReceiver *self) { TupleDesc typeinfo = slot->tts_tupleDescriptor; DR_printtup *myState = (DR_printtup *) self; MemoryContext oldcontext; StringInfoData buf; int natts = typeinfo->natts; int i, j, k; /* Set or update my derived attribute info, if needed */ if (myState->attrinfo != typeinfo || myState->nattrs != natts) printtup_prepare_info(myState, typeinfo, natts); /* Make sure the tuple is fully deconstructed */ slot_getallattrs(slot); /* Switch into per-row context so we can recover memory below */ oldcontext = MemoryContextSwitchTo(myState->tmpcontext); /* * tell the frontend to expect new tuple data (in binary style) */ pq_beginmessage(&buf, 'B'); /* * send a bitmap of which attributes are not null */ j = 0; k = 1 << 7; for (i = 0; i < natts; ++i) { if (!slot->tts_isnull[i]) j |= k; /* set bit if not null */ k >>= 1; if (k == 0) /* end of byte? */ { pq_sendint(&buf, j, 1); j = 0; k = 1 << 7; } } if (k != (1 << 7)) /* flush last partial byte */ pq_sendint(&buf, j, 1); /* * send the attributes of this tuple */ for (i = 0; i < natts; ++i) { PrinttupAttrInfo *thisState = myState->myinfo + i; Datum attr = slot->tts_values[i]; bytea *outputbytes; if (slot->tts_isnull[i]) continue; Assert(thisState->format == 1); outputbytes = SendFunctionCall(&thisState->finfo, attr); pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4); pq_sendbytes(&buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); } pq_endmessage(&buf); /* Return to caller's context, and flush row's temporary memory */ MemoryContextSwitchTo(oldcontext); MemoryContextReset(myState->tmpcontext); return true; }
/* * Check if the tuple being updated will stay in the same part and throw ERROR * if not. This check is especially necessary for default partition that has * no constraint on it. partslot is the tuple being updated, and * resultRelInfo is the target relation of this update. Call this only * estate has valid es_result_partitions. */ static void checkPartitionUpdate(EState *estate, TupleTableSlot *partslot, ResultRelInfo *resultRelInfo) { Relation resultRelationDesc = resultRelInfo->ri_RelationDesc; AttrNumber max_attr; Datum *values = NULL; bool *nulls = NULL; TupleDesc tupdesc = NULL; Oid parentRelid; Oid targetid; Assert(estate->es_partition_state != NULL && estate->es_partition_state->accessMethods != NULL); if (!estate->es_partition_state->accessMethods->part_cxt) estate->es_partition_state->accessMethods->part_cxt = GetPerTupleExprContext(estate)->ecxt_per_tuple_memory; Assert(PointerIsValid(estate->es_result_partitions)); /* * As opposed to INSERT, resultRelation here is the same child part * as scan origin. However, the partition selection is done with the * parent partition's attribute numbers, so if this result (child) part * has physically-different attribute numbers due to dropped columns, * we should map the child attribute numbers to the parent's attribute * numbers to perform the partition selection. * EState doesn't have the parent relation information at the moment, * so we have to do a hard job here by opening it and compare the * tuple descriptors. If we find we need to map attribute numbers, * max_partition_attr could also be bogus for this child part, * so we end up materializing the whole columns using slot_getallattrs(). * The purpose of this code is just to prevent the tuple from * incorrectly staying in default partition that has no constraint * (parts with constraint will throw an error if the tuple is changing * partition keys to out of part value anyway.) It's a bit overkill * to do this complicated logic just for this purpose, which is necessary * with our current partitioning design, but I hope some day we can * change this so that we disallow phyisically-different tuple descriptor * across partition. */ parentRelid = estate->es_result_partitions->part->parrelid; /* * I don't believe this is the case currently, but we check the parent relid * in case the updating partition has changed since the last time we opened it. */ if (resultRelInfo->ri_PartitionParent && parentRelid != RelationGetRelid(resultRelInfo->ri_PartitionParent)) { resultRelInfo->ri_PartCheckTupDescMatch = 0; if (resultRelInfo->ri_PartCheckMap != NULL) pfree(resultRelInfo->ri_PartCheckMap); if (resultRelInfo->ri_PartitionParent) relation_close(resultRelInfo->ri_PartitionParent, AccessShareLock); } /* * Check this at the first pass only to avoid repeated catalog access. */ if (resultRelInfo->ri_PartCheckTupDescMatch == 0 && parentRelid != RelationGetRelid(resultRelInfo->ri_RelationDesc)) { Relation parentRel; TupleDesc resultTupdesc, parentTupdesc; /* * We are on a child part, let's see the tuple descriptor looks like * the parent's one. Probably this won't cause deadlock because * DML should have opened the parent table with appropriate lock. */ parentRel = relation_open(parentRelid, AccessShareLock); resultTupdesc = RelationGetDescr(resultRelationDesc); parentTupdesc = RelationGetDescr(parentRel); if (!equalTupleDescs(resultTupdesc, parentTupdesc, false)) { AttrMap *map; MemoryContext oldcontext; /* Tuple looks different. Construct attribute mapping. */ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); map_part_attrs(resultRelationDesc, parentRel, &map, true); MemoryContextSwitchTo(oldcontext); /* And save it for later use. */ resultRelInfo->ri_PartCheckMap = map; resultRelInfo->ri_PartCheckTupDescMatch = -1; } else resultRelInfo->ri_PartCheckTupDescMatch = 1; resultRelInfo->ri_PartitionParent = parentRel; /* parentRel will be closed as part of ResultRelInfo cleanup */ } if (resultRelInfo->ri_PartCheckMap != NULL) { Datum *parent_values; bool *parent_nulls; Relation parentRel = resultRelInfo->ri_PartitionParent; TupleDesc parentTupdesc; AttrMap *map; Assert(parentRel != NULL); parentTupdesc = RelationGetDescr(parentRel); /* * We need to map the attribute numbers to parent's one, to * select the would-be destination relation, since all partition * rules are based on the parent relation's tuple descriptor. * max_partition_attr can be bogus as well, so don't use it. */ slot_getallattrs(partslot); values = slot_get_values(partslot); nulls = slot_get_isnull(partslot); parent_values = palloc(parentTupdesc->natts * sizeof(Datum)); parent_nulls = palloc0(parentTupdesc->natts * sizeof(bool)); map = resultRelInfo->ri_PartCheckMap; reconstructTupleValues(map, values, nulls, partslot->tts_tupleDescriptor->natts, parent_values, parent_nulls, parentTupdesc->natts); /* Now we have values/nulls in parent's view. */ values = parent_values; nulls = parent_nulls; tupdesc = RelationGetDescr(parentRel); } else { /* * map == NULL means we can just fetch values/nulls from the * current slot. */ Assert(nulls == NULL && tupdesc == NULL); max_attr = estate->es_partition_state->max_partition_attr; slot_getsomeattrs(partslot, max_attr); /* values/nulls pointing to partslot's array. */ values = slot_get_values(partslot); nulls = slot_get_isnull(partslot); tupdesc = partslot->tts_tupleDescriptor; } /* And select the destination relation that this tuple would go to. */ targetid = selectPartition(estate->es_result_partitions, values, nulls, tupdesc, estate->es_partition_state->accessMethods); /* Free up if we allocated mapped attributes. */ if (values != slot_get_values(partslot)) { Assert(nulls != slot_get_isnull(partslot)); pfree(values); pfree(nulls); } if (!OidIsValid(targetid)) ereport(ERROR, (errcode(ERRCODE_NO_PARTITION_FOR_PARTITIONING_KEY), errmsg("no partition for partitioning key"))); if (RelationGetRelid(resultRelationDesc) != targetid) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("moving tuple from partition \"%s\" to " "partition \"%s\" not supported", get_rel_name(RelationGetRelid(resultRelationDesc)), get_rel_name(targetid)))); }