/* -------------------------------- * ExecSetSlotDescriptor * * This function is used to set the tuple descriptor associated * with the slot's tuple. The passed descriptor must have lifespan * at least equal to the slot's. If it is a reference-counted descriptor * then the reference count is incremented for as long as the slot holds * a reference. * -------------------------------- */ void ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ TupleDesc tupdesc) /* new tuple descriptor */ { /* For safety, make sure slot is empty before changing it */ cleanup_slot(slot); /* * Install the new descriptor; if it's refcounted, bump its refcount. */ slot->tts_tupleDescriptor = tupdesc; PinTupleDesc(tupdesc); { /* * Allocate Datum/isnull arrays of the appropriate size. These must have * the same lifetime as the slot, so allocate in the slot's own context. */ MemoryContext oldcontext = MemoryContextSwitchTo(slot->tts_mcxt); slot->tts_mt_bind = create_memtuple_binding(tupdesc); slot->PRIVATE_tts_values = (Datum *) palloc(tupdesc->natts * sizeof(Datum)); slot->PRIVATE_tts_isnull = (bool *) palloc(tupdesc->natts * sizeof(bool)); MemoryContextSwitchTo(oldcontext); } }
/* -------------------------------- * MakeTupleTableSlot * * Basic routine to make an empty TupleTableSlot. If tupleDesc is * specified the slot's descriptor is fixed for it's lifetime, gaining * some efficiency. If that's undesirable, pass NULL. * -------------------------------- */ TupleTableSlot * MakeTupleTableSlot(TupleDesc tupleDesc) { Size sz; TupleTableSlot *slot; /* * When a fixed descriptor is specified, we can reduce overhead by * allocating the entire slot in one go. */ if (tupleDesc) sz = MAXALIGN(sizeof(TupleTableSlot)) + MAXALIGN(tupleDesc->natts * sizeof(Datum)) + MAXALIGN(tupleDesc->natts * sizeof(bool)); else sz = sizeof(TupleTableSlot); slot = palloc0(sz); slot->type = T_TupleTableSlot; slot->tts_isempty = true; slot->tts_shouldFree = false; slot->tts_shouldFreeMin = false; slot->tts_tuple = NULL; slot->tts_fixedTupleDescriptor = tupleDesc != NULL; slot->tts_tupleDescriptor = tupleDesc; slot->tts_mcxt = CurrentMemoryContext; slot->tts_buffer = InvalidBuffer; slot->tts_nvalid = 0; slot->tts_values = NULL; slot->tts_isnull = NULL; slot->tts_mintuple = NULL; if (tupleDesc != NULL) { slot->tts_values = (Datum *) (((char *) slot) + MAXALIGN(sizeof(TupleTableSlot))); slot->tts_isnull = (bool *) (((char *) slot) + MAXALIGN(sizeof(TupleTableSlot)) + MAXALIGN(tupleDesc->natts * sizeof(Datum))); PinTupleDesc(tupleDesc); } return slot; }
/* * Convert a Python object to a composite type. First look up the type's * description, then route the Python object through the conversion function * for obtaining PostgreSQL tuples. */ static Datum PLyObject_ToComposite(PLyObToDatum *arg, PyObject *plrv, bool *isnull, bool inarray) { Datum rv; TupleDesc desc; if (plrv == Py_None) { *isnull = true; return (Datum) 0; } *isnull = false; /* * The string conversion case doesn't require a tupdesc, nor per-field * conversion data, so just go for it if that's the case to use. */ if (PyString_Check(plrv) || PyUnicode_Check(plrv)) return PLyString_ToComposite(arg, plrv, inarray); /* * If we're dealing with a named composite type, we must look up the * tupdesc every time, to protect against possible changes to the type. * RECORD types can't change between calls; but we must still be willing * to set up the info the first time, if nobody did yet. */ if (arg->typoid != RECORDOID) { desc = lookup_rowtype_tupdesc(arg->typoid, arg->typmod); /* We should have the descriptor of the type's typcache entry */ Assert(desc == arg->u.tuple.typentry->tupDesc); /* Detect change of descriptor, update cache if needed */ if (arg->u.tuple.tupdescseq != arg->u.tuple.typentry->tupDescSeqNo) { PLy_output_setup_tuple(arg, desc, PLy_current_execution_context()->curr_proc); arg->u.tuple.tupdescseq = arg->u.tuple.typentry->tupDescSeqNo; } } else { desc = arg->u.tuple.recdesc; if (desc == NULL) { desc = lookup_rowtype_tupdesc(arg->typoid, arg->typmod); arg->u.tuple.recdesc = desc; } else { /* Pin descriptor to match unpin below */ PinTupleDesc(desc); } } /* Simple sanity check on our caching */ Assert(desc->natts == arg->u.tuple.natts); /* * Convert, using the appropriate method depending on the type of the * supplied Python object. */ if (PySequence_Check(plrv)) /* composite type as sequence (tuple, list etc) */ rv = PLySequence_ToComposite(arg, desc, plrv); else if (PyMapping_Check(plrv)) /* composite type as mapping (currently only dict) */ rv = PLyMapping_ToComposite(arg, desc, plrv); else /* returned as smth, must provide method __getattr__(name) */ rv = PLyGenericObject_ToComposite(arg, desc, plrv, inarray); ReleaseTupleDesc(desc); return rv; }